Testowanie z użyciem wzorca referencyjnego
Każda decyzja implementacyjna w Phasis zależy od odpowiedzi na jedno pytanie: czy V8 generuje takie same wyniki?
JavaScript ma subtelną semantykę — NaN !== NaN, -0 === 0, [1] + 1 === "11", dokładną kolejność wyliczania właściwości i moment wykonywania mikrozadań Promise. Odtwarzanie jej na podstawie specyfikacji jest powolne i podatne na błędy. Phasis upraszcza ten proces, traktując Node.js (V8) jako wzorzec referencyjny: przygotuj scenariusz JS, uruchom go w V8 i zapisz wynik jako prawidłowy; następnie uruchom go w Phasis, ponownie zapisz wynik i porównaj oba.
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
Dwa poziomy testowania
Poziom 1 — Własne scenariusze
Każdy scenariusz w scenarios/ to mały program JS o znanym wyniku. Uruchom pojedynczy scenariusz:
./bin/test-scenario operators/arithmetic
Uruchom wszystkie scenariusze, wybraną kategorię albo wiele równolegle:
./bin/test-regression
./bin/test-regression --jobs 4
./bin/test-regression --category expressions
Odśwież wzorzec referencyjny (e.g, na przykład po aktualizacji wersji Node.js):
./bin/oracle --refresh operators/arithmetic
Scenariusze są uporządkowane według rozdziałów ECMAScript: literały, operatory, zmienne, przepływ sterowania, funkcje, obiekty, tablice, klasy, elementy wbudowane, błędy, współdziałanie i przypadki brzegowe.
Poziom 2 — test262
Oficjalny zestaw testów zgodności TC39 jest pobierany jako podmoduł git w test262/. Każdy test weryfikuje się samodzielnie: zalicza się, jeśli nie zgłosi wyjątku, a nie zalicza, jeśli go zgłosi (lub odwrotnie w przypadku testów negative). Przed każdym testem środowisko testowe ładuje assert.js i 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
Pełny zrzut zestawu znajduje się w COMPAT.md; jest odtwarzany przez bin/compat-report. W CI workflow compat-matrix.yml uruchamia go w shardach i zatwierdza wynik; workflow jest uruchamiany ręcznie, a nie przy każdym pushu.
Lokalna kontrola przed pushowaniem
./bin/verify-all uruchamia kontrolę jakości wymaganą przez CI:
=== PHPStan === level 6, zero errors
=== Code Standards === PHPCS, zero warnings
=== PHPUnit === unit suite passes
=== Oracle Regression === scenarios under `scenarios/` pass
Samo w sobie nie wystarcza — test262 trzeba również próbkować przy każdej zmianie parsera, interpretera lub funkcji wbudowanych. Przy każdym pull requeście CI uruchamia PHPStan, PHPCS, zestaw testów jednostkowych i testy regresji oracle; zestawy WPT i popularnych pakietów są uruchamiane, gdy pull request zmienia src/ lub ich fixtures. Pełna macierz test262 nie jest częścią CI dla pull requestów, więc lokalnie przetestuj zmienione kategorie.
Dlaczego to działa
Traktowanie V8 jako źródła prawdy eliminuje niemal całe czytanie specyfikacji z procesu tworzenia. Rozbieżność z V8 zawsze wskazuje na błąd Phasis — nigdy na niejasność specyfikacji, o którą można się spierać. Po wdrożeniu poprawki odpowiadające jej wpisy test262 stają się stałymi testami regresji.
Wskaźnik zaliczenia 100% jest wynikiem tej dyscypliny: każdy PR jest oceniany względem V8, CI odrzuca regresje w zestawach oracle, WPT i popularnych pakietów, a macierz test262 potwierdza wynik pełnego zestawu, gdy zostaje uruchomiona.