Oracletests
Elke implementatiebeslissing in Phasis hangt af van één vraag: levert V8 dezelfde uitvoer op?
JavaScript heeft subtiele semantiek: NaN !== NaN, -0 === 0, [1] + 1 === "11", de exacte volgorde waarin properties worden opgesomd en het moment waarop Promise-microtaken worden uitgevoerd. Deze opnieuw afleiden door de specificatie te lezen is traag en foutgevoelig. Phasis omzeilt dat door Node.js (V8) als oracle te behandelen: schrijf een JS-scenario, voer het uit via V8 en leg de uitvoer vast als waarheid; voer het daarna uit via Phasis, leg de uitvoer opnieuw vast en vergelijk.
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
Twee testniveaus
Niveau 1 — aangepaste scenario's
Elk scenario in scenarios/ is een klein JS-programma met bekende uitvoer. Voer één scenario uit:
./bin/test-scenario operators/arithmetic
Voer ze allemaal, slechts één categorie of ze parallel uit:
./bin/test-regression
./bin/test-regression --jobs 4
./bin/test-regression --category expressions
Vernieuw de oracle (e.g, bijvoorbeeld na een versie-update van Node.js):
./bin/oracle --refresh operators/arithmetic
Scenario's zijn ingedeeld volgens de hoofdstukken van ECMAScript: literals, operators, variabelen, besturingsstroom, functies, objecten, arrays, classes, ingebouwde functies, fouten, interoperabiliteit en randgevallen.
Niveau 2 — test262
De officiële conformiteitssuite van TC39 is uitgecheckt als git-submodule onder test262/. Elke test controleert zichzelf: hij slaagt als er geen fout wordt gegenereerd en faalt als dat wel gebeurt (of andersom voor negative-tests). De harness laadt assert.js en sta.js vóór elke test.
./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
De snapshot van de volledige suite staat in COMPAT.md en wordt opnieuw gegenereerd door bin/compat-report. In CI voert de workflow van compat-matrix.yml deze in shards uit en legt het resultaat vast; die workflow wordt handmatig gestart, niet bij elke push.
Lokale controle vóór het pushen
./bin/verify-all voert de kwaliteitspoort uit die CI afdwingt:
=== PHPStan === level 6, zero errors
=== Code Standards === PHPCS, zero warnings
=== PHPUnit === unit suite passes
=== Oracle Regression === scenarios under `scenarios/` pass
Dat is op zichzelf niet voldoende: test262 moet ook worden getest op wijzigingen in de parser, interpreter of ingebouwde functies. Bij elke pull request voert CI PHPStan, PHPCS, de unit-suite en de oracle-regressietest uit. De WPT- en popular-package-suites worden uitgevoerd wanneer een pull request src/ of de bijbehorende fixtures wijzigt. De volledige matrix van test262 maakt geen deel uit van pull-request-CI; test de geraakte categorieën daarom lokaal.
Waarom dit werkt
Door V8 als bron van waarheid te behandelen, hoeft tijdens het ontwikkelproces bijna nooit meer de specificatie te worden gelezen. Verschillen met V8 duiden altijd op een Phasis-bug, nooit op een onduidelijkheid in de specificatie waarover gediscussieerd kan worden. Zodra een oplossing is toegevoegd, worden de bijbehorende test262-vermeldingen permanente regressietests.
Het passpercentage van 100 % is het resultaat van deze discipline: elke PR wordt beoordeeld aan de hand van V8, CI wijst regressies af in de oracle-, WPT- en popular-package-suites, en de matrix van test262 bevestigt het resultaat van de volledige suite wanneer die wordt uitgevoerd.