Перейти к содержимому

Тестирование с оракулом

Каждое решение о реализации в 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 подтверждает результат полного набора при запуске.