Hoppa till innehåll

Oracle-testning

Varje implementeringsbeslut i Phasis avgörs av en fråga: ger V8 samma resultat?

JavaScript har subtil semantik — NaN !== NaN, -0 === 0, [1] + 1 === "11", den exakta ordningen för uppräkning av egenskaper och tidpunkten för Promise-mikrotasks. Att härleda detta på nytt genom att läsa specifikationen är långsamt och felbenäget. Phasis undviker det genom att behandla Node.js (V8) som ett oracle: skriv ett JS-scenario, kör det genom V8 och spara resultatet som facit; kör det genom Phasis, spara resultatet igen och jämför.

Kod
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

Två testnivåer

Nivå 1 — Anpassade scenarier

Varje scenario i scenarios/ är ett litet JS-program med känt resultat. Kör ett enskilt scenario:

Shell
./bin/test-scenario operators/arithmetic

Kör alla, en enda kategori eller parallellt:

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

Uppdatera oracle (e.g. efter en versionsuppdatering av Node.js):

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

Scenarierna är organiserade efter ECMAScript-kapitel: literaler, operatorer, variabler, kontrollflöde, funktioner, objekt, arrayer, klasser, inbyggda funktioner, fel, interoperabilitet och specialfall.

Nivå 2 — test262

Den officiella TC39-konformitetssviten är utcheckad som en git-submodul under test262/. Varje test verifierar sig självt: det godkänns om det inte kastar ett undantag och misslyckas om det gör det (eller tvärtom för negative-tester). Testriggen laddar assert.js och sta.js före varje 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

Ögonblicksbilden av hela sviten finns i COMPAT.md och återskapas av bin/compat-report. I CI kör compat-matrix.yml-arbetsflödet den i delar och checkar in resultatet. Arbetsflödet startas manuellt, inte vid varje push.

Lokal kontroll före push

./bin/verify-all kör kvalitetskontrollen som CI tillämpar:

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

Det räcker inte på egen hand — test262 måste också testas för alla ändringar som berör parsern, tolken eller inbyggda funktioner. Vid varje pull request kör CI PHPStan, PHPCS, unit-sviten och oracle-regressionstestet. WPT- och popular-paketsviterna körs när en pull request ändrar src/ eller deras fixtures. Den fullständiga test262-matrisen ingår inte i CI för pull requests, så testa berörda kategorier lokalt.

Varför det här fungerar

När V8 behandlas som facit behöver utvecklingsflödet nästan inte alls läsa specifikationen. Om resultatet skiljer sig från V8 finns alltid ett Phasis-fel — det är aldrig en tvetydighet i specifikationen att diskutera. När en korrigering har landat blir motsvarande test262-poster permanenta regressionstester.

100 % godkända tester är resultatet av den här disciplinen: varje PR bedöms mot V8, CI avvisar regressioner i oracle-, WPT- och popular-paketsviterna, och test262-matrisen bekräftar resultatet för hela sviten när den körs.