Testing
Greph is validated with three layers of tests:
- Unit tests for isolated components (parsers, walkers, codecs, options).
- Oracle regression corpus that diffs Greph output against the canonical
grep,ripgrep, andast-grepbinaries on a fixed scenario set. - Probe-driven feature matrix that asserts the verified surface of the
rgandsgwrappers.
All three layers run in CI on every push.
Running the tests
# PHPUnit unit tests
composer test
# Oracle regression corpus
composer test:oracle
# Static analysis
composer analyse
# Coding standards
composer cs
composer cs:fix
# Full release-grade verification (analyse + cs + test + test:oracle)
composer verifycomposer verify is the bar for any release. If it does not pass, the change is not done.
The oracle model
Greph follows an oracle-driven verification model. For every scenario, three commands run:
- Oracle:
grep,ripgrep, orast-grep, captured against the scenario corpus. - Actual: Greph, captured against the same corpus.
- Comparator: a structured diff that checks line-by-line or semantic equivalence.
If Greph disagrees with all three oracles, Greph is wrong. If the oracles disagree with each other, the disagreement is documented in the scenario metadata and Greph is held to the most useful behavior (usually rg's).
The implementation lives under tests/Oracle/ and the scenarios live under scenarios/text/, scenarios/ast/, scenarios/rewrite/, and scenarios/edge/.
Scenario layout
Each scenario is a self-contained directory:
scenarios/text/literal-simple/
├── scenario.json # name, category, oracles, flags
├── setup/ # corpus to search through
├── oracle/ # captured oracle output (grep.txt, rg.txt, rg.json, ...)
├── actual/ # captured greph output
└── reports/ # comparison resultsscenario.json declares the pattern, the flags, the oracles to capture, and the expected comparison mode for each oracle (exact, semantic, etc.).
Running a single scenario
The repository ships a small set of CLIs for working with individual scenarios. Note that these are dev-time tools, not runtime entrypoints, and they live alongside the runtime binaries in bin/.
# Capture oracle output for a scenario
./bin/oracle scenarios/text/literal-simple
# Run greph against a scenario, capture its output
./bin/actual scenarios/text/literal-simple
# Diff oracle vs actual
./bin/compare scenarios/text/literal-simple
# Full pipeline: oracle -> actual -> compare
./bin/test-scenario scenarios/text/literal-simpleRunning the full regression suite
# All scenarios, sequential
./bin/test-regression
# Parallel execution
./bin/test-regression --jobs 4
# Filter by category
./bin/test-regression --category text
./bin/test-regression --category ast
./bin/test-regression --category rewrite
# Pass/fail only, no reports
./bin/test-regression --fastThe regression suite is the gate for new behavior. A new flag, a new edge case, or a new bug fix requires a scenario that captures the expected behavior against an oracle.
Compliance reporting
./bin/verify-complianceverify-compliance runs the full regression suite and prints a report of which scenarios passed, failed, or were skipped, plus the comparison details for any failures. It is the right command to run when you want to know "is the current branch ready for release".
Test-driven development workflow
When adding a feature:
- Write a scenario directory with the corpus and flags that exercise the new behavior.
- Run
./bin/oracle <scenario>to capture the canonical tool's output. - Implement the feature in
src/. - Run
./bin/test-scenario <scenario>to verify Greph matches the oracle. - Run
./bin/test-regression --fastto make sure nothing else regressed. - Run
composer verifybefore committing.
When fixing a bug:
- Capture the failing input as a scenario.
- Confirm the oracle produces the expected output.
- Confirm Greph does not.
- Fix the bug.
- Confirm Greph now matches the oracle.
- Run
composer verify.
The scenario stays in the corpus permanently, so the bug cannot regress without a CI failure.