Zum Inhalt springen

Testen

Greph wird mit drei Testebenen validiert:

  1. Unit-Tests für isolierte Komponenten (Parser, Walker, Codecs, Optionen).
  2. Oracle-Regressionskorpus, das die Ausgabe von Greph mit den kanonischen Binärdateien grep, ripgrep und ast-grep anhand eines festen Szenariosatzes vergleicht.
  3. Probe-gesteuerte Funktionsmatrix, die die verifizierte Oberfläche der Wrapper rg und sg prüft.

CI läuft bei jedem Push nach main und bei jedem Pull Request (.github/workflows/ci.yml). Dabei werden Codierungsstandards (composer cs), statische Analyse (composer analyse), die PHPUnit-Unit- und Integrationssuiten (composer test) sowie composer audit unter PHP 8.4 ausgeführt. Das Oracle-Korpus (composer test:oracle) ist nicht Teil von CI, und die Funktionsmatrix wird manuell mit php bin/feature-matrix neu erstellt. Die PHPUnit-Suite enthält einen Test, der die Matrix mit dem vorgelagerten rg generiert, wobei sg als nicht verfügbar markiert ist, und die Struktur des Berichts überprüft. So schlägt der Test nicht fehl, wenn eine einzelne Prüfung regressiert.

Mit einem Klon arbeiten

Für alles auf dieser Seite brauchst du einen Klon des Repositorys. Seit Version 0.2.3 enthält das Composer-Paket nur src/ und die vier Laufzeitprogramme; tests/, scenarios/, benchmarks/ und die Entwicklungsskripte in bin/ sind nicht enthalten.

Shell
git clone https://github.com/inline0/greph
cd greph
composer install

Tests ausführen

Shell
# PHPUnit unit and integration suites
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 verify

composer verify ist die Mindestanforderung für jede Veröffentlichung. Wenn sie nicht besteht, ist die Änderung nicht abgeschlossen.

Das Oracle-Modell

Greph folgt einem Oracle-gesteuerten Verifikationsmodell. Für jedes Szenario werden drei Befehle ausgeführt:

  1. Oracle: grep, ripgrep oder ast-grep, ausgeführt anhand des Szenariokorpus.
  2. Tatsächliche Ausgabe: Greph, ausgeführt anhand desselben Korpus.
  3. Vergleich: ein strukturierter Diff, der die zeilenweise oder semantische Gleichwertigkeit prüft.

Wenn Greph vom Oracle abweicht, ist Greph falsch. Wenn grep und rg voneinander abweichen, hält das Szenario die Abweichung unter oracle_disagreement fest, nennt das Werkzeug, dem Greph in greph_follows folgt, und setzt den Vergleich mit dem anderen Werkzeug auf skip. Im Korpus kommen beide Richtungen vor.

Text-Szenarien werden mit grep und rg verglichen, AST- und Rewrite-Szenarien mit sg. Die Oracle-Ausgabe wird einmal erfasst und im Verzeichnis oracle/ jedes Szenarios eingecheckt. Für einen normalen Durchlauf brauchst du daher nur PHP; die externen Werkzeuge werden nur zum Erfassen oder Aktualisieren der Oracle-Ausgabe benötigt.

Die Implementierung liegt unter tests/Oracle/, die Szenarien unter scenarios/text/, scenarios/ast/, scenarios/rewrite/ und scenarios/edge/.

Szenarioaufbau

Jedes Szenario ist ein eigenständiges Verzeichnis:

Code
scenarios/text/literal-simple/
├── scenario.json     # name, category, pattern, flags, path
├── setup/            # corpus to search through
├── oracle/           # captured oracle output (grep.txt, rg.txt, rg.json, ...)
├── actual/           # captured greph output (generated, git-ignored)
└── reports/          # comparison results (generated, git-ignored)

scenario.json definiert das Muster, die Flags und den Suchpfad. Ein optionales Objekt expectations legt den Vergleichsmodus für jedes Oracle fest (exact, semantic oder skip). Ohne dieses Objekt werden Textszenarien mit grep und rg exakt und das rg JSON semantisch verglichen; AST- und Rewrite-Szenarien werden mit sg verglichen.

Ein einzelnes Szenario ausführen

Das Repository enthält einige CLIs, um mit einzelnen Szenarien zu arbeiten. Sie sind Entwicklungswerkzeuge und keine Laufzeiteinstiegspunkte: Sie liegen neben den Laufzeit-Binärdateien in bin/, gehören aber nicht zum Composer-Paket. Jedes Tool erwartet den Szenarionamen relativ zu scenarios/.

Shell
# Capture oracle output for a scenario (needs grep and rg, or sg)
./bin/oracle text/literal-simple

# Run greph against a scenario, capture its output
./bin/actual text/literal-simple

# Diff oracle vs actual
./bin/compare text/literal-simple

# Full pipeline: oracle (only if missing) -> actual -> compare
./bin/test-scenario text/literal-simple

# Recapture the oracle output first
./bin/test-scenario text/literal-simple --refresh

Die gesamte Regressionssuite ausführen

Shell
# All scenarios (the same as composer test:oracle)
./bin/test-regression

# Filter by category
./bin/test-regression --category text
./bin/test-regression --category ast
./bin/test-regression --category rewrite
./bin/test-regression --category edge

# Print only failures and the summary
./bin/test-regression --fast

# Recapture every oracle before comparing
./bin/test-regression --refresh

Szenarien werden immer nacheinander ausgeführt. --jobs N wird akzeptiert und in der Zusammenfassungszeile ausgegeben, führt aber nichts parallel aus (#7). In einem frisch geklonten Repository schlagen text/no-ignore und text/explicit-file-input fehl, weil ihre Fixture-Dateien unter setup/vendor/ nicht eingecheckt sind (#6).

Die Regressionssuite ist die Freigabeinstanz für neues Verhalten. Für ein neues Flag, einen neuen Sonderfall oder eine Fehlerbehebung brauchst du ein Szenario, das das erwartete Verhalten anhand eines Oracle festhält.

Compliance-Berichte

Shell
./bin/verify-compliance

verify-compliance führt selbst nichts aus. Es liest die Oracle-, Ist- und Berichtsdateien, die der letzte Szenariolauf hinterlassen hat, und gibt aus, welche Szenarien bestanden haben, welche fehlgeschlagen sind und für welche noch kein Bericht vorliegt. Führe zuerst ./bin/test-regression aus. --output report.json schreibt den Bericht außerdem als JSON. Dieser Befehl eignet sich, wenn du wissen möchtest: „Ist der aktuelle Branch veröffentlichungsbereit?“

Testgetriebener Entwicklungsablauf

Beim Hinzufügen einer Funktion:

  1. Erstelle ein Szenarioverzeichnis mit Korpus und Flags, das das neue Verhalten abdeckt.
  2. Führe ./bin/oracle <scenario> aus, um die Ausgabe des kanonischen Werkzeugs zu erfassen.
  3. Implementiere die Funktion in src/.
  4. Führe ./bin/test-scenario <scenario> aus, um zu prüfen, ob Greph mit dem Oracle übereinstimmt.
  5. Führe ./bin/test-regression --fast aus, um sicherzustellen, dass nichts anderes regressiert ist.
  6. Führe composer verify vor dem Commit aus.

Beim Beheben eines Fehlers:

  1. Erfasse die fehlerhafte Eingabe als Szenario.
  2. Prüfe, ob das Oracle die erwartete Ausgabe erzeugt.
  3. Prüfe, ob Greph das nicht tut.
  4. Behebe den Fehler.
  5. Prüfe, ob Greph jetzt mit dem Oracle übereinstimmt.
  6. Führe composer verify aus.

Das Szenario bleibt dauerhaft im Korpus, sodass der Fehler nicht wieder auftreten kann, ohne dass composer test:oracle (und composer verify) fehlschlägt.