feat(php): add PHPUnit support with compact output (65%+ token savings)#874
feat(php): add PHPUnit support with compact output (65%+ token savings)#874Beninho wants to merge 3 commits into
Conversation
e4a75f6 to
a320846
Compare
a320846 to
37d822a
Compare
dd90f42 to
2e28122
Compare
…t, paratest, ecs, pint, artisan) Consolidates three stalled upstream PRs plus a new .phpt filter and Pint support into one coherent PHP ecosystem module: - `rtk php` + `rtk artisan`: syntax check (-l) and Laravel artisan wrapper - `rtk phpunit`: structured-state parser; aggregate counts + bounded failure list (sourced from rtk-ai#874, refactored to use runner::run_filtered and strip emoji) - `rtk phpstan`: typed serde::Deserialize parser for --error-format=json; groups errors by file, sorts by count desc; handles utility commands (--version, list, clear-result-cache) as passthrough (sourced from rtk-ai#1110, strip emoji in success path) - `rtk pest` + `rtk paratest`: shared test_output helper (from rtk-ai#1246) - `rtk ecs` + `rtk pint`: code style fixers; pint uses --format=json for structured per-file rule counts - `rtk phpt`: wraps `php run-tests.php` for php-src and PHP extensions; collapses per-test PASS chatter (99.5% byte reduction on a 5322-test run) Composer custom-bin-dir detection: `composer_bin_dirs()` reads COMPOSER_BIN_DIR and composer.json `config.bin-dir` so `tools/bin/phpunit` classifies identically to `vendor/bin/phpunit`. registry.rs normalizes tool paths before matching so one rule covers every Composer layout. Sources: - rtk-ai#1246 (aaronflorey): php, artisan, ecs, pest, paratest, test_output, utils, composer_bin_dirs + registry normalization. phpunit and phpstan stubs replaced with deeper implementations below. - rtk-ai#874 (Beninho): phpunit state-machine parser (ported). - rtk-ai#1110 (LucianoVandi): phpstan typed parser (emoji stripped). - New: pint_cmd.rs, phpt_cmd.rs. 1744 tests pass (1683 baseline + 61 new).
…t, paratest, ecs, pint, artisan) Consolidates three stalled upstream PRs plus a new .phpt filter and Pint support into one coherent PHP ecosystem module: - `rtk php` + `rtk artisan`: syntax check (-l) and Laravel artisan wrapper - `rtk phpunit`: structured-state parser; aggregate counts + bounded failure list (sourced from rtk-ai#874, refactored to use runner::run_filtered and strip emoji) - `rtk phpstan`: typed serde::Deserialize parser for --error-format=json; groups errors by file, sorts by count desc; handles utility commands (--version, list, clear-result-cache) as passthrough (sourced from rtk-ai#1110, strip emoji in success path) - `rtk pest` + `rtk paratest`: shared test_output helper (from rtk-ai#1246) - `rtk ecs` + `rtk pint`: code style fixers; pint uses --format=json for structured per-file rule counts - `rtk phpt`: wraps `php run-tests.php` for php-src and PHP extensions; collapses per-test PASS chatter (99.5% byte reduction on a 5322-test run) Composer custom-bin-dir detection: `composer_bin_dirs()` reads COMPOSER_BIN_DIR and composer.json `config.bin-dir` so `tools/bin/phpunit` classifies identically to `vendor/bin/phpunit`. registry.rs normalizes tool paths before matching so one rule covers every Composer layout. Sources: - rtk-ai#1246 (aaronflorey): php, artisan, ecs, pest, paratest, test_output, utils, composer_bin_dirs + registry normalization. phpunit and phpstan stubs replaced with deeper implementations below. - rtk-ai#874 (Beninho): phpunit state-machine parser (ported). - rtk-ai#1110 (LucianoVandi): phpstan typed parser (emoji stripped). - New: pint_cmd.rs, phpt_cmd.rs. 1744 tests pass (1683 baseline + 61 new).
…t, paratest, ecs, pint, artisan) Consolidates three stalled upstream PRs plus a new .phpt filter and Pint support into one coherent PHP ecosystem module: - `rtk php` + `rtk artisan`: syntax check (-l) and Laravel artisan wrapper - `rtk phpunit`: structured-state parser; aggregate counts + bounded failure list (sourced from rtk-ai#874, refactored to use runner::run_filtered and strip emoji) - `rtk phpstan`: typed serde::Deserialize parser for --error-format=json; groups errors by file, sorts by count desc; handles utility commands (--version, list, clear-result-cache) as passthrough (sourced from rtk-ai#1110, strip emoji in success path) - `rtk pest` + `rtk paratest`: shared test_output helper (from rtk-ai#1246) - `rtk ecs` + `rtk pint`: code style fixers; pint uses --format=json for structured per-file rule counts - `rtk phpt`: wraps `php run-tests.php` for php-src and PHP extensions; collapses per-test PASS chatter (99.5% byte reduction on a 5322-test run) Composer custom-bin-dir detection: `composer_bin_dirs()` reads COMPOSER_BIN_DIR and composer.json `config.bin-dir` so `tools/bin/phpunit` classifies identically to `vendor/bin/phpunit`. registry.rs normalizes tool paths before matching so one rule covers every Composer layout. Sources: - rtk-ai#1246 (aaronflorey): php, artisan, ecs, pest, paratest, test_output, utils, composer_bin_dirs + registry normalization. phpunit and phpstan stubs replaced with deeper implementations below. - rtk-ai#874 (Beninho): phpunit state-machine parser (ported). - rtk-ai#1110 (LucianoVandi): phpstan typed parser (emoji stripped). - New: pint_cmd.rs, phpt_cmd.rs. 1744 tests pass (1683 baseline + 61 new).
…pest, paratest, ecs, pint) Consolidates the PHP-tooling work from three upstream PRs plus a new Pint module, leaving phpt to its own PR (rtk-ai#1503). - rtk php / rtk artisan: syntax check (-l) and Laravel artisan wrapper. - rtk phpunit: structured-state parser, aggregate counts, bounded failure list. Uses runner::run_filtered. - rtk phpstan: typed serde::Deserialize parser for --error-format=json, groups errors by file, sorts by count desc. Utility commands (--version, list, clear-result-cache) pass through unchanged. - rtk pest / rtk paratest: shared test_output helper. - rtk ecs / rtk pint: code-style fixers; pint uses --format=json for structured per-file rule counts. Composer custom-bin-dir detection: composer_bin_dirs() reads COMPOSER_BIN_DIR and composer.json config.bin-dir, so tools/bin/phpunit classifies identically to vendor/bin/phpunit. registry.rs normalizes tool paths before matching. Sources: - rtk-ai#1246 (aaronflorey, self-closed): php, artisan, ecs, pest, paratest, test_output, utils, composer_bin_dirs, registry normalization. - rtk-ai#874 (Beninho, open): phpunit state-machine parser. - rtk-ai#1110 (LucianoVandi, open): phpstan typed parser. - New: pint_cmd.rs. Tests: discover::registry 253 pass; cmds::php 36 pass; cargo build --release 0 errors; cargo fmt --check clean.
- New `rtk phpunit` command: state-machine filter that captures test names, assertion messages, and file locations from PHPUnit output, stripping version banner, progress dots, timing, and memory noise - Supports all invocation variants: `phpunit`, `vendor/bin/phpunit`, `bin/phpunit` (Symfony), `php vendor/bin/phpunit`, `php bin/phpunit` - Binary detection priority: global phpunit → bin/phpunit → vendor/bin/phpunit - Registers in discover/rules.rs for `rtk discover` and hook rewriting - 17 tests: 7 filter tests (including token savings ≥60%), 10 registry tests for classify/rewrite across all variants; real fixture in tests/fixtures/phpunit_raw.txt - "OK (X tests, Y assertions)" → already worked, now also shows counts for the generic fallback path - "OK, but incomplete, skipped, or risky tests!" → new handler shows skipped count (e.g. "PHPUnit: 9 tests, 15 assertions, 2 skipped") - Generic fallback now calls parse_counts to show test/assertion counts instead of bare "PHPUnit: OK" - parse_counts returns 4-tuple (tests, assertions, failures, skipped) to support Skipped: field with trailing-period stripping - 3 new tests covering each success variant - catch segfault + Fatal error # Conflicts: # src/cmds/mod.rs # src/main.rs
…rent_prefixes arg Remove unused PATTERNS const that triggered dead-code error. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2e28122 to
f97e423
Compare
…t, paratest, ecs, pint, artisan) Consolidates three stalled upstream PRs plus a new .phpt filter and Pint support into one coherent PHP ecosystem module: - `rtk php` + `rtk artisan`: syntax check (-l) and Laravel artisan wrapper - `rtk phpunit`: structured-state parser; aggregate counts + bounded failure list (sourced from rtk-ai#874, refactored to use runner::run_filtered and strip emoji) - `rtk phpstan`: typed serde::Deserialize parser for --error-format=json; groups errors by file, sorts by count desc; handles utility commands (--version, list, clear-result-cache) as passthrough (sourced from rtk-ai#1110, strip emoji in success path) - `rtk pest` + `rtk paratest`: shared test_output helper (from rtk-ai#1246) - `rtk ecs` + `rtk pint`: code style fixers; pint uses --format=json for structured per-file rule counts - `rtk phpt`: wraps `php run-tests.php` for php-src and PHP extensions; collapses per-test PASS chatter (99.5% byte reduction on a 5322-test run) Composer custom-bin-dir detection: `composer_bin_dirs()` reads COMPOSER_BIN_DIR and composer.json `config.bin-dir` so `tools/bin/phpunit` classifies identically to `vendor/bin/phpunit`. registry.rs normalizes tool paths before matching so one rule covers every Composer layout. Sources: - rtk-ai#1246 (aaronflorey): php, artisan, ecs, pest, paratest, test_output, utils, composer_bin_dirs + registry normalization. phpunit and phpstan stubs replaced with deeper implementations below. - rtk-ai#874 (Beninho): phpunit state-machine parser (ported). - rtk-ai#1110 (LucianoVandi): phpstan typed parser (emoji stripped). - New: pint_cmd.rs, phpt_cmd.rs. 1744 tests pass (1683 baseline + 61 new).
…pest, paratest, ecs, pint) Consolidates the PHP-tooling work from three upstream PRs plus a new Pint module, leaving phpt to its own PR (rtk-ai#1503). - rtk php / rtk artisan: syntax check (-l) and Laravel artisan wrapper. - rtk phpunit: structured-state parser, aggregate counts, bounded failure list. Uses runner::run_filtered. - rtk phpstan: typed serde::Deserialize parser for --error-format=json, groups errors by file, sorts by count desc. Utility commands (--version, list, clear-result-cache) pass through unchanged. - rtk pest / rtk paratest: shared test_output helper. - rtk ecs / rtk pint: code-style fixers; pint uses --format=json for structured per-file rule counts. Composer custom-bin-dir detection: composer_bin_dirs() reads COMPOSER_BIN_DIR and composer.json config.bin-dir, so tools/bin/phpunit classifies identically to vendor/bin/phpunit. registry.rs normalizes tool paths before matching. Sources: - rtk-ai#1246 (aaronflorey, self-closed): php, artisan, ecs, pest, paratest, test_output, utils, composer_bin_dirs, registry normalization. - rtk-ai#874 (Beninho, open): phpunit state-machine parser. - rtk-ai#1110 (LucianoVandi, open): phpstan typed parser. - New: pint_cmd.rs. Tests: discover::registry 253 pass; cmds::php 36 pass; cargo build --release 0 errors; cargo fmt --check clean.
…pest, paratest, ecs, pint) Consolidates the PHP-tooling work from three upstream PRs plus a new Pint module, leaving phpt to its own PR (rtk-ai#1503). - rtk php / rtk artisan: syntax check (-l) and Laravel artisan wrapper. - rtk phpunit: structured-state parser, aggregate counts, bounded failure list. Uses runner::run_filtered. - rtk phpstan: typed serde::Deserialize parser for --error-format=json, groups errors by file, sorts by count desc. Utility commands (--version, list, clear-result-cache) pass through unchanged. - rtk pest / rtk paratest: shared test_output helper. - rtk ecs / rtk pint: code-style fixers; pint uses --format=json for structured per-file rule counts. Composer custom-bin-dir detection: composer_bin_dirs() reads COMPOSER_BIN_DIR and composer.json config.bin-dir, so tools/bin/phpunit classifies identically to vendor/bin/phpunit. registry.rs normalizes tool paths before matching. Sources: - rtk-ai#1246 (aaronflorey, self-closed): php, artisan, ecs, pest, paratest, test_output, utils, composer_bin_dirs, registry normalization. - rtk-ai#874 (Beninho, open): phpunit state-machine parser. - rtk-ai#1110 (LucianoVandi, open): phpstan typed parser. - New: pint_cmd.rs. Tests: discover::registry 253 pass; cmds::php 36 pass; cargo build --release 0 errors; cargo fmt --check clean.
…t, paratest, ecs, pint, artisan) Consolidates three stalled upstream PRs plus a new .phpt filter and Pint support into one coherent PHP ecosystem module: - `rtk php` + `rtk artisan`: syntax check (-l) and Laravel artisan wrapper - `rtk phpunit`: structured-state parser; aggregate counts + bounded failure list (sourced from rtk-ai#874, refactored to use runner::run_filtered and strip emoji) - `rtk phpstan`: typed serde::Deserialize parser for --error-format=json; groups errors by file, sorts by count desc; handles utility commands (--version, list, clear-result-cache) as passthrough (sourced from rtk-ai#1110, strip emoji in success path) - `rtk pest` + `rtk paratest`: shared test_output helper (from rtk-ai#1246) - `rtk ecs` + `rtk pint`: code style fixers; pint uses --format=json for structured per-file rule counts - `rtk phpt`: wraps `php run-tests.php` for php-src and PHP extensions; collapses per-test PASS chatter (99.5% byte reduction on a 5322-test run) Composer custom-bin-dir detection: `composer_bin_dirs()` reads COMPOSER_BIN_DIR and composer.json `config.bin-dir` so `tools/bin/phpunit` classifies identically to `vendor/bin/phpunit`. registry.rs normalizes tool paths before matching so one rule covers every Composer layout. Sources: - rtk-ai#1246 (aaronflorey): php, artisan, ecs, pest, paratest, test_output, utils, composer_bin_dirs + registry normalization. phpunit and phpstan stubs replaced with deeper implementations below. - rtk-ai#874 (Beninho): phpunit state-machine parser (ported). - rtk-ai#1110 (LucianoVandi): phpstan typed parser (emoji stripped). - New: pint_cmd.rs, phpt_cmd.rs. 1744 tests pass (1683 baseline + 61 new).
KuSh
left a comment
There was a problem hiding this comment.
Hi @Beninho,
Thanks for your PR! I’ve left some feedback and suggestions for improvement. Once those are addressed, please rebase your changes on the latest develop branch to ensure a clean merge.
Let me know if you have any questions!
There was a problem hiding this comment.
Those changes are unwanted. Please revert
| // --- PHP tooling --- | ||
|
|
||
| #[test] | ||
| fn test_classify_phpunit() { |
There was a problem hiding this comment.
See how it has been done in test_classify_vitest() and test_rewrite_vitest for example. You can merge all your tests in two test functions
| section "PHP (conditional)" | ||
|
|
||
| if command -v phpunit &>/dev/null || [ -f vendor/bin/phpunit ] || [ -f bin/phpunit ]; then | ||
| assert_help "rtk phpunit" rtk phpunit --help |
There was a problem hiding this comment.
Not sure it is worth it without at least one assert_ok or assert_contains and/or assert_fails
| let phpunit_script = if std::path::Path::new("bin/phpunit").exists() { | ||
| "bin/phpunit" | ||
| } else { | ||
| "vendor/bin/phpunit" | ||
| }; |
There was a problem hiding this comment.
This should go in the else block
| }, | ||
| // PHP tooling | ||
| RtkRule { | ||
| pattern: r"^(?:php\s+)?(?:(?:vendor/bin|bin)/)?phpunit(?:\s|$)", |
There was a problem hiding this comment.
It seems, phpunit also document calling it like that:
./phpunit, ./bin/phpunit or ./vendor/bin/phpunit
You should check how rtk rewrite is called in that case and, if necessary, handle those rewrites too
| result | ||
| } | ||
|
|
||
| fn filter_phpunit_output(output: &str) -> String { |
There was a problem hiding this comment.
suggestion, you could use a struct like that for failures:
struct Failure {
header: String,
details: Vec<String>,
}As you only use the first 2 lines of detail, only store that and skip everything else.
Also, another possibility to reduce output a little bit: aggregate on error message so that we can list errors per message (if that's a good idea), something like:
PHPUnit: 110 tests, 340 assertions.
2 Failures:
- Failed asserting that false is true.
- UserTest::testEmail /path/to/test:42
- UserTest::testPassword /path/to/test:99
- AuthTest::testLogin /src/auth:15
- Failed asserting array has key "id".
- UserValidationTest::testStructure /src/validation:203| } | ||
|
|
||
| fn build_phpunit_summary(output: &str, failures: &[Vec<String>]) -> String { | ||
| let (tests, assertions, failures_count, _skipped) = parse_counts(output); |
There was a problem hiding this comment.
It seems if failures there's no skipped tests but that seems strange, could you check that it can't have skipped with failures? In that case, skipped is a valuable information that should not be erased
| tests, assertions, failures_count | ||
| ); | ||
| result.push_str("═══════════════════════════════════════\n"); | ||
| result.push_str("Failures:\n"); |
There was a problem hiding this comment.
This can be simplfied.
Just output the failure numbers directly on second line:
PHPUnit: {} tests, {} assertions[, {} skipped.
{} Failures:
- ...failures detail| k if k.starts_with("Skipped") => skipped = val, | ||
| _ => {} | ||
| } | ||
| } |
There was a problem hiding this comment.
shouldn't we break after the Tests line? Or should the numbers be accumulated?
| for line in output.lines() { | ||
| let trimmed = line.trim(); | ||
| if !trimmed.starts_with("Tests:") { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
It seems to always be the last line in your tests. At least it should be localted on the end, so iterating in the reverse order seems a good idea
…t, paratest, ecs, pint, artisan) Consolidates three stalled upstream PRs plus a new .phpt filter and Pint support into one coherent PHP ecosystem module: - `rtk php` + `rtk artisan`: syntax check (-l) and Laravel artisan wrapper - `rtk phpunit`: structured-state parser; aggregate counts + bounded failure list (sourced from rtk-ai#874, refactored to use runner::run_filtered and strip emoji) - `rtk phpstan`: typed serde::Deserialize parser for --error-format=json; groups errors by file, sorts by count desc; handles utility commands (--version, list, clear-result-cache) as passthrough (sourced from rtk-ai#1110, strip emoji in success path) - `rtk pest` + `rtk paratest`: shared test_output helper (from rtk-ai#1246) - `rtk ecs` + `rtk pint`: code style fixers; pint uses --format=json for structured per-file rule counts - `rtk phpt`: wraps `php run-tests.php` for php-src and PHP extensions; collapses per-test PASS chatter (99.5% byte reduction on a 5322-test run) Composer custom-bin-dir detection: `composer_bin_dirs()` reads COMPOSER_BIN_DIR and composer.json `config.bin-dir` so `tools/bin/phpunit` classifies identically to `vendor/bin/phpunit`. registry.rs normalizes tool paths before matching so one rule covers every Composer layout. Sources: - rtk-ai#1246 (aaronflorey): php, artisan, ecs, pest, paratest, test_output, utils, composer_bin_dirs + registry normalization. phpunit and phpstan stubs replaced with deeper implementations below. - rtk-ai#874 (Beninho): phpunit state-machine parser (ported). - rtk-ai#1110 (LucianoVandi): phpstan typed parser (emoji stripped). - New: pint_cmd.rs, phpt_cmd.rs. 1744 tests pass (1683 baseline + 61 new).
…pest, paratest, ecs, pint) Consolidates the PHP-tooling work from three upstream PRs plus a new Pint module, leaving phpt to its own PR (rtk-ai#1503). - rtk php / rtk artisan: syntax check (-l) and Laravel artisan wrapper. - rtk phpunit: structured-state parser, aggregate counts, bounded failure list. Uses runner::run_filtered. - rtk phpstan: typed serde::Deserialize parser for --error-format=json, groups errors by file, sorts by count desc. Utility commands (--version, list, clear-result-cache) pass through unchanged. - rtk pest / rtk paratest: shared test_output helper. - rtk ecs / rtk pint: code-style fixers; pint uses --format=json for structured per-file rule counts. Composer custom-bin-dir detection: composer_bin_dirs() reads COMPOSER_BIN_DIR and composer.json config.bin-dir, so tools/bin/phpunit classifies identically to vendor/bin/phpunit. registry.rs normalizes tool paths before matching. Sources: - rtk-ai#1246 (aaronflorey, self-closed): php, artisan, ecs, pest, paratest, test_output, utils, composer_bin_dirs, registry normalization. - rtk-ai#874 (Beninho, open): phpunit state-machine parser. - rtk-ai#1110 (LucianoVandi, open): phpstan typed parser. - New: pint_cmd.rs. Tests: discover::registry 253 pass; cmds::php 36 pass; cargo build --release 0 errors; cargo fmt --check clean.
|
Hi, thanks for your PR. This has been superseded by #1649, and you’ve been properly credited there. |
Summary
rtk phpunitcommand with a state-machine output filter (65%+ token savings)phpunit,vendor/bin/phpunit,bin/phpunit(Symfony),php vendor/bin/phpunit,php bin/phpunitChanges
src/cmds/mod.rssrc/cmds/php/mod.rssrc/cmds/php/phpunit_cmd.rssrc/main.rsis_operational_commandregistrationsrc/discover/rules.rssrc/discover/registry.rstests/fixtures/phpunit_raw.txtscripts/test-all.shREADME.mdFilter design
State machine:
Header → Failures → DoneOutput:
Test plan
cargo test --all— 1143 passed, 0 failedcargo fmt --all && cargo clippy --all-targets— clean for updated filesrtk phpunit --helpshows descriptionphpunit tests/→rtk phpunit tests/