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

Всяко решение за имплементация в 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` изпълнява тестовете на части и записва резултата; стартира се ръчно, а не при всяко изпращане.

## Локална проверка преди изпращане

`./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, набора от unit тестове и регресионните тестове на оракула; WPT и наборите за популярни пакети се изпълняват, когато заявката за сливане променя `src/` или съответните им fixture файлове. Пълната матрица test262 не е част от CI за заявките за сливане, затова локално изпълнявайте засегнатите категории на извадка.

## Защо това работи

Когато V8 се приема за източник на истината, почти отпада нуждата от четене на спецификацията по време на разработката. Несъгласието с V8 винаги показва грешка в Phasis — никога спорна неяснота в спецификацията. След като поправката бъде включена, съответните тестове от test262 стават постоянни регресионни тестове.

Процентът на успешно преминалите тестове 100 % е резултат от тази дисциплина: всяка заявка за сливане се оценява спрямо V8, CI отхвърля регресии в наборите oracle, WPT и popular-package, а матрицата test262 потвърждава резултата от целия набор, когато бъде изпълнена.
