Treci la conținut

Testarea oracle

Fiecare decizie de implementare din Phasis depinde de un singur răspuns: produce V8 același rezultat?

JavaScript are semantică subtilă — NaN !== NaN, -0 === 0, [1] + 1 === "11", ordinea exactă de enumerare a proprietăților și momentul executării microtask-urilor Promise. Rededucerea acestor detalii prin citirea specificației este lentă și predispusă la erori. Phasis evită această problemă folosind Node.js (V8) drept oracle: scrieți un scenariu JS, rulați-l prin V8 și înregistrați rezultatul drept adevăr; rulați-l prin Phasis, înregistrați din nou rezultatul și comparați-le.

Cod
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

Două niveluri de testare

Nivelul 1 — Scenarii personalizate

Fiecare scenariu din scenarios/ este un mic program JS cu rezultat cunoscut. Rulați un singur scenariu:

Shell
./bin/test-scenario operators/arithmetic

Rulați-le pe toate, doar o categorie sau în paralel:

Shell
./bin/test-regression
./bin/test-regression --jobs 4
./bin/test-regression --category expressions

Reîmprospătați oracle-ul (e.g după o actualizare a versiunii Node.js):

Shell
./bin/oracle --refresh operators/arithmetic

Scenariile sunt organizate după capitolele ECMAScript: literali, operatori, variabile, flux de control, funcții, obiecte, tablouri, clase, funcționalități încorporate, erori, interoperabilitate și cazuri-limită.

Nivelul 2 — test262

Suita oficială de conformitate TC39 este preluată ca submodul git în test262/. Fiecare test își verifică singur rezultatul: trece dacă nu aruncă o excepție și eșuează dacă o aruncă (sau invers, pentru testele negative). Harness-ul încarcă assert.js și sta.js înaintea fiecărui test.

Shell
./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

Instantaneul suitei complete se află în COMPAT.md și este regenerat de bin/compat-report. În CI, fluxul de lucru compat-matrix.yml îl rulează în fragmente și înregistrează rezultatul; fluxul este pornit manual, nu la fiecare trimitere.

Verificare locală înainte de trimitere

./bin/verify-all rulează verificările de calitate impuse de CI:

Cod
=== PHPStan ===                  level 6, zero errors
=== Code Standards ===           PHPCS, zero warnings
=== PHPUnit ===                  unit suite passes
=== Oracle Regression ===        scenarios under `scenarios/` pass

Acest lucru nu este suficient de unul singur — trebuie testat și test262 pentru orice modificare care atinge parserul, interpretorul sau funcționalitățile încorporate. La fiecare pull request, CI rulează PHPStan, PHPCS, suita unitară și testul de regresie oracle; suitele WPT și popular rulează când pull request-ul modifică src/ sau fixture-urile acestora. Matricea completă test262 nu face parte din CI pentru pull request-uri, așa că testați local categoriile afectate.

De ce funcționează

Folosirea V8 ca sursă de adevăr elimină aproape complet citirea specificației din ciclul de dezvoltare. Orice dezacord cu V8 indică întotdeauna o eroare Phasis — niciodată o ambiguitate a specificației care poate fi dezbătută. După ce o remediere este integrată, intrările test262 corespunzătoare devin teste de regresie permanente.

Rata de promovare de 100% este rezultatul acestei discipline: fiecare PR este evaluat în raport cu V8, CI respinge regresiile din oracle și suitele WPT și popular, iar matricea test262 confirmă rezultatul suitei complete când este rulată.