Saltar al contenido

Pruebas con oráculo

Cada decisión de implementación en Phasis depende de una pregunta: ¿V8 produce el mismo resultado?

JavaScript tiene una semántica sutil: NaN !== NaN, -0 === 0, [1] + 1 === "11", el orden exacto de enumeración de propiedades y el momento de las microtareas de Promise. Volver a deducirlo leyendo la especificación es lento y propenso a errores. Phasis lo evita al tratar Node.js (V8) como oráculo: se escribe un caso de prueba JS, se ejecuta con V8 y se guarda el resultado como referencia; luego se ejecuta con Phasis, se vuelve a guardar el resultado y se comparan ambos.

Código
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

Dos niveles de pruebas

Nivel 1 — Casos personalizados

Cada caso de prueba en scenarios/ es un pequeño programa JS con un resultado conocido. Ejecuta un solo caso:

Shell
./bin/test-scenario operators/arithmetic

Ejecútalos todos, una sola categoría o en paralelo:

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

Actualiza el oráculo (e.g, por ejemplo, después de actualizar la versión de Node.js):

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

Los casos están organizados por capítulo de ECMAScript: literales, operadores, variables, flujo de control, funciones, objetos, matrices, clases, funciones integradas, errores, interoperabilidad y casos límite.

Nivel 2 — test262

La suite oficial de conformidad de TC39 está disponible como submódulo de git en test262/. Cada prueba se verifica por sí sola: pasa si no lanza una excepción y falla si la lanza (o lo contrario en las pruebas negative). El arnés carga assert.js y sta.js antes de cada prueba.

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

La instantánea completa se encuentra en COMPAT.md, generada de nuevo por bin/compat-report. En CI, el flujo de trabajo compat-matrix.yml la ejecuta en fragmentos y confirma el resultado; se inicia manualmente, no con cada envío.

Comprobación local antes del envío

./bin/verify-all ejecuta el control de calidad que CI exige:

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

Por sí solo no es suficiente: también hay que muestrear test262 para cualquier cambio que afecte al analizador, al intérprete o a las funciones integradas. En cada solicitud de incorporación de cambios, CI ejecuta PHPStan, PHPCS, la batería de pruebas unitarias y la regresión del oráculo; las baterías de WPT y paquetes populares se ejecutan cuando una solicitud modifica src/ o sus fixtures. La matriz completa de test262 no forma parte de CI para solicitudes de incorporación de cambios, así que pruebe localmente las categorías afectadas.

Por qué funciona

Tratar V8 como fuente de verdad elimina casi toda lectura de especificaciones del ciclo de desarrollo. Cualquier discrepancia con V8 indica un error de Phasis, nunca una ambigüedad de la especificación que se pueda debatir. Cuando se incorpora una corrección, las entradas correspondientes de test262 se convierten en pruebas de regresión permanentes.

La tasa de aciertos del 100 % depende de esta disciplina: cada solicitud de incorporación de cambios se evalúa frente a V8, CI rechaza las regresiones en el oráculo y en las baterías de WPT y paquetes populares, y la matriz de test262 confirma el resultado de la batería completa cuando se ejecuta.