From b9a461311fe0318badb4f2ff2632e0301a3681b2 Mon Sep 17 00:00:00 2001 From: Chris Di Carlo Date: Sun, 23 Nov 2025 20:14:47 -0500 Subject: [PATCH 1/7] Ignore phpunit.cache and vendor folder --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..178d88b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +vendor +.phpunit.cache From f7321fc366ff0c308217e3ffd0862f8f5f56ab05 Mon Sep 17 00:00:00 2001 From: Chris Di Carlo Date: Sun, 23 Nov 2025 20:15:15 -0500 Subject: [PATCH 2/7] tests: add tests --- README.md | 16 + bin/phpunit-failed-runner | 18 +- composer.json | 15 +- composer.lock | 1787 ++++++++++++++++++++ phpunit.xml | 20 + tests/AlwaysPassingTest.php | 26 + tests/AnotherFailingTest.php | 19 + tests/Fixtures/AlwaysPassingFixture.php | 14 + tests/Fixtures/AnotherFailingFixture.php | 16 + tests/Fixtures/InitiallyFailingFixture.php | 16 + tests/InitiallyFailingTest.php | 29 + tests/README.md | 113 ++ tests/Unit/XslTransformationTest.php | 195 +++ tests/demo-multi-phase.sh | 92 + tests/run-additional-tests.sh | 199 +++ tests/run-automated-test.sh | 172 ++ 16 files changed, 2743 insertions(+), 4 deletions(-) create mode 100644 composer.lock create mode 100644 phpunit.xml create mode 100644 tests/AlwaysPassingTest.php create mode 100644 tests/AnotherFailingTest.php create mode 100644 tests/Fixtures/AlwaysPassingFixture.php create mode 100644 tests/Fixtures/AnotherFailingFixture.php create mode 100644 tests/Fixtures/InitiallyFailingFixture.php create mode 100644 tests/InitiallyFailingTest.php create mode 100644 tests/README.md create mode 100644 tests/Unit/XslTransformationTest.php create mode 100755 tests/demo-multi-phase.sh create mode 100755 tests/run-additional-tests.sh create mode 100755 tests/run-automated-test.sh diff --git a/README.md b/README.md index 3635a44..a097cdc 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,22 @@ For XmlStarlet on other platforms, see [here](http://xmlstar.sourceforge.net/doc ./vendor/bin/phpunit-failed-runner ``` +## Testing + +This package includes a comprehensive test suite that demonstrates the incremental test-fixing workflow. See [tests/README.md](tests/README.md) for details. + +Run the test suite: + +```bash +composer test +``` + +Run the automated multi-phase demonstration: + +```bash +composer test:auto +``` + ### Changelog Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. diff --git a/bin/phpunit-failed-runner b/bin/phpunit-failed-runner index 564a529..7bc4eb4 100755 --- a/bin/phpunit-failed-runner +++ b/bin/phpunit-failed-runner @@ -8,10 +8,22 @@ else runner="./vendor/bin/phpunit" fi +# Determine XSL path - check if we're in the package itself or using it as a dependency +if [ -f "./prune.xsl" ]; then + # We're in the package root + xsl_path="." +elif [ -f "./vendor/chrisdicarlo/phpunit-failed-runner/prune.xsl" ]; then + # We're using the package as a dependency + xsl_path="./vendor/chrisdicarlo/phpunit-failed-runner" +else + echo "Error: Cannot find XSL transformation files" + exit 1 +fi + if test -f "$logfile"; then echo -e "Logfile found. Searching for previously failing tests... \U23F3" - count_failed_tests="$(xmlstarlet tr ./vendor/chrisdicarlo/phpunit-failed-runner/prune.xsl junit.xml | xmlstarlet tr --omit-decl ./vendor/chrisdicarlo/phpunit-failed-runner/count-failed.xsl)" + count_failed_tests="$(xmlstarlet tr "$xsl_path/prune.xsl" junit.xml | xmlstarlet tr --omit-decl "$xsl_path/count-failed.xsl")" if [ "$count_failed_tests" = "0" ]; then echo -e "No failed tests! Great job! \U1F44D \U1F389" @@ -19,7 +31,7 @@ if test -f "$logfile"; then exit 0 else echo -e "Found $count_failed_tests previously failing tests, filtering... \U1F97A" - filter=$(xmlstarlet tr ./vendor/chrisdicarlo/phpunit-failed-runner/prune.xsl junit.xml | xmlstarlet tr --omit-decl ./vendor/chrisdicarlo/phpunit-failed-runner/failed-tests.xsl); "$runner" --filter "$filter" --log-junit junit.xml + filter=$(xmlstarlet tr "$xsl_path/prune.xsl" junit.xml | xmlstarlet tr --omit-decl "$xsl_path/failed-tests.xsl"); "$runner" --filter "$filter" --log-junit junit.xml fi else echo -e "Logfile not found. Running the test suite... \U1F91E" @@ -29,7 +41,7 @@ else fi if test -f "$logfile"; then - count_failed_tests="$(xmlstarlet tr ./vendor/chrisdicarlo/phpunit-failed-runner/prune.xsl junit.xml | xmlstarlet tr --omit-decl ./vendor/chrisdicarlo/phpunit-failed-runner/count-failed.xsl)" + count_failed_tests="$(xmlstarlet tr "$xsl_path/prune.xsl" junit.xml | xmlstarlet tr --omit-decl "$xsl_path/count-failed.xsl")" if [ "$count_failed_tests" = "0" ]; then echo -e "No failed tests! Great job! \U1F44D \U1F389" diff --git a/composer.json b/composer.json index dc23a5c..c06959f 100644 --- a/composer.json +++ b/composer.json @@ -19,5 +19,18 @@ ], "bin": [ "bin/phpunit-failed-runner" - ] + ], + "require-dev": { + "phpunit/phpunit": "^10.5|^11.0" + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "scripts": { + "test": "phpunit", + "test:demo": "tests/demo-multi-phase.sh", + "test:auto": "tests/run-automated-test.sh" + } } diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..d99c32d --- /dev/null +++ b/composer.lock @@ -0,0 +1,1787 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "b4dc0e7fc00dcf282353d31702aa1325", + "packages": [], + "packages-dev": [ + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.6.2", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" + }, + "time": "2025-10-21T19:32:17+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "11.0.11", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.4.0", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.0", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^11.5.2" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.11" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2025-08-27T14:37:49+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.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" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-27T05:02:59+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.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" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:07:44+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.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" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:08:43+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.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" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:09:35+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "11.5.44", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "c346885c95423eda3f65d85a194aaa24873cda82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c346885c95423eda3f65d85a194aaa24873cda82", + "reference": "c346885c95423eda3f65d85a194aaa24873cda82", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.11", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.3", + "sebastian/comparator": "^6.3.2", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.1", + "sebastian/exporter": "^6.3.2", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/type": "^5.1.3", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.44" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2025-11-13T07:17:35+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.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" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:41:36+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.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" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-03-19T07:56:08+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:45:54+00:00" + }, + { + "name": "sebastian/comparator", + "version": "6.3.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.4" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2025-08-10T08:07:46+00:00" + }, + { + "name": "sebastian/complexity", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.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" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:49:50+00:00" + }, + { + "name": "sebastian/diff", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:53:05+00:00" + }, + { + "name": "sebastian/environment", + "version": "7.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" + } + ], + "time": "2025-05-21T11:55:47+00:00" + }, + { + "name": "sebastian/exporter", + "version": "6.3.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:12:51+00:00" + }, + { + "name": "sebastian/global-state", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:57:36+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.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" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:58:38+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:00:13+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:01:32+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-13T04:42:22+00:00" + }, + { + "name": "sebastian/type", + "version": "5.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" + } + ], + "time": "2025-08-09T06:55:48+00:00" + }, + { + "name": "sebastian/version", + "version": "5.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.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" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-09T05:16:32+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2025-11-17T20:03:58+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..fede57b --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,20 @@ + + + + + tests + tests/Fixtures + + + + + bin + + + diff --git a/tests/AlwaysPassingTest.php b/tests/AlwaysPassingTest.php new file mode 100644 index 0000000..8251285 --- /dev/null +++ b/tests/AlwaysPassingTest.php @@ -0,0 +1,26 @@ +assertTrue( + $fixture->success(), + 'AlwaysPassingFixture should always return true' + ); + } + + public function test_another_always_passing_test(): void + { + $fixture = new AlwaysPassingFixture(); + + $this->assertTrue($fixture->success()); + } +} diff --git a/tests/AnotherFailingTest.php b/tests/AnotherFailingTest.php new file mode 100644 index 0000000..3404e85 --- /dev/null +++ b/tests/AnotherFailingTest.php @@ -0,0 +1,19 @@ +assertTrue( + $fixture->success(), + 'This test will also fail initially' + ); + } +} diff --git a/tests/Fixtures/AlwaysPassingFixture.php b/tests/Fixtures/AlwaysPassingFixture.php new file mode 100644 index 0000000..f24b4c3 --- /dev/null +++ b/tests/Fixtures/AlwaysPassingFixture.php @@ -0,0 +1,14 @@ +assertTrue( + $fixture->success(), + 'This test will fail initially but pass after the fixture is fixed' + ); + } + + public function test_another_initially_failing_test(): void + { + $fixture = new InitiallyFailingFixture(); + + $this->assertTrue( + $fixture->success(), + 'Another test that depends on the same fixture' + ); + } +} diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..5e1e8f1 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,113 @@ +# Tests + +This directory contains the test suite for the phpunit-failed-runner package. + +## Test Structure + +The test suite uses fixture classes that simulate test failures and successes through a simple `success()` method: + +### Fixture Classes (`tests/Fixtures/`) + +- **AlwaysPassingFixture.php** - Always returns `true` (passing tests) +- **InitiallyFailingFixture.php** - Returns `false` initially (can be "fixed" to return `true`) +- **AnotherFailingFixture.php** - Returns `false` initially (can be "fixed" to return `true`) + +### Test Cases + +- **AlwaysPassingTest.php** - Tests that use the always-passing fixture +- **InitiallyFailingTest.php** - Tests that use InitiallyFailingFixture +- **AnotherFailingTest.php** - Tests that use AnotherFailingFixture + +## Running Tests + +### Run all tests normally + +```bash +./vendor/bin/phpunit +``` + +or + +```bash +composer test +``` + +### Multi-Phase Test Demonstration + +The package includes scripts that demonstrate the incremental test-fixing workflow: + +#### Interactive Demo + +```bash +./tests/demo-multi-phase.sh +``` + +This script runs through the multi-phase workflow interactively, pausing between phases to show: +1. Initial test run with failures +2. Fixing the first set of failing tests +3. Fixing the remaining failing tests +4. Final verification that all tests pass + +#### Automated Test + +```bash +./tests/run-automated-test.sh +``` + +or + +```bash +composer test:auto +``` + +This script automatically runs through all phases and validates: +- Phase 1: Initial run creates 3 failures +- Phase 2: After fixing InitiallyFailingFixture, only 1 failure remains +- Phase 3: After fixing AnotherFailingFixture, all tests pass and junit.xml is cleaned up +- Phase 4: Full test suite passes + +## How It Works + +The multi-phase tests use `sed` to modify the return values in fixture files inline: + +```bash +# Simulate a failure +sed -i 's/return true;/return false;/g' tests/Fixtures/InitiallyFailingFixture.php + +# Simulate a fix +sed -i 's/return false;/return true;/g' tests/Fixtures/InitiallyFailingFixture.php +``` + +This demonstrates the real-world workflow of: +1. Running tests and having some fail (junit.xml created) +2. Fixing code to make tests pass +3. Re-running only the previously failed tests +4. Continuing until all tests pass + +## What the Automated Test Validates + +The `run-automated-test.sh` script is an **integration test** that verifies the `phpunit-failed-runner` script's behavior by checking its actual output: + +### Phase 1: Initial Run +- Verifies script detects "Logfile not found" +- Verifies script runs full test suite +- Verifies junit.xml is created +- Verifies failures are reported + +### Phase 2: Incremental Re-run +- Verifies script detects "Logfile found" +- Verifies script finds "previously failing tests" +- Verifies script filters and runs only failed tests +- Verifies junit.xml persists (because tests still fail) + +### Phase 3: All Tests Pass +- Verifies script shows "No failed tests! Great job!" +- Verifies script automatically deletes junit.xml +- Confirms cleanup behavior works correctly + +### Phase 4: Clean State +- Verifies script correctly handles clean state (no junit.xml) +- Verifies script runs full suite when starting fresh +- Verifies all tests pass and junit.xml is cleaned up + +The test validates the **actual script behavior** rather than duplicating its logic, ensuring the phpunit-failed-runner script works as intended. diff --git a/tests/Unit/XslTransformationTest.php b/tests/Unit/XslTransformationTest.php new file mode 100644 index 0000000..bfb5da9 --- /dev/null +++ b/tests/Unit/XslTransformationTest.php @@ -0,0 +1,195 @@ +projectRoot = dirname(__DIR__, 1); + } + + public function test_prune_xsl_extracts_failed_tests_from_junit_xml(): void + { + $junitXml = << + + + + + + Failed + + + Failed + + + +XML; + + $tempFile = tempnam(sys_get_temp_dir(), 'junit'); + file_put_contents($tempFile, $junitXml); + + $output = shell_exec("xmlstarlet tr {$this->projectRoot}/../prune.xsl {$tempFile}"); + + $this->assertStringContainsString('', $output); + $this->assertStringContainsString('Tests\FailingTest::test_failing_one', $output); + $this->assertStringContainsString('Tests\AnotherTest::test_failing_two', $output); + $this->assertStringNotContainsString('PassingTest', $output); + + unlink($tempFile); + } + + public function test_prune_xsl_handles_errors_as_failures(): void + { + $junitXml = << + + + + Error occurred + + + Failed + + + +XML; + + $tempFile = tempnam(sys_get_temp_dir(), 'junit'); + file_put_contents($tempFile, $junitXml); + + $output = shell_exec("xmlstarlet tr {$this->projectRoot}/../prune.xsl {$tempFile}"); + + $this->assertStringContainsString('ErrorTest::test_with_error', $output); + $this->assertStringContainsString('FailureTest::test_with_failure', $output); + + unlink($tempFile); + } + + public function test_count_failed_xsl_counts_tests_correctly(): void + { + $prunedXml = << + + Tests\FailingTest::test_one + Tests\FailingTest::test_two + Tests\AnotherTest::test_three + +XML; + + $tempFile = tempnam(sys_get_temp_dir(), 'pruned'); + file_put_contents($tempFile, $prunedXml); + + $count = trim(shell_exec("xmlstarlet tr --omit-decl {$this->projectRoot}/../count-failed.xsl {$tempFile}")); + + $this->assertEquals('3', $count); + + unlink($tempFile); + } + + public function test_count_failed_xsl_returns_zero_for_no_failures(): void + { + $prunedXml = << + + +XML; + + $tempFile = tempnam(sys_get_temp_dir(), 'pruned'); + file_put_contents($tempFile, $prunedXml); + + $count = trim(shell_exec("xmlstarlet tr --omit-decl {$this->projectRoot}/../count-failed.xsl {$tempFile}")); + + $this->assertEquals('0', $count); + + unlink($tempFile); + } + + public function test_failed_tests_xsl_creates_phpunit_filter(): void + { + $prunedXml = << + + Tests\FailingTest::test_one + Tests\FailingTest::test_two + Tests\AnotherTest::test_three + +XML; + + $tempFile = tempnam(sys_get_temp_dir(), 'pruned'); + file_put_contents($tempFile, $prunedXml); + + $filter = trim(shell_exec("xmlstarlet tr --omit-decl {$this->projectRoot}/../failed-tests.xsl {$tempFile}")); + + $this->assertStringContainsString('Tests\\\\FailingTest::test_one', $filter); + $this->assertStringContainsString('Tests\\\\FailingTest::test_two', $filter); + $this->assertStringContainsString('Tests\\\\AnotherTest::test_three', $filter); + $this->assertStringContainsString('|', $filter); // Tests are separated by | + + unlink($tempFile); + } + + public function test_failed_tests_xsl_escapes_backslashes(): void + { + $prunedXml = << + + Tests\Namespace\ClassName::testMethod + +XML; + + $tempFile = tempnam(sys_get_temp_dir(), 'pruned'); + file_put_contents($tempFile, $prunedXml); + + $filter = trim(shell_exec("xmlstarlet tr --omit-decl {$this->projectRoot}/../failed-tests.xsl {$tempFile}")); + + // Backslashes should be escaped for PHPUnit filter + $this->assertStringContainsString('Tests\\\\Namespace\\\\ClassName::testMethod', $filter); + + unlink($tempFile); + } + + public function test_full_transformation_pipeline(): void + { + $junitXml = << + + + + + + Failed + + + + Failed + + + +XML; + + $tempFile = tempnam(sys_get_temp_dir(), 'junit'); + file_put_contents($tempFile, $junitXml); + + // Run full pipeline: prune -> count + $count = trim(shell_exec("xmlstarlet tr {$this->projectRoot}/../prune.xsl {$tempFile} | xmlstarlet tr --omit-decl {$this->projectRoot}/../count-failed.xsl")); + $this->assertEquals('2', $count); + + // Run full pipeline: prune -> failed-tests + $filter = trim(shell_exec("xmlstarlet tr {$this->projectRoot}/../prune.xsl {$tempFile} | xmlstarlet tr --omit-decl {$this->projectRoot}/../failed-tests.xsl")); + $this->assertStringContainsString('Tests\\\\FailingTest::test_fail_1', $filter); + $this->assertStringContainsString('Tests\\\\FailingTest::test_fail_2', $filter); + $this->assertStringContainsString('|', $filter); + + unlink($tempFile); + } +} diff --git a/tests/demo-multi-phase.sh b/tests/demo-multi-phase.sh new file mode 100755 index 0000000..a00ac5a --- /dev/null +++ b/tests/demo-multi-phase.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +# Multi-phase test runner demonstration script +# This script simulates the full workflow of: +# 1. Running tests with failures +# 2. Fixing the failing tests +# 3. Re-running only the failed tests +# 4. Verifying all tests pass + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +cd "$PROJECT_ROOT" + +echo "======================================" +echo "Multi-Phase Test Runner Demonstration" +echo "======================================" +echo "" + +# Ensure junit.xml is removed before starting +if [ -f "junit.xml" ]; then + echo "Cleaning up previous junit.xml..." + rm junit.xml +fi + +# Phase 1: Run tests with failures +echo "======================================" +echo "PHASE 1: Initial test run (expect failures)" +echo "======================================" +echo "" + +# Ensure fixtures are set to fail +sed -i 's/return true;/return false;/g' tests/Fixtures/InitiallyFailingFixture.php +sed -i 's/return true;/return false;/g' tests/Fixtures/AnotherFailingFixture.php + +echo "Running: ./bin/phpunit-failed-runner" +./bin/phpunit-failed-runner || true + +echo "" +echo "Press Enter to continue to Phase 2..." +read -r + +# Phase 2: Fix the first fixture +echo "" +echo "======================================" +echo "PHASE 2: Fix InitiallyFailingFixture" +echo "======================================" +echo "" + +echo "Fixing InitiallyFailingFixture (changing return false to return true)..." +sed -i 's/return false;/return true;/g' tests/Fixtures/InitiallyFailingFixture.php + +echo "Running: ./bin/phpunit-failed-runner (should only run previously failed tests)" +./bin/phpunit-failed-runner || true + +echo "" +echo "Press Enter to continue to Phase 3..." +read -r + +# Phase 3: Fix the second fixture +echo "" +echo "======================================" +echo "PHASE 3: Fix AnotherFailingFixture" +echo "======================================" +echo "" + +echo "Fixing AnotherFailingFixture (changing return false to return true)..." +sed -i 's/return false;/return true;/g' tests/Fixtures/AnotherFailingFixture.php + +echo "Running: ./bin/phpunit-failed-runner (should only run remaining failed test)" +./bin/phpunit-failed-runner || true + +echo "" +echo "======================================" +echo "PHASE 4: Verify all tests pass" +echo "======================================" +echo "" + +echo "Running full test suite to verify everything passes..." +./vendor/bin/phpunit + +echo "" +echo "======================================" +echo "Demo Complete!" +echo "======================================" +echo "" +echo "To reset fixtures for another demo run:" +echo " sed -i 's/return true;/return false;/g' tests/Fixtures/InitiallyFailingFixture.php" +echo " sed -i 's/return true;/return false;/g' tests/Fixtures/AnotherFailingFixture.php" +echo "" diff --git a/tests/run-additional-tests.sh b/tests/run-additional-tests.sh new file mode 100755 index 0000000..2802aaf --- /dev/null +++ b/tests/run-additional-tests.sh @@ -0,0 +1,199 @@ +#!/bin/bash + +# Additional integration tests for phpunit-failed-runner +# Tests additional functionality beyond the basic multi-phase workflow + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +cd "$PROJECT_ROOT" + +echo "==========================================" +echo "Additional Integration Tests" +echo "==========================================" +echo "" + +# Cleanup function +cleanup() { + echo "Cleaning up..." + rm -f junit.xml + sed -i 's/return false;/return true;/g' tests/Fixtures/InitiallyFailingFixture.php 2>/dev/null || true + sed -i 's/return false;/return true;/g' tests/Fixtures/AnotherFailingFixture.php 2>/dev/null || true +} + +trap cleanup EXIT + +# Test 1: Passing additional parameters to PHPUnit +echo "TEST 1: Passing additional parameters" +echo "========================================" +echo "" + +rm -f junit.xml +sed -i 's/return true;/return false;/g' tests/Fixtures/InitiallyFailingFixture.php + +echo "Running with --testdox parameter..." +output=$(./bin/phpunit-failed-runner --testdox 2>&1) + +if ! echo "$output" | grep -q "Initially Failing"; then + echo "ERROR: --testdox parameter not passed through" + exit 1 +fi + +echo "✓ Additional parameters passed correctly" +echo "" + +# Test 2: XSL transformations produce correct filter +echo "TEST 2: XSL filter generation" +echo "==============================" +echo "" + +rm -f junit.xml +./bin/phpunit-failed-runner > /dev/null 2>&1 || true + +if [ ! -f "junit.xml" ]; then + echo "ERROR: junit.xml not created" + exit 1 +fi + +# Generate the filter manually to verify XSL works +filter=$(xmlstarlet tr ./prune.xsl junit.xml | xmlstarlet tr --omit-decl ./failed-tests.xsl) + +if [ -z "$filter" ]; then + echo "ERROR: XSL transformation produced empty filter" + exit 1 +fi + +# Check filter format (should be like: TestClass::testMethod|AnotherTest::anotherMethod) +if ! echo "$filter" | grep -q "::"; then + echo "ERROR: Filter doesn't contain :: separator" + exit 1 +fi + +echo "Generated filter: $filter" +echo "✓ XSL transformations work correctly" +echo "" + +# Test 3: All tests failing +echo "TEST 3: All tests fail scenario" +echo "================================" +echo "" + +rm -f junit.xml +sed -i 's/return true;/return false;/g' tests/Fixtures/AlwaysPassingFixture.php +sed -i 's/return true;/return false;/g' tests/Fixtures/InitiallyFailingFixture.php +sed -i 's/return true;/return false;/g' tests/Fixtures/AnotherFailingFixture.php + +output=$(./bin/phpunit-failed-runner 2>&1) + +if ! echo "$output" | grep -q "5.*test"; then + echo "ERROR: Should report 5 total tests" + exit 1 +fi + +# Run again to verify it tries to run all failed tests +output=$(./bin/phpunit-failed-runner 2>&1) + +if ! echo "$output" | grep -q "Found 5 previously failing tests"; then + echo "ERROR: Should find all 5 tests failed" + exit 1 +fi + +# Reset AlwaysPassingFixture +sed -i 's/return false;/return true;/g' tests/Fixtures/AlwaysPassingFixture.php + +echo "✓ All tests failing handled correctly" +echo "" + +# Test 4: XSL path detection +echo "TEST 4: XSL path detection" +echo "==========================" +echo "" + +# Verify XSL files are found +if [ ! -f "./prune.xsl" ]; then + echo "ERROR: prune.xsl not found in package root" + exit 1 +fi + +if [ ! -f "./count-failed.xsl" ]; then + echo "ERROR: count-failed.xsl not found in package root" + exit 1 +fi + +if [ ! -f "./failed-tests.xsl" ]; then + echo "ERROR: failed-tests.xsl not found in package root" + exit 1 +fi + +echo "✓ XSL files detected correctly" +echo "" + +# Test 5: Error handling (create a fixture with an error vs failure) +echo "TEST 5: Handling errors and failures" +echo "=====================================" +echo "" + +# Create a temporary test that throws an error +cat > tests/Fixtures/ErrorFixture.php << 'EOF' + tests/ErrorTest.php << 'EOF' +assertTrue($fixture->success()); + } +} +EOF + +rm -f junit.xml +output=$(./bin/phpunit-failed-runner 2>&1 || true) + +if ! echo "$output" | grep -q "error\|Error\|ERROR"; then + echo "WARNING: Error might not be reported clearly (this may be OK)" +fi + +# Run again to verify errors are also tracked +if [ -f "junit.xml" ]; then + output=$(./bin/phpunit-failed-runner 2>&1 || true) + + if ! echo "$output" | grep -q "previously failing tests"; then + echo "ERROR: Errors should also be tracked as failed tests" + exit 1 + fi + + echo "✓ Errors handled correctly" +else + echo "WARNING: junit.xml not created with error" +fi + +# Cleanup error test files +rm -f tests/Fixtures/ErrorFixture.php +rm -f tests/ErrorTest.php + +echo "" + +echo "==========================================" +echo "✓ All additional tests passed!" +echo "==========================================" +echo "" diff --git a/tests/run-automated-test.sh b/tests/run-automated-test.sh new file mode 100755 index 0000000..3a61b85 --- /dev/null +++ b/tests/run-automated-test.sh @@ -0,0 +1,172 @@ +#!/bin/bash + +# Automated multi-phase test runner (non-interactive) +# This script tests the phpunit-failed-runner script by verifying its actual output and behavior + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +cd "$PROJECT_ROOT" + +echo "======================================" +echo "Automated Multi-Phase Test Runner" +echo "======================================" +echo "" + +# Cleanup function to reset fixtures +cleanup() { + echo "Resetting fixtures to passing state..." + sed -i 's/return false;/return true;/g' tests/Fixtures/InitiallyFailingFixture.php 2>/dev/null || true + sed -i 's/return false;/return true;/g' tests/Fixtures/AnotherFailingFixture.php 2>/dev/null || true + rm -f junit.xml +} + +# Ensure cleanup on exit +trap cleanup EXIT + +# Ensure junit.xml is removed before starting +rm -f junit.xml + +# Phase 1: Run tests with failures - should detect no logfile and run full suite +echo "PHASE 1: Initial test run (expect failures)" +echo "============================================" +echo "" + +# Set fixtures to fail +sed -i 's/return true;/return false;/g' tests/Fixtures/InitiallyFailingFixture.php +sed -i 's/return true;/return false;/g' tests/Fixtures/AnotherFailingFixture.php + +echo "Running: ./bin/phpunit-failed-runner" +output=$(./bin/phpunit-failed-runner 2>&1) +echo "$output" + +# Verify script detected no logfile +if ! echo "$output" | grep -q "Logfile not found"; then + echo "ERROR: Script should have detected no logfile" + exit 1 +fi + +# Verify junit.xml was created +if [ ! -f "junit.xml" ]; then + echo "ERROR: junit.xml should have been created" + exit 1 +fi + +# Verify we have failures (PHPUnit should report failures) +if ! echo "$output" | grep -q "FAILURES!"; then + echo "ERROR: Expected test failures in Phase 1" + exit 1 +fi + +echo "" +echo "✓ Phase 1 complete: Script detected no logfile, ran full suite, tests failed as expected" +echo "" + +# Phase 2: Fix the first fixture and re-run - script should detect logfile and filter +echo "PHASE 2: Fix InitiallyFailingFixture and re-run" +echo "================================================" +echo "" + +echo "Fixing InitiallyFailingFixture..." +sed -i 's/return false;/return true;/g' tests/Fixtures/InitiallyFailingFixture.php + +echo "Running: ./bin/phpunit-failed-runner" +output=$(./bin/phpunit-failed-runner 2>&1) +echo "$output" + +# Verify script found the logfile +if ! echo "$output" | grep -q "Logfile found"; then + echo "ERROR: Script should have detected existing logfile" + exit 1 +fi + +# Verify script found previously failing tests +if ! echo "$output" | grep -q "Found .* previously failing tests"; then + echo "ERROR: Script should have found previously failing tests" + exit 1 +fi + +# Verify we still have 1 failure (AnotherFailingTest) +if ! echo "$output" | grep -q "FAILURES!"; then + echo "ERROR: Expected AnotherFailingFixture to still fail in Phase 2" + exit 1 +fi + +# Verify junit.xml still exists (because there are still failures) +if [ ! -f "junit.xml" ]; then + echo "ERROR: junit.xml should still exist because tests failed" + exit 1 +fi + +echo "" +echo "✓ Phase 2 complete: Script detected logfile, filtered to failed tests, 1 test still failing" +echo "" + +# Phase 3: Fix the second fixture and re-run - script should show success and cleanup +echo "PHASE 3: Fix AnotherFailingFixture and re-run" +echo "==============================================" +echo "" + +echo "Fixing AnotherFailingFixture..." +sed -i 's/return false;/return true;/g' tests/Fixtures/AnotherFailingFixture.php + +echo "Running: ./bin/phpunit-failed-runner" +output=$(./bin/phpunit-failed-runner 2>&1) +echo "$output" + +# Verify script found the logfile +if ! echo "$output" | grep -q "Logfile found"; then + echo "ERROR: Script should have detected existing logfile" + exit 1 +fi + +# Verify script shows success message +if ! echo "$output" | grep -q "No failed tests! Great job!"; then + echo "ERROR: Script should have shown success message" + exit 1 +fi + +# Verify junit.xml was deleted by the script +if [ -f "junit.xml" ]; then + echo "ERROR: Script should have deleted junit.xml after all tests pass" + exit 1 +fi + +echo "" +echo "✓ Phase 3 complete: Script detected all tests pass, cleaned up junit.xml" +echo "" + +# Phase 4: Run script again - should detect no logfile and run full suite (all passing) +echo "PHASE 4: Verify clean state - no logfile exists" +echo "================================================" +echo "" + +echo "Running: ./bin/phpunit-failed-runner" +output=$(./bin/phpunit-failed-runner 2>&1) +echo "$output" + +# Verify script detected no logfile +if ! echo "$output" | grep -q "Logfile not found"; then + echo "ERROR: Script should have detected no logfile in clean state" + exit 1 +fi + +# Verify all tests pass +if ! echo "$output" | grep -q "OK"; then + echo "ERROR: All tests should pass" + exit 1 +fi + +# Verify script cleaned up junit.xml again +if [ -f "junit.xml" ]; then + echo "ERROR: Script should have deleted junit.xml after successful run" + exit 1 +fi + +echo "" +echo "✓ Phase 4 complete: Script handled clean state correctly, all tests pass" +echo "" +echo "======================================" +echo "✓ All phases completed successfully!" +echo "======================================" +echo "" From 092f9046a62d80b71f6a5c80af28e3f49cbac51c Mon Sep 17 00:00:00 2001 From: Chris Di Carlo Date: Mon, 24 Nov 2025 01:16:38 -0500 Subject: [PATCH 3/7] tests: refactor integration tests as Phpunit tests --- composer.json | 4 +- phpunit-integration-fixtures.xml | 21 ++ tests/Integration/ScriptBehaviorTest.php | 293 +++++++++++++++++++++++ tests/README.md | 113 --------- tests/demo-multi-phase.sh | 92 ------- tests/run-additional-tests.sh | 199 --------------- tests/run-automated-test.sh | 172 ------------- 7 files changed, 315 insertions(+), 579 deletions(-) create mode 100644 phpunit-integration-fixtures.xml create mode 100644 tests/Integration/ScriptBehaviorTest.php delete mode 100644 tests/README.md delete mode 100755 tests/demo-multi-phase.sh delete mode 100755 tests/run-additional-tests.sh delete mode 100755 tests/run-automated-test.sh diff --git a/composer.json b/composer.json index c06959f..2b3b6e4 100644 --- a/composer.json +++ b/composer.json @@ -29,8 +29,6 @@ } }, "scripts": { - "test": "phpunit", - "test:demo": "tests/demo-multi-phase.sh", - "test:auto": "tests/run-automated-test.sh" + "test": "phpunit" } } diff --git a/phpunit-integration-fixtures.xml b/phpunit-integration-fixtures.xml new file mode 100644 index 0000000..cbd0c63 --- /dev/null +++ b/phpunit-integration-fixtures.xml @@ -0,0 +1,21 @@ + + + + + tests/AlwaysPassingTest.php + tests/InitiallyFailingTest.php + tests/AnotherFailingTest.php + + + + + bin + + + diff --git a/tests/Integration/ScriptBehaviorTest.php b/tests/Integration/ScriptBehaviorTest.php new file mode 100644 index 0000000..80c8631 --- /dev/null +++ b/tests/Integration/ScriptBehaviorTest.php @@ -0,0 +1,293 @@ +projectRoot = dirname(__DIR__, 2); + $this->junitPath = $this->projectRoot . '/junit.xml'; + $this->scriptPath = $this->projectRoot . '/bin/phpunit-failed-runner'; + $this->fixtureFilePath = $this->projectRoot . '/tests/Fixtures/InitiallyFailingFixture.php'; + + // Clean up any existing junit.xml before each test + if (file_exists($this->junitPath)) { + unlink($this->junitPath); + } + + // Backup the fixture file + $this->originalFixtureContent = file_get_contents($this->fixtureFilePath); + } + + protected function tearDown(): void + { + // Restore the fixture file + if ($this->originalFixtureContent !== null) { + file_put_contents($this->fixtureFilePath, $this->originalFixtureContent); + } + + // Clean up junit.xml after each test + if (file_exists($this->junitPath)) { + unlink($this->junitPath); + } + } + + /** + * Execute the script and capture output + * Uses phpunit-integration-fixtures.xml to only run fixture tests + */ + private function runScript(?string &$output = null): int + { + $output = []; + $exitCode = 0; + + // Use the fixtures config to only run fixture tests (not the full suite) + exec("cd {$this->projectRoot} && {$this->scriptPath} -c phpunit-integration-fixtures.xml 2>&1", $output, $exitCode); + $output = implode("\n", $output); + + return $exitCode; + } + + /** + * Make the fixture fail by changing return true to return false + */ + private function makeFixtureFail(): void + { + $content = file_get_contents($this->fixtureFilePath); + $content = str_replace('return true;', 'return false;', $content); + file_put_contents($this->fixtureFilePath, $content); + } + + /** + * Make the fixture pass by changing return false to return true + */ + private function makeFixturePass(): void + { + $content = file_get_contents($this->fixtureFilePath); + $content = str_replace('return false;', 'return true;', $content); + file_put_contents($this->fixtureFilePath, $content); + } + + public function test_script_runs_full_suite_when_no_logfile_exists(): void + { + // Arrange: No junit.xml exists, make fixture fail so junit.xml persists + $this->assertFileDoesNotExist($this->junitPath); + $this->makeFixtureFail(); + + // Act: Run the script + $exitCode = $this->runScript($output); + + // Assert: Should run full suite + $this->assertEquals(0, $exitCode, 'Script should exit successfully'); + $this->assertStringContainsString('Logfile not found', $output); + $this->assertStringContainsString('Running the test suite', $output); + + // junit.xml should be created and persist (because test failed) + $this->assertFileExists($this->junitPath); + } + + public function test_script_removes_logfile_when_all_tests_pass(): void + { + // Arrange: Make fixture fail first, run script to create junit.xml + $this->makeFixtureFail(); + $this->runScript($firstOutput); + $this->assertFileExists($this->junitPath, 'junit.xml should exist after failed run'); + + // Now fix the fixture + $this->makeFixturePass(); + + // Act: Run script again (it should find the logfile with no failures) + $exitCode = $this->runScript($output); + + // Assert: Should clean up the logfile + $this->assertEquals(0, $exitCode); + $this->assertStringContainsString('Logfile found', $output); + $this->assertStringContainsString('No failed tests', $output); + $this->assertStringContainsString('Great job', $output); + + // junit.xml should be removed + $this->assertFileDoesNotExist($this->junitPath); + } + + public function test_script_filters_and_reruns_only_failing_tests(): void + { + // Arrange: Make fixture fail, run script to create junit.xml with failures + $this->makeFixtureFail(); + $this->runScript($firstOutput); + + $this->assertFileExists($this->junitPath); + $this->assertStringContainsString('Logfile not found', $firstOutput); + + // Act: Run script again (should detect failures and rerun only those tests) + $exitCode = $this->runScript($secondOutput); + + // Assert: Should find and filter failed tests + $this->assertEquals(0, $exitCode); + $this->assertStringContainsString('Logfile found', $secondOutput); + $this->assertStringContainsString('Searching for previously failing tests', $secondOutput); + $this->assertStringContainsString('previously failing tests, filtering', $secondOutput); + + // junit.xml should still exist (tests still failing) + $this->assertFileExists($this->junitPath); + } + + public function test_full_workflow_fail_then_fix_then_cleanup(): void + { + // Phase 1: Run with failing test + $this->makeFixtureFail(); + $exitCode1 = $this->runScript($output1); + + $this->assertEquals(0, $exitCode1, 'Script should exit successfully even with failures'); + $this->assertStringContainsString('Logfile not found', $output1); + $this->assertFileExists($this->junitPath); + + // Phase 2: Run again (should detect and rerun failures) + $exitCode2 = $this->runScript($output2); + + $this->assertEquals(0, $exitCode2); + $this->assertStringContainsString('Logfile found', $output2); + $this->assertStringContainsString('previously failing tests', $output2); + $this->assertFileExists($this->junitPath); + + // Phase 3: Fix the test and run again + $this->makeFixturePass(); + $exitCode3 = $this->runScript($output3); + + // Assert: After fixing, should clean up + $this->assertEquals(0, $exitCode3); + $this->assertStringContainsString('Logfile found', $output3); + $this->assertStringContainsString('No failed tests', $output3); + $this->assertFileDoesNotExist($this->junitPath, 'junit.xml should be cleaned up after all tests pass'); + } + + public function test_script_counts_failed_tests_correctly(): void + { + // Arrange: Make fixture fail + $this->makeFixtureFail(); + $this->runScript(); + + // Act: Run again and check the count in output + $this->runScript($output); + + // Assert: Output should mention the count of failed tests + $this->assertStringContainsString('Logfile found', $output); + + // The script should report finding failed tests + $this->assertMatchesRegularExpression( + '/Found \d+ previously failing tests/', + $output, + 'Should report count of failed tests' + ); + } + + public function test_script_exits_with_zero_on_success(): void + { + // Arrange: All tests passing + $this->makeFixturePass(); + + // Act: Run script + $exitCode = $this->runScript(); + + // Assert: Should exit with 0 + $this->assertEquals(0, $exitCode, 'Script should always exit with code 0'); + } + + public function test_script_creates_valid_junit_xml(): void + { + // Arrange: Make fixture fail so junit.xml persists + $this->makeFixtureFail(); + + // Act: Run script + $this->runScript(); + + // Assert: junit.xml should be valid XML + $this->assertFileExists($this->junitPath); + + $xml = @simplexml_load_file($this->junitPath); + $this->assertNotFalse($xml, 'junit.xml should be valid XML'); + $this->assertEquals('testsuites', $xml->getName()); + } + + public function test_script_handles_no_failures_on_first_run(): void + { + // Arrange: All tests passing + $this->makeFixturePass(); + + // Act: Run script (first run, no logfile) + $exitCode = $this->runScript($output); + + // Assert: Should run full suite and clean up immediately if all pass + $this->assertEquals(0, $exitCode); + $this->assertStringContainsString('Logfile not found', $output); + + // The logfile might be cleaned up immediately after first run if all tests pass + // Check if we see success message + if (!file_exists($this->junitPath)) { + $this->assertStringContainsString('No failed tests', $output); + } + } + + public function test_script_output_contains_expected_messages(): void + { + // Test that the script outputs the expected emoji/messages + $this->makeFixtureFail(); + $this->runScript($firstOutput); + + // First run: should see "Logfile not found" + $this->assertStringContainsString('Logfile not found', $firstOutput); + $this->assertStringContainsString('Running the test suite', $firstOutput); + + // Second run: should see "Logfile found" + $this->runScript($secondOutput); + $this->assertStringContainsString('Logfile found', $secondOutput); + $this->assertStringContainsString('Searching for previously failing tests', $secondOutput); + } + + public function test_script_persists_junit_xml_when_tests_fail(): void + { + // Arrange: Make fixture fail + $this->makeFixtureFail(); + + // Act: Run script + $this->runScript(); + + // Assert: junit.xml should persist (not be deleted) + $this->assertFileExists($this->junitPath, 'junit.xml should not be deleted when tests fail'); + } + + public function test_script_uses_xmlstarlet_for_transformations(): void + { + // This test verifies xmlstarlet is available (required dependency) + // Act: Run script + $exitCode = $this->runScript($output); + + // Assert: Should not contain errors about xmlstarlet + $this->assertStringNotContainsString('xmlstarlet: command not found', $output); + $this->assertStringNotContainsString('xmlstarlet: not found', $output); + } + + public function test_script_detects_xsl_files_in_project_root(): void + { + // Act: Run script + $exitCode = $this->runScript($output); + + // Assert: Should not contain XSL path detection errors + $this->assertStringNotContainsString('Cannot find XSL transformation files', $output); + $this->assertEquals(0, $exitCode); + } +} diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index 5e1e8f1..0000000 --- a/tests/README.md +++ /dev/null @@ -1,113 +0,0 @@ -# Tests - -This directory contains the test suite for the phpunit-failed-runner package. - -## Test Structure - -The test suite uses fixture classes that simulate test failures and successes through a simple `success()` method: - -### Fixture Classes (`tests/Fixtures/`) - -- **AlwaysPassingFixture.php** - Always returns `true` (passing tests) -- **InitiallyFailingFixture.php** - Returns `false` initially (can be "fixed" to return `true`) -- **AnotherFailingFixture.php** - Returns `false` initially (can be "fixed" to return `true`) - -### Test Cases - -- **AlwaysPassingTest.php** - Tests that use the always-passing fixture -- **InitiallyFailingTest.php** - Tests that use InitiallyFailingFixture -- **AnotherFailingTest.php** - Tests that use AnotherFailingFixture - -## Running Tests - -### Run all tests normally - -```bash -./vendor/bin/phpunit -``` - -or - -```bash -composer test -``` - -### Multi-Phase Test Demonstration - -The package includes scripts that demonstrate the incremental test-fixing workflow: - -#### Interactive Demo - -```bash -./tests/demo-multi-phase.sh -``` - -This script runs through the multi-phase workflow interactively, pausing between phases to show: -1. Initial test run with failures -2. Fixing the first set of failing tests -3. Fixing the remaining failing tests -4. Final verification that all tests pass - -#### Automated Test - -```bash -./tests/run-automated-test.sh -``` - -or - -```bash -composer test:auto -``` - -This script automatically runs through all phases and validates: -- Phase 1: Initial run creates 3 failures -- Phase 2: After fixing InitiallyFailingFixture, only 1 failure remains -- Phase 3: After fixing AnotherFailingFixture, all tests pass and junit.xml is cleaned up -- Phase 4: Full test suite passes - -## How It Works - -The multi-phase tests use `sed` to modify the return values in fixture files inline: - -```bash -# Simulate a failure -sed -i 's/return true;/return false;/g' tests/Fixtures/InitiallyFailingFixture.php - -# Simulate a fix -sed -i 's/return false;/return true;/g' tests/Fixtures/InitiallyFailingFixture.php -``` - -This demonstrates the real-world workflow of: -1. Running tests and having some fail (junit.xml created) -2. Fixing code to make tests pass -3. Re-running only the previously failed tests -4. Continuing until all tests pass - -## What the Automated Test Validates - -The `run-automated-test.sh` script is an **integration test** that verifies the `phpunit-failed-runner` script's behavior by checking its actual output: - -### Phase 1: Initial Run -- Verifies script detects "Logfile not found" -- Verifies script runs full test suite -- Verifies junit.xml is created -- Verifies failures are reported - -### Phase 2: Incremental Re-run -- Verifies script detects "Logfile found" -- Verifies script finds "previously failing tests" -- Verifies script filters and runs only failed tests -- Verifies junit.xml persists (because tests still fail) - -### Phase 3: All Tests Pass -- Verifies script shows "No failed tests! Great job!" -- Verifies script automatically deletes junit.xml -- Confirms cleanup behavior works correctly - -### Phase 4: Clean State -- Verifies script correctly handles clean state (no junit.xml) -- Verifies script runs full suite when starting fresh -- Verifies all tests pass and junit.xml is cleaned up - -The test validates the **actual script behavior** rather than duplicating its logic, ensuring the phpunit-failed-runner script works as intended. diff --git a/tests/demo-multi-phase.sh b/tests/demo-multi-phase.sh deleted file mode 100755 index a00ac5a..0000000 --- a/tests/demo-multi-phase.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/bin/bash - -# Multi-phase test runner demonstration script -# This script simulates the full workflow of: -# 1. Running tests with failures -# 2. Fixing the failing tests -# 3. Re-running only the failed tests -# 4. Verifying all tests pass - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" - -cd "$PROJECT_ROOT" - -echo "======================================" -echo "Multi-Phase Test Runner Demonstration" -echo "======================================" -echo "" - -# Ensure junit.xml is removed before starting -if [ -f "junit.xml" ]; then - echo "Cleaning up previous junit.xml..." - rm junit.xml -fi - -# Phase 1: Run tests with failures -echo "======================================" -echo "PHASE 1: Initial test run (expect failures)" -echo "======================================" -echo "" - -# Ensure fixtures are set to fail -sed -i 's/return true;/return false;/g' tests/Fixtures/InitiallyFailingFixture.php -sed -i 's/return true;/return false;/g' tests/Fixtures/AnotherFailingFixture.php - -echo "Running: ./bin/phpunit-failed-runner" -./bin/phpunit-failed-runner || true - -echo "" -echo "Press Enter to continue to Phase 2..." -read -r - -# Phase 2: Fix the first fixture -echo "" -echo "======================================" -echo "PHASE 2: Fix InitiallyFailingFixture" -echo "======================================" -echo "" - -echo "Fixing InitiallyFailingFixture (changing return false to return true)..." -sed -i 's/return false;/return true;/g' tests/Fixtures/InitiallyFailingFixture.php - -echo "Running: ./bin/phpunit-failed-runner (should only run previously failed tests)" -./bin/phpunit-failed-runner || true - -echo "" -echo "Press Enter to continue to Phase 3..." -read -r - -# Phase 3: Fix the second fixture -echo "" -echo "======================================" -echo "PHASE 3: Fix AnotherFailingFixture" -echo "======================================" -echo "" - -echo "Fixing AnotherFailingFixture (changing return false to return true)..." -sed -i 's/return false;/return true;/g' tests/Fixtures/AnotherFailingFixture.php - -echo "Running: ./bin/phpunit-failed-runner (should only run remaining failed test)" -./bin/phpunit-failed-runner || true - -echo "" -echo "======================================" -echo "PHASE 4: Verify all tests pass" -echo "======================================" -echo "" - -echo "Running full test suite to verify everything passes..." -./vendor/bin/phpunit - -echo "" -echo "======================================" -echo "Demo Complete!" -echo "======================================" -echo "" -echo "To reset fixtures for another demo run:" -echo " sed -i 's/return true;/return false;/g' tests/Fixtures/InitiallyFailingFixture.php" -echo " sed -i 's/return true;/return false;/g' tests/Fixtures/AnotherFailingFixture.php" -echo "" diff --git a/tests/run-additional-tests.sh b/tests/run-additional-tests.sh deleted file mode 100755 index 2802aaf..0000000 --- a/tests/run-additional-tests.sh +++ /dev/null @@ -1,199 +0,0 @@ -#!/bin/bash - -# Additional integration tests for phpunit-failed-runner -# Tests additional functionality beyond the basic multi-phase workflow - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" - -cd "$PROJECT_ROOT" - -echo "==========================================" -echo "Additional Integration Tests" -echo "==========================================" -echo "" - -# Cleanup function -cleanup() { - echo "Cleaning up..." - rm -f junit.xml - sed -i 's/return false;/return true;/g' tests/Fixtures/InitiallyFailingFixture.php 2>/dev/null || true - sed -i 's/return false;/return true;/g' tests/Fixtures/AnotherFailingFixture.php 2>/dev/null || true -} - -trap cleanup EXIT - -# Test 1: Passing additional parameters to PHPUnit -echo "TEST 1: Passing additional parameters" -echo "========================================" -echo "" - -rm -f junit.xml -sed -i 's/return true;/return false;/g' tests/Fixtures/InitiallyFailingFixture.php - -echo "Running with --testdox parameter..." -output=$(./bin/phpunit-failed-runner --testdox 2>&1) - -if ! echo "$output" | grep -q "Initially Failing"; then - echo "ERROR: --testdox parameter not passed through" - exit 1 -fi - -echo "✓ Additional parameters passed correctly" -echo "" - -# Test 2: XSL transformations produce correct filter -echo "TEST 2: XSL filter generation" -echo "==============================" -echo "" - -rm -f junit.xml -./bin/phpunit-failed-runner > /dev/null 2>&1 || true - -if [ ! -f "junit.xml" ]; then - echo "ERROR: junit.xml not created" - exit 1 -fi - -# Generate the filter manually to verify XSL works -filter=$(xmlstarlet tr ./prune.xsl junit.xml | xmlstarlet tr --omit-decl ./failed-tests.xsl) - -if [ -z "$filter" ]; then - echo "ERROR: XSL transformation produced empty filter" - exit 1 -fi - -# Check filter format (should be like: TestClass::testMethod|AnotherTest::anotherMethod) -if ! echo "$filter" | grep -q "::"; then - echo "ERROR: Filter doesn't contain :: separator" - exit 1 -fi - -echo "Generated filter: $filter" -echo "✓ XSL transformations work correctly" -echo "" - -# Test 3: All tests failing -echo "TEST 3: All tests fail scenario" -echo "================================" -echo "" - -rm -f junit.xml -sed -i 's/return true;/return false;/g' tests/Fixtures/AlwaysPassingFixture.php -sed -i 's/return true;/return false;/g' tests/Fixtures/InitiallyFailingFixture.php -sed -i 's/return true;/return false;/g' tests/Fixtures/AnotherFailingFixture.php - -output=$(./bin/phpunit-failed-runner 2>&1) - -if ! echo "$output" | grep -q "5.*test"; then - echo "ERROR: Should report 5 total tests" - exit 1 -fi - -# Run again to verify it tries to run all failed tests -output=$(./bin/phpunit-failed-runner 2>&1) - -if ! echo "$output" | grep -q "Found 5 previously failing tests"; then - echo "ERROR: Should find all 5 tests failed" - exit 1 -fi - -# Reset AlwaysPassingFixture -sed -i 's/return false;/return true;/g' tests/Fixtures/AlwaysPassingFixture.php - -echo "✓ All tests failing handled correctly" -echo "" - -# Test 4: XSL path detection -echo "TEST 4: XSL path detection" -echo "==========================" -echo "" - -# Verify XSL files are found -if [ ! -f "./prune.xsl" ]; then - echo "ERROR: prune.xsl not found in package root" - exit 1 -fi - -if [ ! -f "./count-failed.xsl" ]; then - echo "ERROR: count-failed.xsl not found in package root" - exit 1 -fi - -if [ ! -f "./failed-tests.xsl" ]; then - echo "ERROR: failed-tests.xsl not found in package root" - exit 1 -fi - -echo "✓ XSL files detected correctly" -echo "" - -# Test 5: Error handling (create a fixture with an error vs failure) -echo "TEST 5: Handling errors and failures" -echo "=====================================" -echo "" - -# Create a temporary test that throws an error -cat > tests/Fixtures/ErrorFixture.php << 'EOF' - tests/ErrorTest.php << 'EOF' -assertTrue($fixture->success()); - } -} -EOF - -rm -f junit.xml -output=$(./bin/phpunit-failed-runner 2>&1 || true) - -if ! echo "$output" | grep -q "error\|Error\|ERROR"; then - echo "WARNING: Error might not be reported clearly (this may be OK)" -fi - -# Run again to verify errors are also tracked -if [ -f "junit.xml" ]; then - output=$(./bin/phpunit-failed-runner 2>&1 || true) - - if ! echo "$output" | grep -q "previously failing tests"; then - echo "ERROR: Errors should also be tracked as failed tests" - exit 1 - fi - - echo "✓ Errors handled correctly" -else - echo "WARNING: junit.xml not created with error" -fi - -# Cleanup error test files -rm -f tests/Fixtures/ErrorFixture.php -rm -f tests/ErrorTest.php - -echo "" - -echo "==========================================" -echo "✓ All additional tests passed!" -echo "==========================================" -echo "" diff --git a/tests/run-automated-test.sh b/tests/run-automated-test.sh deleted file mode 100755 index 3a61b85..0000000 --- a/tests/run-automated-test.sh +++ /dev/null @@ -1,172 +0,0 @@ -#!/bin/bash - -# Automated multi-phase test runner (non-interactive) -# This script tests the phpunit-failed-runner script by verifying its actual output and behavior - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" - -cd "$PROJECT_ROOT" - -echo "======================================" -echo "Automated Multi-Phase Test Runner" -echo "======================================" -echo "" - -# Cleanup function to reset fixtures -cleanup() { - echo "Resetting fixtures to passing state..." - sed -i 's/return false;/return true;/g' tests/Fixtures/InitiallyFailingFixture.php 2>/dev/null || true - sed -i 's/return false;/return true;/g' tests/Fixtures/AnotherFailingFixture.php 2>/dev/null || true - rm -f junit.xml -} - -# Ensure cleanup on exit -trap cleanup EXIT - -# Ensure junit.xml is removed before starting -rm -f junit.xml - -# Phase 1: Run tests with failures - should detect no logfile and run full suite -echo "PHASE 1: Initial test run (expect failures)" -echo "============================================" -echo "" - -# Set fixtures to fail -sed -i 's/return true;/return false;/g' tests/Fixtures/InitiallyFailingFixture.php -sed -i 's/return true;/return false;/g' tests/Fixtures/AnotherFailingFixture.php - -echo "Running: ./bin/phpunit-failed-runner" -output=$(./bin/phpunit-failed-runner 2>&1) -echo "$output" - -# Verify script detected no logfile -if ! echo "$output" | grep -q "Logfile not found"; then - echo "ERROR: Script should have detected no logfile" - exit 1 -fi - -# Verify junit.xml was created -if [ ! -f "junit.xml" ]; then - echo "ERROR: junit.xml should have been created" - exit 1 -fi - -# Verify we have failures (PHPUnit should report failures) -if ! echo "$output" | grep -q "FAILURES!"; then - echo "ERROR: Expected test failures in Phase 1" - exit 1 -fi - -echo "" -echo "✓ Phase 1 complete: Script detected no logfile, ran full suite, tests failed as expected" -echo "" - -# Phase 2: Fix the first fixture and re-run - script should detect logfile and filter -echo "PHASE 2: Fix InitiallyFailingFixture and re-run" -echo "================================================" -echo "" - -echo "Fixing InitiallyFailingFixture..." -sed -i 's/return false;/return true;/g' tests/Fixtures/InitiallyFailingFixture.php - -echo "Running: ./bin/phpunit-failed-runner" -output=$(./bin/phpunit-failed-runner 2>&1) -echo "$output" - -# Verify script found the logfile -if ! echo "$output" | grep -q "Logfile found"; then - echo "ERROR: Script should have detected existing logfile" - exit 1 -fi - -# Verify script found previously failing tests -if ! echo "$output" | grep -q "Found .* previously failing tests"; then - echo "ERROR: Script should have found previously failing tests" - exit 1 -fi - -# Verify we still have 1 failure (AnotherFailingTest) -if ! echo "$output" | grep -q "FAILURES!"; then - echo "ERROR: Expected AnotherFailingFixture to still fail in Phase 2" - exit 1 -fi - -# Verify junit.xml still exists (because there are still failures) -if [ ! -f "junit.xml" ]; then - echo "ERROR: junit.xml should still exist because tests failed" - exit 1 -fi - -echo "" -echo "✓ Phase 2 complete: Script detected logfile, filtered to failed tests, 1 test still failing" -echo "" - -# Phase 3: Fix the second fixture and re-run - script should show success and cleanup -echo "PHASE 3: Fix AnotherFailingFixture and re-run" -echo "==============================================" -echo "" - -echo "Fixing AnotherFailingFixture..." -sed -i 's/return false;/return true;/g' tests/Fixtures/AnotherFailingFixture.php - -echo "Running: ./bin/phpunit-failed-runner" -output=$(./bin/phpunit-failed-runner 2>&1) -echo "$output" - -# Verify script found the logfile -if ! echo "$output" | grep -q "Logfile found"; then - echo "ERROR: Script should have detected existing logfile" - exit 1 -fi - -# Verify script shows success message -if ! echo "$output" | grep -q "No failed tests! Great job!"; then - echo "ERROR: Script should have shown success message" - exit 1 -fi - -# Verify junit.xml was deleted by the script -if [ -f "junit.xml" ]; then - echo "ERROR: Script should have deleted junit.xml after all tests pass" - exit 1 -fi - -echo "" -echo "✓ Phase 3 complete: Script detected all tests pass, cleaned up junit.xml" -echo "" - -# Phase 4: Run script again - should detect no logfile and run full suite (all passing) -echo "PHASE 4: Verify clean state - no logfile exists" -echo "================================================" -echo "" - -echo "Running: ./bin/phpunit-failed-runner" -output=$(./bin/phpunit-failed-runner 2>&1) -echo "$output" - -# Verify script detected no logfile -if ! echo "$output" | grep -q "Logfile not found"; then - echo "ERROR: Script should have detected no logfile in clean state" - exit 1 -fi - -# Verify all tests pass -if ! echo "$output" | grep -q "OK"; then - echo "ERROR: All tests should pass" - exit 1 -fi - -# Verify script cleaned up junit.xml again -if [ -f "junit.xml" ]; then - echo "ERROR: Script should have deleted junit.xml after successful run" - exit 1 -fi - -echo "" -echo "✓ Phase 4 complete: Script handled clean state correctly, all tests pass" -echo "" -echo "======================================" -echo "✓ All phases completed successfully!" -echo "======================================" -echo "" From aa83b677341fd0494dac88ddf5acea8290b98d85 Mon Sep 17 00:00:00 2001 From: Chris Di Carlo Date: Mon, 24 Nov 2025 01:23:32 -0500 Subject: [PATCH 4/7] build: add Github Action to run tests --- .github/workflows/tests.yml | 50 +++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..0dc9a60 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,50 @@ +name: Tests + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + php: ['8.1', '8.2', '8.3', '8.4', '8.5'] + + name: PHP ${{ matrix.php }} - ${{ matrix.os }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip + coverage: none + + - name: Install xmlstarlet + run: sudo apt-get update && sudo apt-get install -y xmlstarlet + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-php-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php-${{ matrix.php }}- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run test suite + run: ./vendor/bin/phpunit --testdox From 63a9c8be3ca3caa9903490eb21edcf7f6d6c8aa8 Mon Sep 17 00:00:00 2001 From: Chris Di Carlo Date: Mon, 24 Nov 2025 01:26:00 -0500 Subject: [PATCH 5/7] docs: remove reference to old testing README --- README.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/README.md b/README.md index a097cdc..ba6b668 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ For XmlStarlet on other platforms, see [here](http://xmlstar.sourceforge.net/doc ## Testing -This package includes a comprehensive test suite that demonstrates the incremental test-fixing workflow. See [tests/README.md](tests/README.md) for details. +This package includes a comprehensive test suite that demonstrates the incremental test-fixing workflow. Run the test suite: @@ -40,12 +40,6 @@ Run the test suite: composer test ``` -Run the automated multi-phase demonstration: - -```bash -composer test:auto -``` - ### Changelog Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. From c65e1b0374760dd9a809631ee1b4d07987664721 Mon Sep 17 00:00:00 2001 From: Chris Di Carlo Date: Mon, 24 Nov 2025 01:29:10 -0500 Subject: [PATCH 6/7] build: add GitHub Action to update changelog --- .github/workflows/update-changelog.yml | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/update-changelog.yml diff --git a/.github/workflows/update-changelog.yml b/.github/workflows/update-changelog.yml new file mode 100644 index 0000000..277821c --- /dev/null +++ b/.github/workflows/update-changelog.yml @@ -0,0 +1,32 @@ +name: "Update Changelog" + +on: + release: + types: [released] + +permissions: + contents: write + +jobs: + update: + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout code + uses: actions/checkout@v5 + with: + ref: main + + - name: Update Changelog + uses: stefanzweifel/changelog-updater-action@v1 + with: + latest-version: ${{ github.event.release.name }} + release-notes: ${{ github.event.release.body }} + + - name: Commit updated CHANGELOG + uses: stefanzweifel/git-auto-commit-action@v7 + with: + branch: main + commit_message: Update CHANGELOG + file_pattern: CHANGELOG.md From 9151778758ee975f577e9d4b7bb973fb9b43f4c1 Mon Sep 17 00:00:00 2001 From: Chris Di Carlo Date: Mon, 24 Nov 2025 01:35:54 -0500 Subject: [PATCH 7/7] build: clean up GitHub Action, expand tested PHP versions --- .github/workflows/tests.yml | 16 +- .gitignore | 1 + composer.json | 2 +- composer.lock | 1787 ----------------------------------- 4 files changed, 4 insertions(+), 1802 deletions(-) delete mode 100644 composer.lock diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0dc9a60..5397f59 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - php: ['8.1', '8.2', '8.3', '8.4', '8.5'] + php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] name: PHP ${{ matrix.php }} - ${{ matrix.os }} @@ -31,20 +31,8 @@ jobs: - name: Install xmlstarlet run: sudo apt-get update && sudo apt-get install -y xmlstarlet - - name: Validate composer.json and composer.lock - run: composer validate --strict - - - name: Cache Composer packages - id: composer-cache - uses: actions/cache@v3 - with: - path: vendor - key: ${{ runner.os }}-php-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }} - restore-keys: | - ${{ runner.os }}-php-${{ matrix.php }}- - - name: Install dependencies - run: composer install --prefer-dist --no-progress + run: composer update --prefer-dist --no-progress - name: Run test suite run: ./vendor/bin/phpunit --testdox diff --git a/.gitignore b/.gitignore index 178d88b..dddbc6a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ vendor .phpunit.cache +composer.lock diff --git a/composer.json b/composer.json index 2b3b6e4..b5986dd 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "bin/phpunit-failed-runner" ], "require-dev": { - "phpunit/phpunit": "^10.5|^11.0" + "phpunit/phpunit": "^8.5|^9.6|^10.5|^11.0" }, "autoload-dev": { "psr-4": { diff --git a/composer.lock b/composer.lock deleted file mode 100644 index d99c32d..0000000 --- a/composer.lock +++ /dev/null @@ -1,1787 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "b4dc0e7fc00dcf282353d31702aa1325", - "packages": [], - "packages-dev": [ - { - "name": "myclabs/deep-copy", - "version": "1.13.4", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3 <3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpspec/prophecy": "^1.10", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2025-08-01T08:46:24+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v5.6.2", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-json": "*", - "ext-tokenizer": "*", - "php": ">=7.4" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" - }, - "time": "2025-10-21T19:32:17+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "54750ef60c58e43759730615a392c31c80e23176" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", - "reference": "54750ef60c58e43759730615a392c31c80e23176", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2024-03-03T12:33:53+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "11.0.11", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", - "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^5.4.0", - "php": ">=8.2", - "phpunit/php-file-iterator": "^5.1.0", - "phpunit/php-text-template": "^4.0.1", - "sebastian/code-unit-reverse-lookup": "^4.0.1", - "sebastian/complexity": "^4.0.1", - "sebastian/environment": "^7.2.0", - "sebastian/lines-of-code": "^3.0.1", - "sebastian/version": "^5.0.2", - "theseer/tokenizer": "^1.2.3" - }, - "require-dev": { - "phpunit/phpunit": "^11.5.2" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "11.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.11" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", - "type": "tidelift" - } - ], - "time": "2025-08-27T14:37:49+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "5.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.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" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-08-27T05:02:59+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "5.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", - "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^11.0" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.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" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T05:07:44+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", - "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.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" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T05:08:43+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "7.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", - "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "7.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" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "security": "https://github.com/sebastianbergmann/php-timer/security/policy", - "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T05:09:35+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "11.5.44", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c346885c95423eda3f65d85a194aaa24873cda82" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c346885c95423eda3f65d85a194aaa24873cda82", - "reference": "c346885c95423eda3f65d85a194aaa24873cda82", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.4", - "phar-io/manifest": "^2.0.4", - "phar-io/version": "^3.2.1", - "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.11", - "phpunit/php-file-iterator": "^5.1.0", - "phpunit/php-invoker": "^5.0.1", - "phpunit/php-text-template": "^4.0.1", - "phpunit/php-timer": "^7.0.1", - "sebastian/cli-parser": "^3.0.2", - "sebastian/code-unit": "^3.0.3", - "sebastian/comparator": "^6.3.2", - "sebastian/diff": "^6.0.2", - "sebastian/environment": "^7.2.1", - "sebastian/exporter": "^6.3.2", - "sebastian/global-state": "^7.0.2", - "sebastian/object-enumerator": "^6.0.1", - "sebastian/type": "^5.1.3", - "sebastian/version": "^5.0.2", - "staabm/side-effects-detector": "^1.0.5" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "11.5-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.44" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" - } - ], - "time": "2025-11-13T07:17:35+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", - "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.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" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:41:36+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", - "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.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" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "security": "https://github.com/sebastianbergmann/code-unit/security/policy", - "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2025-03-19T07:56:08+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "183a9b2632194febd219bb9246eee421dad8d45e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", - "reference": "183a9b2632194febd219bb9246eee421dad8d45e", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:45:54+00:00" - }, - { - "name": "sebastian/comparator", - "version": "6.3.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", - "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-mbstring": "*", - "php": ">=8.2", - "sebastian/diff": "^6.0", - "sebastian/exporter": "^6.0" - }, - "require-dev": { - "phpunit/phpunit": "^11.4" - }, - "suggest": { - "ext-bcmath": "For comparing BcMath\\Number objects" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.3-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", - "type": "tidelift" - } - ], - "time": "2025-08-10T08:07:46+00:00" - }, - { - "name": "sebastian/complexity", - "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", - "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^5.0", - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.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" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:49:50+00:00" - }, - { - "name": "sebastian/diff", - "version": "6.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0", - "symfony/process": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:53:05+00:00" - }, - { - "name": "sebastian/environment", - "version": "7.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", - "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.3" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "7.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "https://github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", - "type": "tidelift" - } - ], - "time": "2025-05-21T11:55:47+00:00" - }, - { - "name": "sebastian/exporter", - "version": "6.3.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", - "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": ">=8.2", - "sebastian/recursion-context": "^6.0" - }, - "require-dev": { - "phpunit/phpunit": "^11.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.3-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", - "type": "tidelift" - } - ], - "time": "2025-09-24T06:12:51+00:00" - }, - { - "name": "sebastian/global-state", - "version": "7.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "3be331570a721f9a4b5917f4209773de17f747d7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", - "reference": "3be331570a721f9a4b5917f4209773de17f747d7", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "sebastian/object-reflector": "^4.0", - "sebastian/recursion-context": "^6.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "7.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "https://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:57:36+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", - "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^5.0", - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.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" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:58:38+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "6.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "f5b498e631a74204185071eb41f33f38d64608aa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", - "reference": "f5b498e631a74204185071eb41f33f38d64608aa", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "sebastian/object-reflector": "^4.0", - "sebastian/recursion-context": "^6.0" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T05:00:13+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", - "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T05:01:32+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "6.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", - "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", - "type": "tidelift" - } - ], - "time": "2025-08-13T04:42:22+00:00" - }, - { - "name": "sebastian/type", - "version": "5.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", - "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/type", - "type": "tidelift" - } - ], - "time": "2025-08-09T06:55:48+00:00" - }, - { - "name": "sebastian/version", - "version": "5.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", - "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.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" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "security": "https://github.com/sebastianbergmann/version/security/policy", - "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-10-09T05:16:32+00:00" - }, - { - "name": "staabm/side-effects-detector", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/staabm/side-effects-detector.git", - "reference": "d8334211a140ce329c13726d4a715adbddd0a163" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", - "reference": "d8334211a140ce329c13726d4a715adbddd0a163", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^1.12.6", - "phpunit/phpunit": "^9.6.21", - "symfony/var-dumper": "^5.4.43", - "tomasvotruba/type-coverage": "1.0.0", - "tomasvotruba/unused-public": "1.0.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "lib/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A static analysis tool to detect side effects in PHP code", - "keywords": [ - "static analysis" - ], - "support": { - "issues": "https://github.com/staabm/side-effects-detector/issues", - "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" - }, - "funding": [ - { - "url": "https://github.com/staabm", - "type": "github" - } - ], - "time": "2024-10-20T05:08:20+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.3.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.3.1" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2025-11-17T20:03:58+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": {}, - "prefer-stable": false, - "prefer-lowest": false, - "platform": {}, - "platform-dev": {}, - "plugin-api-version": "2.6.0" -}