Load an existing committed fixture through python-xlsx, save it to a temp path, then re-assert the primary structural markers survive. Covers eight fixtures spanning cell data, formulas, defined names, multi-sheet workbooks, merges, panes, number formatting and font styling.
| Library | Pass | Fail | Pending |
|---|---|---|---|
python-xlsx | 0 | 0 | 8 |
xlsxjs | 0 | 0 | 8 |
case (8 values): cell-formula, merged-cell, defined-name, multiple-sheets, freeze-panes, cell-number, cell-date, cell-boldscripts/gen_round_trip_identity_xlsx.py — runs with --arg_template --source fixtures/{case.source}.xlsx --out fixtures/xlsx/round-trip-identity--{case.id}.xlsx
#!/usr/bin/env python3
"""Generate an xlsx round-trip fixture by loading a source via python-xlsx
and saving the result to the named output path.
Usage::
python scripts/gen_round_trip_identity_xlsx.py \
--source fixtures/xlsx/cell-formula.xlsx \
--out fixtures/xlsx/round-trip-identity--cell-formula.xlsx
"""
from __future__ import annotations
import argparse
import zipfile
from pathlib import Path
import xlsx # type: ignore[import-not-found]
def _rezip_with_epoch_timestamps(path: Path) -> None:
tmp = path.with_suffix(path.suffix + ".tmp")
with zipfile.ZipFile(path, "r") as src, zipfile.ZipFile(
tmp, "w", zipfile.ZIP_DEFLATED
) as dst:
for info in src.infolist():
data = src.read(info.filename)
info.date_time = (1980, 1, 1, 0, 0, 0)
dst.writestr(info, data)
tmp.replace(path)
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--source", required=True)
ap.add_argument("--out", required=True)
args = ap.parse_args()
out = Path(args.out)
out.parent.mkdir(parents=True, exist_ok=True)
xlsx.load_workbook(args.source).save(str(out))
_rezip_with_epoch_timestamps(out)
if __name__ == "__main__":
main()
{
"$schema": "../manifest.schema.json",
"id": "xlsx/round-trip-identity",
"kind": "parameterised",
"title": "Round-trip preserves core content (xlsx)",
"format": "xlsx",
"category": "round-trip",
"summary": "Load an existing committed fixture through python-xlsx, save it to a temp path, then re-assert the primary structural markers survive. Covers eight fixtures spanning cell data, formulas, defined names, multi-sheet workbooks, merges, panes, number formatting and font styling.",
"spec": {
"source": "ecma-376-5-part-1",
"clause": "18",
"element": "x:sheetData",
"xsd_reference": "spec/ecma-376-5/part-1/xsd/sml.xsd",
"notes": "Identity-style round-trip — each case re-loads a committed fixture and asserts a representative cell or formula text is preserved. The sharedStrings count is *not* checked directly in XPath (no equal-count primitive) — the presence assertions below catch the usual classes of drift."
},
"fixtures": {
"machine": "xlsx/round-trip-identity"
},
"round_trip": {
"library": "python-xlsx",
"source_fixture": "{case.source}"
},
"parameters": {
"case": [
{
"id": "cell-formula",
"source": "xlsx/cell-formula",
"probe_part": "xl/worksheets/sheet1.xml",
"probe_xpath": "//x:c[@r='A3']/x:f[normalize-space()='A1+A2']"
},
{
"id": "merged-cell",
"source": "xlsx/merged-cell",
"probe_part": "xl/worksheets/sheet1.xml",
"probe_xpath": "//x:mergeCell[@ref='A1:C1']"
},
{
"id": "defined-name",
"source": "xlsx/defined-name",
"probe_part": "xl/workbook.xml",
"probe_xpath": "//x:definedName"
},
{
"id": "multiple-sheets",
"source": "xlsx/multiple-sheets",
"probe_part": "xl/workbook.xml",
"probe_xpath": "//x:sheets/x:sheet[3]"
},
{
"id": "freeze-panes",
"source": "xlsx/freeze-panes",
"probe_part": "xl/worksheets/sheet1.xml",
"probe_xpath": "//x:pane[@state='frozen']"
},
{
"id": "cell-number",
"source": "xlsx/cell-number",
"probe_part": "xl/worksheets/sheet1.xml",
"probe_xpath": "//x:c[@r='A1']/x:v"
},
{
"id": "cell-date",
"source": "xlsx/cell-date",
"probe_part": "xl/worksheets/sheet1.xml",
"probe_xpath": "//x:c[@r='A1']/x:v"
},
{
"id": "cell-bold",
"source": "xlsx/cell-bold",
"probe_part": "xl/styles.xml",
"probe_xpath": "//x:fonts/x:font/x:b"
}
]
},
"generator": {
"python": "scripts/gen_round_trip_identity_xlsx.py",
"arg_template": "--source fixtures/{case.source}.xlsx --out fixtures/xlsx/round-trip-identity--{case.id}.xlsx"
},
"assertions_template": [
{
"id": "workbook-parses",
"part": "xl/workbook.xml",
"namespaces": {
"x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
},
"xpath": "//x:workbook",
"must": "exist",
"description": "Round-tripped workbook.xml must parse and carry a <workbook> root."
},
{
"id": "first-sheet-listed",
"part": "xl/workbook.xml",
"namespaces": {
"x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
},
"xpath": "//x:sheets/x:sheet[1]",
"must": "exist",
"description": "At least one worksheet must still be registered under <sheets>."
},
{
"id": "sheet1-parses",
"part": "xl/worksheets/sheet1.xml",
"namespaces": {
"x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
},
"xpath": "//x:worksheet",
"must": "exist",
"description": "sheet1.xml must still parse."
},
{
"id": "styles-part-present",
"part": "xl/styles.xml",
"namespaces": {
"x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
},
"xpath": "//x:styleSheet",
"must": "exist",
"description": "styles.xml must survive with a <styleSheet> root — number-format ids live here."
},
{
"id": "case-specific-probe",
"part": "{case.probe_part}",
"namespaces": {
"x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
},
"xpath": "{case.probe_xpath}",
"must": "exist",
"description": "A case-specific feature marker (formula text, merged range, defined name, freeze pane, bold font, etc.) must survive the round-trip."
}
]
}