Тестування
Greph перевіряється за допомогою трьох рівнів тестів:
- Модульні тести для ізольованих компонентів (парсерів, обхідників, кодеків, параметрів).
- Корпус регресійних тестів за еталоном, що порівнює вивід Greph із канонічними бінарними файлами
grep,ripgrepтаast-grepна фіксованому наборі сценаріїв. - Матриця можливостей на основі проб, що підтверджує перевірені можливості оболонок
rgіsg.
CI запускається після кожного надсилання змін до main і кожного запиту на злиття (.github/workflows/ci.yml). Він перевіряє стандарти кодування (composer cs), статичний аналіз (composer analyse), набори модульних та інтеграційних тестів PHPUnit (composer test) і composer audit на PHP 8.4. Корпус еталонних тестів (composer test:oracle) не входить до CI, а матрицю можливостей генерують вручну за допомогою php bin/feature-matrix. Набір PHPUnit містить тест, який генерує матрицю з позначеними як недоступні upstream-інструментами rg і sg та перевіряє структуру звіту, тому регресія однієї проби не призводить до помилки.
Робота з клоном
Для всього на цій сторінці потрібен клон репозиторію. Оскільки 0.2.3 пакет Composer містить лише src/ і чотири виконувані файли середовища виконання, tests/, scenarios/, benchmarks/ та скрипти розробки в bin/ до нього не включено.
git clone https://github.com/inline0/greph
cd greph
composer install
Запуск тестів
# 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 — обов'язкова умова для будь-якого випуску. Якщо він не проходить, зміна не готова.
Модель еталонної перевірки
Greph використовує модель перевірки за еталоном. Для кожного сценарію виконуються три команди:
- Еталон:
grep,ripgrepабоast-grep, записані для корпусу сценаріїв. - Фактичний результат: Greph, записаний для того самого корпусу.
- Засіб порівняння: структуроване порівняння, що перевіряє відповідність рядок за рядком або семантичну еквівалентність.
Якщо Greph не збігається з еталоном, помилковий Greph. Якщо grep і rg не збігаються, сценарій фіксує розбіжність у oracle_disagreement, зазначає інструмент, якого дотримується Greph у greph_follows, і встановлює для порівняння з іншим інструментом статус skip. У корпусі є сценарії для обох випадків.
Текстові сценарії порівнюються з grep і rg, а сценарії AST і переписування — з sg. Вивід еталона записують один раз і зберігають у каталозі oracle/ кожного сценарію, тому для звичайного запуску потрібен лише PHP; зовнішні інструменти потрібні тільки для запису або оновлення еталонного виводу.
Реалізація розташована в tests/Oracle/, а сценарії — в scenarios/text/, scenarios/ast/, scenarios/rewrite/ та scenarios/edge/.
Структура сценаріїв
Кожен сценарій — це самодостатній каталог:
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 задаються шаблон, прапорці та шлях пошуку. Необов'язковий об'єкт expectations налаштовує режим порівняння для кожного еталона (exact, semantic або skip); якщо його немає, у текстових сценаріях grep і rg порівнюються точно, а rg JSON — семантично; сценарії AST і переписування порівнюються з sg.
Запуск одного сценарію
У репозиторії є невеликий набір CLI-інструментів для роботи з окремими сценаріями. Це засоби розробки, а не точки входу під час виконання: вони розташовані поруч із виконуваними файлами в bin/, але не входять до пакета Composer. Кожен із них приймає назву сценарію відносно scenarios/.
# 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
Запуск повного набору регресійних тестів
# 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
Сценарії завжди запускаються по одному. Параметр --jobs N приймається та відображається у підсумковому рядку, але нічого не запускає паралельно (#7). У щойно клонованому репозиторії text/no-ignore і text/explicit-file-input завершуються помилкою, бо файли тестових даних у setup/vendor/ не зафіксовані в репозиторії (#6).
Набір регресійних тестів є умовою для додавання нової поведінки. Для нового прапорця, граничного випадку або виправлення помилки потрібен сценарій, що фіксує очікувану поведінку за еталоном.
Звітування про відповідність
./bin/verify-compliance
verify-compliance самостійно нічого не запускає. Команда читає файли oracle, actual і report, залишені останнім запуском сценаріїв, і виводить, які сценарії пройшли, які завершилися помилкою, а для яких звіт ще відсутній. Спочатку запустіть ./bin/test-regression. --output report.json також записує звіт у JSON. Це саме та команда, яку слід запустити, щоб дізнатися: «чи готова поточна гілка до випуску».
Процес розробки через тестування
Під час додавання функції:
- Створіть каталог сценарію з корпусом і прапорцями, які перевіряють нову поведінку.
- Запустіть
./bin/oracle <scenario>, щоб зафіксувати вивід канонічного інструмента. - Реалізуйте функцію в
src/. - Запустіть
./bin/test-scenario <scenario>, щоб перевірити, чи відповідає Greph oracle. - Запустіть
./bin/test-regression --fast, щоб переконатися, що нічого іншого не зламалося. - Запустіть
composer verifyперед комітом.
Під час виправлення помилки:
- Збережіть вхідні дані, що спричиняють помилку, як сценарій.
- Переконайтеся, що oracle видає очікуваний результат.
- Переконайтеся, що Greph цього не робить.
- Виправте помилку.
- Переконайтеся, що Greph тепер відповідає oracle.
- Запустіть
composer verify.
Сценарій назавжди залишається в корпусі, тож помилка не повториться без збою composer test:oracle (і composer verify).