Тестирование
Greph проверяется на трех уровнях:
- Модульные тесты изолированных компонентов (парсеров, обходчиков, кодеков, параметров).
- Регрессионный корпус oracle, сравнивающий вывод 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. Корпус oracle (composer test:oracle) не входит в CI, а матрица функций обновляется вручную с помощью php bin/feature-matrix. В наборе PHPUnit есть тест, который создает матрицу, помечая вышестоящие rg и sg как недоступные, и проверяет структуру отчета, поэтому он не падает при сбое одной проверки.
Работа с клоном репозитория
Для всего, что описано на этой странице, нужен клон репозитория. Поскольку пакет Composer содержит только src/ и четыре исполняемых файла для работы, из него исключены 0.2.3, 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 — обязательное условие выпуска. Если проверка не проходит, изменение не готово.
Модель oracle
В Greph используется модель проверки на основе oracle. Для каждого сценария выполняются три команды:
- Oracle:
grep,ripgrepилиast-grep, вывод которых сохранен для корпуса сценариев. - Фактический результат: Greph, вывод которого сохранен для того же корпуса.
- Сравнение: структурированное сопоставление для проверки построчной или семантической эквивалентности.
Если Greph расходится с oracle, значит, Greph работает неверно. Если grep и rg расходятся между собой, сценарий фиксирует расхождение в oracle_disagreement, указывает инструмент, за которым следует Greph в greph_follows, и задает значение skip для сравнения с другим инструментом. В корпусе встречаются оба варианта.
Текстовые сценарии сравниваются с grep и rg, сценарии AST и переписывания — с sg. Вывод oracle сохраняется один раз в каталоге oracle/ каждого сценария и фиксируется в репозитории, поэтому для обычного запуска нужен только PHP; внешние инструменты требуются лишь для получения или обновления вывода oracle.
Реализация находится в 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 задает режим сравнения для каждого oracle (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).
Регрессионный набор служит проверкой для нового поведения. Для нового флага, граничного случая или исправления ошибки нужен сценарий, который фиксирует ожидаемое поведение относительно oracle.
Отчет о соответствии требованиям
./bin/verify-compliance
verify-compliance ничего не запускает. Он читает файлы oracle, фактических результатов и отчетов, оставленные последним запуском сценариев, и выводит, какие сценарии прошли, какие завершились с ошибкой, а для каких отчет еще не создан. Сначала запустите ./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).