Preskoči na vsebino

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.

Koda
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:

Lupina
./bin/test-scenario operators/arithmetic

Zaženite vse scenarije, samo eno kategorijo ali pa jih zaženite vzporedno:

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

Osvežite oracle (e.g po posodobitvi različice Node.js):

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

Lupina
./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:

Koda
=== 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.