Перейти до вмісту

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

Кожне рішення щодо реалізації в 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 запускає тести частинами й фіксує результат; його запускають вручну, а не під час кожного надсилання змін.

Локальна перевірка перед відправленням змін

./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 також потрібно вибірково запускати для будь-яких змін у парсері, інтерпретаторі чи вбудованих функціях. Для кожного pull request CI запускає PHPStan, PHPCS, модульний набір тестів і регресійний тест оракула; набори WPT і popular запускаються, якщо pull request змінює src/ або відповідні тестові приклади. Повна матриця test262 не входить до CI для pull request, тож локально вибірково запускайте тести для відповідних категорій.

Чому це працює

Якщо вважати V8 еталоном, під час розробки майже не доводиться читати специфікацію. Будь-яка розбіжність із V8 означає помилку Phasis, а не неоднозначність специфікації, яку можна оскаржувати. Після внесення виправлення відповідні записи test262 стають постійними регресійними тестами.

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