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

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

```bash
./bin/test-scenario operators/arithmetic
```

Запустите их все, одну категорию или несколько параллельно:

```bash
./bin/test-regression
./bin/test-regression --jobs 4
./bin/test-regression --category expressions
```

Обновите эталон (e.g после обновления версии Node.js):

```bash
./bin/oracle --refresh operators/arithmetic
```

Сценарии организованы по главам ECMAScript: литералы, операторы, переменные, поток управления, функции, объекты, массивы, классы, встроенные функции, ошибки, взаимодействие и крайние случаи.

### Уровень 2 — test262

Официальный набор тестов на соответствие спецификации TC39 подключён как подмодуль git в `test262/`. Каждый тест проверяет себя сам: он проходит, если не вызывает исключение, и завершается ошибкой, если вызывает (для тестов `negative` — наоборот). Перед каждым тестом исполнитель загружает `assert.js` и `sta.js`.

```bash
./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`](https://github.com/inline0/phasis/blob/main/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 подтверждает результат полного набора при запуске.
