Testiranje oracle
Vsaka odločitev pri implementaciji v Phasis temelji na enem vprašanju: ali V8 ustvari enak rezultat?
JavaScript ima subtilno semantiko — NaN !== NaN, -0 === 0, [1] + 1 === "11", natančen vrstni red naštevanja lastnosti in čas izvajanja mikroopravil Promise. Ponovno izpeljevanje teh podrobnosti z branjem specifikacije je počasno in nagnjeno k napakam. Phasis to poenostavi tako, da Node.js (V8) obravnava kot oracle: napišite scenarij JS, ga zaženite prek V8 in zajemite rezultat kot resnico; nato ga zaženite prek Phasis, znova zajemite rezultat in primerjajte razlike.
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
Dve ravni testiranja
Raven 1 — Scenariji po meri
Vsak scenarij v scenarios/ je majhen program JS z znanim rezultatom. Zaženite en sam scenarij:
./bin/test-scenario operators/arithmetic
Zaženite vse scenarije, samo eno kategorijo ali pa jih zaženite vzporedno:
./bin/test-regression
./bin/test-regression --jobs 4
./bin/test-regression --category expressions
Osvežite oracle (e.g po posodobitvi različice Node.js):
./bin/oracle --refresh operators/arithmetic
Scenariji so razvrščeni po poglavjih ECMAScripta: literali, operatorji, spremenljivke, nadzorni tok, funkcije, objekti, polja, razredi, vgrajene funkcije, napake, interoperabilnost in robni primeri.
Raven 2 — test262
Uradna zbirka testov skladnosti TC39 je vključena kot podmodul git v test262/. Vsak test preveri samega sebe: uspe, če ne sproži izjeme, in ne uspe, če jo (pri testih negative velja obratno). Pred vsakim testom testno okolje naloži assert.js in 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
Posnetek celotne zbirke je shranjen v COMPAT.md, ponovno pa ga ustvari bin/compat-report. V CI ga potek dela compat-matrix.yml izvede v delih in potrdi rezultat; ta potek dela se zažene ročno, ne ob vsakem potisku kode.
Lokalni pregled pred pošiljanjem
./bin/verify-all zažene preverjanja kakovosti, ki jih uveljavlja CI:
=== PHPStan === level 6, zero errors
=== Code Standards === PHPCS, zero warnings
=== PHPUnit === unit suite passes
=== Oracle Regression === scenarios under `scenarios/` pass
Sam po sebi ne zadostuje — pri vsaki spremembi razčlenjevalnika, tolmača ali vgrajenih funkcij je treba vzorčno preizkusiti tudi test262. Ob vsakem zahtevku za združitev CI zažene PHPStan, PHPCS, zbirko testov enot in regresijski test oracle; zbirki WPT in popular se zaženeta, ko zahtevek spremeni src/ ali njune testne primere. Celotna matrika test262 ni del CI za zahtevke za združitev, zato lokalno vzorčno preizkusite prizadete kategorije.
Zakaj to deluje
Če V8 obravnavamo kot vir resnice, v razvojnem procesu skoraj ni več treba brati specifikacije. Vsako odstopanje od V8 kaže na napako v Phasis — nikoli na dvoumnost specifikacije, o kateri bi se bilo mogoče prepirati. Ko je popravek vključen, ustrezni vnosi test262 postanejo trajni regresijski testi.
Stopnja uspešnosti 100 % je posledica te doslednosti: vsak zahtevek za združitev se preveri glede na V8, CI zavrne regresije v zbirkah oracle, WPT in popular, matrika test262 pa ob zagonu potrdi rezultat celotne zbirke.