Protocolo del ejecutor de conformidad
Define el contrato entre el corpus de conformidad y cualquier implementación de XTL que quiera reclamar conformidad.
Qué es un ejecutor
Un ejecutor de conformidad es un programa pequeño que:
- Itera sobre los fixtures en
conformance/fixtures/ - Invoca la implementación bajo prueba en cada
template.xlsx+data.xlsxdel fixture - Compara la salida de la implementación con el
expected.xlsxdel fixture (o con el directorioexpected/) - Reporta pass / fail / skip por fixture en un formato estándar
Cada implementación provee su propio ejecutor (ya que la invocación es específica del lenguaje), pero todos los ejecutores producen salidas comparables.
Carga de fixtures
Un ejecutor descubre los fixtures enumerando los subdirectorios de conformance/fixtures/. Cada subdirectorio se llama <NNN>-<slug>/ (p. ej., 001-basic-substitution).
Para cada fixture, el ejecutor lee:
template.xlsx— plantilla de entradadata.xlsx— datos fuente de entradaexpected.xlsx(caso de salida única) o directorioexpected/con archivos.xlsx(caso de grupo multi-archivo, incluyendo casos de cero salidas)meta.yaml— metadatos del fixture
Los fixtures estáticos que esperan cero archivos de salida usan un directorio
expected/ vacío.
Los fixtures de error omiten expected.xlsx y expected/. Declaran
expected_error en meta.yaml; el resultado esperado es que la implementación
reporte un error cuyo mensaje contenga el texto declarado.
Los fixtures dinámicos omiten expected.xlsx y expected/. Declaran
expected_dynamic en meta.yaml; el resultado esperado lo calcula el ejecutor
a partir del timestamp de inicio del ejecutor y las reglas de aserción
declaradas. Los fixtures dinámicos están reservados para comportamiento que es
explícitamente dependiente del tiempo en la especificación, como TODAY().
Campos requeridos en meta.yaml
description: string # descripción humana de una línea
spec_section: string # la sección de la especificación que este fixture ejercita
spec_version: string # versión mínima de XTL (p. ej., "0.1")
tags: [string, ...] # tags de filtro (p. ej., [substitution, repeat, aggregate])
tags es una conveniencia del lado del fixture para el flag CLI
--filter=<tag>. Los valores de tag NO son parte del contrato de
conformidad — los ejecutores MUST tratarlos como strings opacas y
SHOULD NOT rechazar un fixture porque su conjunto de tags difiera
del de otro fixture. El corpus de referencia usa tokens en minúscula
separados por guión, pero no impone una taxonomía canónica.
Campos opcionales:
verified_by: [hand | excel-formulas | manual-script | reference-impl]
expected_warnings: [string, ...] # advertencias que la implementación debe emitir
expected_error: string # substring esperado del mensaje de error; no se requiere salida esperada
expected_error_code: string # código de error estable ADR-0015 opcional (p. ej. "xl3/source/undeclared")
expected_dynamic: string # tipo de aserción dinámica; no se requiere salida esperada
comparison_stage: 1 | 2 # etapa de comparación mínima para fixtures de salida est ática; el valor por defecto es 1
skip_reason: string # si el fixture está roto actualmente
inputs: # entradas en tiempo de ejecución provistas por el host (ADR-0010)
- name: region
value: Seoul
El bloque inputs lista pares nombre/valor que el ejecutor pasa a
la implementación como entradas de tiempo de ejecución (según la hoja
_inputs de ADR-0010). Los ejecutores MUST reenviar estos valores
al punto de entrada de conversión de la implementación. Las plantillas
sin hoja _inputs ignoran el campo.
Metadatos de control de etapa:
comparison_stageaplica solo a fixtures de salida estática. Toma por defecto1. Usa2solo cuando el fixture verifique contenido del libro que la etapa 1 no puede observar, como estilos, combinaciones, partes del paquete o medios binarios.- Los fixtures
expected_errory los fixturesexpected_dynamicno usan etapas de comparación del libro para pass/fail. Un ejecutor sigue reportando la etapa de ejecución activa, pero estos fixtures mantienen sus propias reglas de aserción de error o dinámica. expected_dynamicrequieredynamic_cellspara el tipo de aserción actualmente definidoutc_today. Los fixtures de salida estática y de error omitendynamic_cells.
Un ejecutor MUST marcar un fixture expected_error como:
passcuando la implementación reporta un error que contieneexpected_errorfailcuando la implementación tiene éxitofailcuando la implementación reporta un error diferente
expected_error y expected_dynamic son mutuamente exclusivos.
Aserciones dinámicas
Las aserciones dinámicas hacen testeable el comportamiento en tiempo de render
sin comprometer un expected.xlsx obsoleto. Un ejecutor MUST capturar un
único timestamp de inicio del ejecutor antes de ejecutar el primer fixture y
usar ese timestamp para cada fixture dinámico de la ejecución. Esto evita
diferencias por el borde de medianoche entre fixtures dentro del mismo reporte.
XTL 0.1 define un único tipo de aserción dinámica:
expected_dynamic: utc_today
dynamic_cells:
- sheet: Report
cell: A2
format: YYYY-MM-DD
Para utc_today, el valor esperado para cada celda listada es la fecha
calendario UTC del timestamp de inicio del ejecutor, formateada con el formato
de fecha TEXT() de XTL listado. La salida de la implementación MUST
contener el valor string esperado en cada coordenada hoja/celda listada.
Un ejecutor MUST marcar un fixture expected_dynamic como:
passcuando la implementación tiene éxito y cada celda dinámica listada coincidefailcuando la implementación reporta un errorfailcuando cualquier celda dinámica listada difiere del valor esperado calculado
Los ejecutores que no implementen un tipo expected_dynamic declarado MUST
marcar el fixture como skip e incluir una razón. MUST NOT reportarlo como
pasado.
Etapas de comparación
El protocolo de conformidad tiene dos etapas de comparación:
- Etapa 1: comparación de valores de celda. El ejecutor compara nombres de
hoja y valores de celda no auxiliares tras cargar los archivos
.xlsxpor una librería de hojas de cálculo. Esta etapa ignora intencionalmente estilos, combinaciones, page setup, medios embebidos, fórmulas más allá de los valores cacheados y la estructura del paquete. Es suficiente para el corpus de arranque de XTL 0.1 mientras se especifica e implementa la comparación canónica de OOXML. - Etapa 2: comparación canónica de OOXML. El ejecutor compara los archivos
.xlsxgenerados tras canonicalizar sus paquetes OOXML. Esta es la meta para la conformidad completa de salida estática porque puede detectar regresiones de layout, estilo, combinación, estructura de hoja y paquete que la etapa 1 no puede ver.
Los fixtures de error y los fixtures dinámicos no son comparaciones de salida
de libro. Mantienen sus reglas de pass/fail de expected_error y
expected_dynamic independientemente de la etapa de comparación.
Los reportes SHOULD identificar la etapa de comparación usada en cada
ejecución. Una implementación MUST NOT reclamar conformidad de etapa 2 a
partir de una ejecución solo de etapa 1. Los fixtures de salida estática
MAY declarar comparison_stage en meta.yaml. Un ejecutor MUST
saltarse un fixture cuya etapa de comparación declarada sea mayor que la
etapa activa del ejecutor.
Comparación de salida en etapa 2
La comparación se realiza sobre OOXML canonicalizado. Las reglas mínimas de canonicalización:
- Los archivos dentro del zip MUST compararse por contenido, no por metadatos del zip (timestamps, compresión, orden de entradas o nivel de compresión).
- Los nombres de partes del paquete MUST coincidir tras la canonicalización. Partes del libro faltantes o extra son diferencias salvo que un ADR posterior marque la parte como volátil.
- Los archivos XML MUST compararse tras parsear y re-serializar con declaraciones de namespace, orden de atributos, estilo de comillas y representación de elementos vacíos deterministas.
- El orden de elementos XML MUST preservarse salvo que un ADR posterior marque explícitamente una colección de elementos específica como no ordenada. Los archivos de relaciones son datos ordenados del paquete, no conjuntos, hasta que exista tal regla.
- Los siguientes campos se eliminan antes de comparar (reflejan metadatos del generador, no contenido):
cp:lastModifiedBy,dc:creator,dcterms:created,dcterms:modified- Cualquier atributo
calcIdde<calcPr>(versión del motor de cálculo de Excel) - IDs de hoja generados y nombres de archivo de parte de hoja cuando pueden resolverse a través de las relaciones del libro y los nombres de hoja
- Valores de page setup por defecto que ExcelJS puede añadir u omitir (
copies="1",firstPageNumber="1",useFirstPageNumber="1")
- El whitespace insignificante dentro de runs de texto se preserva (puede ser semánticamente significativo).
- Los atributos
r(reference) de celda MUST coincidir exactamente; el orden de celdas dentro de<row>MUST coincidir. - Las partes binarias del paquete, como imágenes, MUST compararse por bytes exactos.
El ejecutor de referencia JS incluye un canonicalizador de etapa 2 para la comparación de conformidad. Su alcance está intencionalmente acotado al OOXML producido por los fixtures XTL soportados más las reglas de normalización arriba; no es una librería de canonicalización XML de propósito general. En particular, no reclama soporte completo de XML C14N, procesamiento de DTD/ entidades, reescritura semántica de namespaces, ni reglas de colección no ordenada específicas de aplicación más allá de las explícitamente listadas aquí. Los fixtures que necesiten reglas adicionales de equivalencia OOXML deberían actualizar primero este protocolo.
Huecos conocidos de canonicalización
Estos casos NO son normalizados por el canonicalizador actual. Se tratan como diferencias si llegan a aparecer. Consulta la enmienda de ADR-0006 para la justificación.
- Equivalencia de atributos por defecto. Un atributo booleano que un
default de OOXML especifica, omitido vs. emitido como el valor por
defecto (p. ej.,
applyFont="0"), se trata como una diferencia. - Mayúsculas/minúsculas en colores hex.
rgb="FF000000"yrgb="ff000000"se comparan como strings diferentes. - Bindings de prefijo de namespace. Diferentes prefijos enlazados al mismo URI de namespace no se unifican.
Cuando un fixture cross-writer expone uno de estos huecos como una diferencia genuinamente volátil (no una diferencia de contenido disfrazada), el protocolo y el canonicalizador de referencia deben extenderse juntos. Las implementaciones MUST NOT relajar estas reglas localmente en silencio.
Convenciones del CLI del ejecutor
Las implementaciones deberían exponer un ejecutor con esta interfaz mínima:
<runner> [--fixture-dir=<path>] [--filter=<tag>] [--spec-version=<x.y>] [--comparison-stage=1|2] [--report=json|text]
Las implementaciones que provean un canonicalizador de etapa 2 SHOULD también exponer un comando de depuración que imprima el contenido canónico del paquete en orden determinista de partes:
<runner> canonicalize <input.xlsx> [--part=<canonical-part-name>]
Cuando se omite --part, el comando SHOULD emitir un objeto JSON indexado
por nombre canónico de parte del paquete. Cuando --part está presente,
SHOULD emitir solo el contenido de esa parte canónica.
Formato del reporte JSON:
{
"implementation": "xl3-js",
"version": "0.1.0-alpha.0",
"spec_version": "0.1",
"comparison_stage": 1,
"results": [
{
"fixture": "001-basic-substitution",
"status": "pass",
"duration_ms": 12
},
{
"fixture": "007-aggregate-sum",
"status": "fail",
"duration_ms": 8,
"diff": "cell B5: expected 1234, got 1234.0"
}
],
"summary": {
"total": 42,
"passed": 40,
"failed": 1,
"skipped": 1
}
}
Reporte de conformidad
Una implementación reporta su nivel de conformidad enlazando a una ejecución pública de conformidad. La forma esperada:
xl3-py 0.2.0 — XTL 0.1 conformance: 38/42 (passes filter, repeat, aggregate; fails image-clone, _config-pattern-match, two date-edge cases)
El IMPLEMENTATIONS.md del repo lista las implementaciones conocidas y sus niveles de conformidad.