Тестирование с оракулом
Каждое решение о реализации в Phasis зависит от одного вопроса: выдаёт ли V8 тот же результат?
Семантика JavaScript сложна: NaN !== NaN, -0 === 0, [1] + 1 === "11", точный порядок перечисления свойств и время выполнения микрозадач Promise. Повторное выведение этих правил по спецификации занимает много времени и чревато ошибками. Phasis позволяет обойти эту проблему: он использует Node.js (V8) как оракул. Создайте сценарий JS, запустите его через V8 и сохраните результат как эталон; затем запустите через Phasis, снова сохраните результат и сравните.
1. SETUP → a JavaScript source file or snippet
2. ORACLE → Node.js executes it, output captured as truth
3. ACTUAL → Phasis executes it, output captured
4. COMPARE → oracle vs actual, diff measures the gap
Два уровня тестирования
Уровень 1 — собственные сценарии
Каждый сценарий в scenarios/ — небольшая программа JS с известным результатом. Запуск одного сценария:
./bin/test-scenario operators/arithmetic
Запустите их все, одну категорию или несколько параллельно:
./bin/test-regression
./bin/test-regression --jobs 4
./bin/test-regression --category expressions
Обновите эталон (e.g после обновления версии Node.js):
./bin/oracle --refresh operators/arithmetic
Сценарии организованы по главам ECMAScript: литералы, операторы, переменные, поток управления, функции, объекты, массивы, классы, встроенные функции, ошибки, взаимодействие и крайние случаи.
Уровень 2 — test262
Официальный набор тестов на соответствие спецификации TC39 подключён как подмодуль git в test262/. Каждый тест проверяет себя сам: он проходит, если не вызывает исключение, и завершается ошибкой, если вызывает (для тестов negative — наоборот). Перед каждым тестом исполнитель загружает assert.js и sta.js.
./bin/test262 # full suite
./bin/test262 --category built-ins/Array # subset
./bin/test262 --jobs 4 # parallel
./bin/test262 --failures # one line per failing test
./bin/compat-report # full COMPAT.md + compat.json
Снимок полного набора находится в COMPAT.md и пересоздаётся с помощью bin/compat-report. В CI рабочий процесс compat-matrix.yml запускает его частями и фиксирует результат; он запускается вручную, а не при каждом push.
Локальная проверка перед отправкой изменений
./bin/verify-all запускает проверки качества, обязательные для CI:
=== PHPStan === level 6, zero errors
=== Code Standards === PHPCS, zero warnings
=== PHPUnit === unit suite passes
=== Oracle Regression === scenarios under `scenarios/` pass
Одного этого недостаточно: при изменениях парсера, интерпретатора или встроенных функций нужно также проверять отдельные тесты test262. Для каждого запроса на слияние CI запускает PHPStan, PHPCS, модульный набор тестов и регрессионную проверку оракула. Наборы WPT и popular запускаются, когда запрос на слияние затрагивает src/ или их тестовые примеры. Полная матрица test262 не входит в CI для запросов на слияние, поэтому локально проверяйте затронутые категории.
Почему это работает
Если считать V8 источником истины, в процессе разработки почти не нужно обращаться к спецификации. Любое расхождение с V8 означает ошибку в Phasis, а не спорную трактовку спецификации. После исправления соответствующие записи test262 становятся постоянными регрессионными тестами.
Доля успешных тестов 100 % — результат такого подхода: каждый PR сверяется с V8, CI отклоняет регрессии в наборах oracle, WPT и popular, а матрица test262 подтверждает результат полного набора при запуске.