Rows 2 and 3 require the build's inventory to EQUAL the witness's, so what the witness does not count, nothing can lose visibly. An independent review put a header and a comment in a docx, measured 0 of either in the bundle, and the accounting still read "2 of 2 carried". Thirteen classes are now counted, each with a red test written first: docx header/footer, comment, endnote and text box (a box's paragraphs are its own, or the text is booked twice) - pptx speaker note and hidden slide (`show="0"`, no longer counted as an ordinary slide) - xlsx formula and hidden sheet (the state lives in `workbook.xml` and is reached through the relationship id, so the sheet part itself says nothing about it) - odt header/footer from `styles.xml` and annotation (counted as prose, it made the accounting demand a reader carry a note the author wrote to themselves) - STS `mixed-citation`, `mml:math`, `fig` and its caption, measured by the review at 4.1 % of N200's source text and 3.9 % of N100's. M-2: the two STS witnesses had ONE role map between them, so row 5 -- "two witnesses agree" -- could not see a hole in it. `_sts_role_xml` and `_sts_role_json` are written apart, each for its own delivery, and a test holds them apart. M-3: 20 of 63 element types had a count of ZERO in their only fixture. Seven hand-built documents close it, every element type now occurs at least once (a test asserts it), and ALL TWENTY documents carry a hand count read off the fixture's own bytes (four did before). `.xlsx image` -- the operator's own proposed exception -- could not be exercised at all until now. Every witness also states WHAT IT STILL DOES NOT COUNT, per file type, and the gate prints that list on every run. THE FIXTURE ROWS ARE RED NOW, AND THAT IS THE POINT. Row 2 red on .docx, .odt, .pptx, .xlsx and .xml; row 3 at u = 25, d = 2 over the new classes, including a footnote and four spreadsheet cells the build genuinely drops. `0 claimed and not found` on the same run: nothing the build DOES book as carried failed the bundle check, so the red is the build's and not the instrument's. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
327 lines
15 KiB
Python
327 lines
15 KiB
Python
"""The accounting corpus's SECOND document per format: the elements the
|
|
witness could not see until 2026-09-18.
|
|
|
|
An independent review of the gate found that the witness -- and therefore the
|
|
whole accounting, because rows 2 and 3 require the build's inventory to EQUAL
|
|
it -- counted no header, no comment, no speaker note, no hidden sheet, no
|
|
formula, no citation and no figure caption. What nothing counts, nothing can
|
|
lose visibly. It also found that 20 of 63 element types had a count of ZERO in
|
|
their only fixture, so six of seven witness mutants survived.
|
|
|
|
These documents are written BY HAND, part by part, for the reason the XML
|
|
fixtures state: a library that writes and then reads its own format proves
|
|
only that it agrees with itself. Every count they carry is written down in
|
|
`tests/fixtures/README.md` by a person reading these strings, not by running
|
|
the witness over them.
|
|
|
|
python3 tests/fixtures/accounting/make_accounting_fixtures.py
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import io
|
|
import zipfile
|
|
from pathlib import Path
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
CORPUS = HERE / "corpus"
|
|
|
|
_ZIP_DATE = (2020, 1, 1, 0, 0, 0)
|
|
_XML = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
|
|
|
|
_W = 'xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"'
|
|
_A = 'xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"'
|
|
_P = 'xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"'
|
|
_S = 'xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"'
|
|
|
|
|
|
def build_zip(parts: dict[str, str | bytes]) -> bytes:
|
|
"""Zip the parts with a fixed timestamp, so a regeneration that changes
|
|
nothing leaves the bytes alone and `git diff --quiet` stays a real check."""
|
|
out = io.BytesIO()
|
|
with zipfile.ZipFile(out, "w", compression=zipfile.ZIP_DEFLATED) as archive:
|
|
for name, payload in parts.items():
|
|
info = zipfile.ZipInfo(name, date_time=_ZIP_DATE)
|
|
info.compress_type = zipfile.ZIP_DEFLATED
|
|
archive.writestr(info, payload)
|
|
return out.getvalue()
|
|
|
|
|
|
# --- docx: a header, a footer, a comment, an endnote and a text box ----------
|
|
|
|
_DOCX_BODY = (
|
|
'<w:p><w:pPr><w:pStyle w:val="Heading1"/></w:pPr>'
|
|
"<w:r><w:t>Krav til gangbruer</w:t></w:r></w:p>"
|
|
"<w:p><w:r><w:t>Gangbruer skal ha rekkverk paa begge sider.</w:t></w:r></w:p>"
|
|
"<w:tbl><w:tr>"
|
|
"<w:tc><w:p><w:r><w:t>Bredde</w:t></w:r></w:p></w:tc>"
|
|
"<w:tc><w:p><w:r><w:t>3,0 m</w:t></w:r></w:p></w:tc>"
|
|
"</w:tr></w:tbl>"
|
|
"<w:p><w:r><w:pict><w:txbxContent>"
|
|
"<w:p><w:r><w:t>Merk: kravet gjelder ikke midlertidige bruer.</w:t></w:r></w:p>"
|
|
"</w:txbxContent></w:pict></w:r></w:p>"
|
|
)
|
|
|
|
_DOCX_PARTS: dict[str, str | bytes] = {
|
|
"[Content_Types].xml": _XML
|
|
+ '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
|
|
+ '<Default Extension="xml" ContentType="application/xml"/>'
|
|
+ '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.'
|
|
+ 'relationships+xml"/>'
|
|
+ '<Override PartName="/word/document.xml" ContentType="application/vnd.'
|
|
+ 'openxmlformats-officedocument.wordprocessingml.document.main+xml"/>'
|
|
+ '<Override PartName="/word/styles.xml" ContentType="application/vnd.'
|
|
+ 'openxmlformats-officedocument.wordprocessingml.styles+xml"/>'
|
|
+ "</Types>",
|
|
"_rels/.rels": _XML
|
|
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
|
|
+ '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/'
|
|
+ 'relationships/officeDocument" Target="word/document.xml"/></Relationships>',
|
|
"word/_rels/document.xml.rels": _XML
|
|
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
|
|
+ '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/'
|
|
+ 'relationships/styles" Target="styles.xml"/></Relationships>',
|
|
"word/styles.xml": _XML
|
|
+ f"<w:styles {_W}>"
|
|
+ '<w:style w:type="paragraph" w:styleId="Heading1"><w:name w:val="heading 1"/></w:style>'
|
|
+ "</w:styles>",
|
|
"word/document.xml": _XML + f"<w:document {_W}><w:body>{_DOCX_BODY}</w:body></w:document>",
|
|
"word/header1.xml": _XML
|
|
+ f"<w:hdr {_W}><w:p><w:r><w:t>Utkast - gjelder ikke etter 2026-01-01</w:t></w:r></w:p>"
|
|
+ "</w:hdr>",
|
|
"word/footer1.xml": _XML
|
|
+ f"<w:ftr {_W}><w:p><w:r><w:t>Statens vegvesen, side 1</w:t></w:r></w:p></w:ftr>",
|
|
"word/comments.xml": _XML
|
|
+ f"<w:comments {_W}>"
|
|
+ '<w:comment w:id="1"><w:p><w:r><w:t>Unntak: gjelder IKKE gangbruer i tunnel.'
|
|
+ "</w:t></w:r></w:p></w:comment></w:comments>",
|
|
"word/footnotes.xml": _XML
|
|
+ f"<w:footnotes {_W}>"
|
|
+ '<w:footnote w:id="0"><w:p><w:r><w:t>separator</w:t></w:r></w:p></w:footnote>'
|
|
+ '<w:footnote w:id="2"><w:p><w:r><w:t>Se haandbok N400 kapittel 5.</w:t></w:r></w:p>'
|
|
+ "</w:footnote></w:footnotes>",
|
|
"word/endnotes.xml": _XML
|
|
+ f"<w:endnotes {_W}>"
|
|
+ '<w:endnote w:id="0"><w:p><w:r><w:t>separator</w:t></w:r></w:p></w:endnote>'
|
|
+ '<w:endnote w:id="3"><w:p><w:r><w:t>Kravet ble skjerpet i 2024.</w:t></w:r></w:p>'
|
|
+ "</w:endnote></w:endnotes>",
|
|
}
|
|
|
|
|
|
# --- pptx: a speaker note and a hidden slide --------------------------------
|
|
|
|
|
|
def _slide(title: str, body: str, *, hidden: bool = False) -> str:
|
|
show = ' show="0"' if hidden else ""
|
|
return (
|
|
_XML
|
|
+ f"<p:sld {_P} {_A}{show}><p:cSld><p:spTree>"
|
|
+ '<p:sp><p:nvSpPr><p:nvPr><p:ph type="title"/></p:nvPr></p:nvSpPr>'
|
|
+ f"<p:txBody><a:p><a:r><a:t>{title}</a:t></a:r></a:p></p:txBody></p:sp>"
|
|
+ "<p:sp><p:nvSpPr><p:nvPr/></p:nvSpPr>"
|
|
+ f"<p:txBody><a:p><a:r><a:t>{body}</a:t></a:r></a:p></p:txBody></p:sp>"
|
|
+ "<p:graphicFrame><a:graphic><a:graphicData><a:tbl><a:tr>"
|
|
+ "<a:tc><a:txBody><a:p><a:r><a:t>Post</a:t></a:r></a:p></a:txBody></a:tc>"
|
|
+ "<a:tc><a:txBody><a:p><a:r><a:t>84.1</a:t></a:r></a:p></a:txBody></a:tc>"
|
|
+ "</a:tr></a:tbl></a:graphicData></a:graphic></p:graphicFrame>"
|
|
+ "</p:spTree></p:cSld></p:sld>"
|
|
)
|
|
|
|
|
|
_PPTX_PARTS: dict[str, str | bytes] = {
|
|
"[Content_Types].xml": _XML
|
|
+ '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
|
|
+ '<Default Extension="xml" ContentType="application/xml"/>'
|
|
+ '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.'
|
|
+ 'relationships+xml"/>'
|
|
+ '<Override PartName="/ppt/presentation.xml" ContentType="application/vnd.'
|
|
+ 'openxmlformats-officedocument.presentationml.presentation.main+xml"/>'
|
|
+ "".join(
|
|
f'<Override PartName="/ppt/slides/slide{n}.xml" ContentType="application/vnd.'
|
|
f'openxmlformats-officedocument.presentationml.slide+xml"/>'
|
|
for n in (1, 2)
|
|
)
|
|
+ "</Types>",
|
|
"_rels/.rels": _XML
|
|
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
|
|
+ '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/'
|
|
+ 'relationships/officeDocument" Target="ppt/presentation.xml"/></Relationships>',
|
|
"ppt/presentation.xml": _XML
|
|
+ f"<p:presentation {_P}><p:sldIdLst>"
|
|
+ '<p:sldId id="256" r:id="rId1"/><p:sldId id="257" r:id="rId2"/>'
|
|
+ "</p:sldIdLst></p:presentation>",
|
|
"ppt/slides/slide1.xml": _slide("Prosess 84 Konstruksjoner", "Toleranser er gitt i tabell."),
|
|
"ppt/slides/slide2.xml": _slide("Utgaatt lysbilde", "Ikke vis dette.", hidden=True),
|
|
"ppt/notesSlides/notesSlide1.xml": _XML
|
|
+ f"<p:notes {_P} {_A}><p:cSld><p:spTree><p:sp><p:txBody>"
|
|
+ "<a:p><a:r><a:t>Husk aa nevne at toleranseklassen er skjerpet.</a:t></a:r></a:p>"
|
|
+ "</p:txBody></p:sp></p:spTree></p:cSld></p:notes>",
|
|
}
|
|
|
|
|
|
# --- xlsx: a hidden sheet and a formula --------------------------------------
|
|
|
|
_XLSX_STRINGS = ["Post", "Enhet", "Mengde", "Sum", "Internt", "Kladd"]
|
|
|
|
_XLSX_PARTS: dict[str, str | bytes] = {
|
|
"[Content_Types].xml": _XML
|
|
+ '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
|
|
+ '<Default Extension="xml" ContentType="application/xml"/>'
|
|
+ '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.'
|
|
+ 'relationships+xml"/>'
|
|
+ '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-'
|
|
+ 'officedocument.spreadsheetml.sheet.main+xml"/>'
|
|
+ "</Types>",
|
|
"_rels/.rels": _XML
|
|
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
|
|
+ '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/'
|
|
+ 'relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>',
|
|
"xl/workbook.xml": _XML
|
|
+ f'<workbook {_S} xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/'
|
|
+ 'relationships"><sheets>'
|
|
+ '<sheet name="Mengder" sheetId="1" r:id="rId1"/>'
|
|
+ '<sheet name="Internt" sheetId="2" state="hidden" r:id="rId2"/>'
|
|
+ "</sheets></workbook>",
|
|
"xl/_rels/workbook.xml.rels": _XML
|
|
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
|
|
+ '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/'
|
|
+ 'relationships/worksheet" Target="worksheets/sheet1.xml"/>'
|
|
+ '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/'
|
|
+ 'relationships/worksheet" Target="worksheets/sheet2.xml"/></Relationships>',
|
|
"xl/sharedStrings.xml": _XML
|
|
+ f'<sst {_S} count="{len(_XLSX_STRINGS)}" uniqueCount="{len(_XLSX_STRINGS)}">'
|
|
+ "".join(f"<si><t>{value}</t></si>" for value in _XLSX_STRINGS)
|
|
+ "</sst>",
|
|
"xl/worksheets/sheet1.xml": _XML
|
|
+ f'<worksheet {_S}><dimension ref="A1:C3"/><sheetData>'
|
|
+ '<row r="1"><c r="A1" t="s"><v>0</v></c><c r="B1" t="s"><v>1</v></c>'
|
|
+ '<c r="C1" t="s"><v>2</v></c></row>'
|
|
+ '<row r="2"><c r="A2" t="s"><v>3</v></c><c r="B2"><v>12</v></c>'
|
|
+ '<c r="C2"><f>B2*2</f><v>24</v></c></row>'
|
|
+ '</sheetData><drawing r:id="rId1" xmlns:r="http://schemas.openxmlformats.org/'
|
|
+ 'officeDocument/2006/relationships"/></worksheet>',
|
|
"xl/drawings/drawing1.xml": _XML
|
|
+ '<xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/'
|
|
+ 'spreadsheetDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">'
|
|
+ "<xdr:twoCellAnchor><xdr:pic><xdr:nvPicPr>"
|
|
+ '<xdr:cNvPr id="1" name="Diagram"/>'
|
|
+ "</xdr:nvPicPr></xdr:pic></xdr:twoCellAnchor></xdr:wsDr>",
|
|
"xl/worksheets/_rels/sheet1.xml.rels": _XML
|
|
+ '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'
|
|
+ '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/'
|
|
+ 'relationships/drawing" Target="../drawings/drawing1.xml"/></Relationships>',
|
|
"xl/worksheets/sheet2.xml": _XML
|
|
+ f'<worksheet {_S}><dimension ref="A1:A1"/><sheetData>'
|
|
+ '<row r="1"><c r="A1" t="s"><v>5</v></c></row>'
|
|
+ "</sheetData></worksheet>",
|
|
}
|
|
|
|
|
|
# --- odt: a header, a list, an annotation and a picture ----------------------
|
|
|
|
_ODT_NS = (
|
|
'xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" '
|
|
'xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" '
|
|
'xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" '
|
|
'xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" '
|
|
'xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" '
|
|
'xmlns:xlink="http://www.w3.org/1999/xlink"'
|
|
)
|
|
|
|
_ODT_PARTS: dict[str, str | bytes] = {
|
|
"mimetype": "application/vnd.oasis.opendocument.text",
|
|
"META-INF/manifest.xml": _XML
|
|
+ '<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">'
|
|
+ '<manifest:file-entry manifest:full-path="/" manifest:media-type="application/vnd.oasis.'
|
|
+ 'opendocument.text"/>'
|
|
+ '<manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/>'
|
|
+ "</manifest:manifest>",
|
|
"content.xml": _XML
|
|
+ f"<office:document-content {_ODT_NS}><office:body><office:text>"
|
|
+ '<text:h text:outline-level="1">Drift av gangbruer</text:h>'
|
|
+ "<text:p>Gangbruer inspiseres hvert aar.</text:p>"
|
|
+ "<text:list><text:list-item><text:p>Rekkverk</text:p></text:list-item>"
|
|
+ "<text:list-item><text:p>Dekke</text:p></text:list-item></text:list>"
|
|
+ "<text:p>Se figuren under."
|
|
+ '<draw:frame><draw:image xlink:href="graphics/figur-84-1.png"/></draw:frame></text:p>'
|
|
+ "<office:annotation><text:p>Sjekk denne mot N400 foer utsendelse.</text:p>"
|
|
+ "</office:annotation>"
|
|
+ "<table:table><table:table-row>"
|
|
+ "<table:table-cell><text:p>Type</text:p></table:table-cell>"
|
|
+ "<table:table-cell><text:p>Gangbru</text:p></table:table-cell>"
|
|
+ "</table:table-row></table:table>"
|
|
+ "</office:text></office:body></office:document-content>",
|
|
"styles.xml": _XML
|
|
+ f"<office:document-styles {_ODT_NS}><office:master-styles>"
|
|
+ '<style:master-page style:name="Standard">'
|
|
+ "<style:header><text:p>Intern arbeidsversjon</text:p></style:header>"
|
|
+ "<style:footer><text:p>Vegdirektoratet</text:p></style:footer>"
|
|
+ "</style:master-page></office:master-styles></office:document-styles>",
|
|
}
|
|
|
|
|
|
# --- rtf: a picture ----------------------------------------------------------
|
|
|
|
_RTF = (
|
|
r"{\rtf1\ansi\deff0{\fonttbl{\f0 Times New Roman;}}"
|
|
r"\pard Figur 84-1 viser prinsippet.\par"
|
|
r"\pard{\pict\pngblip\picw16\pich16 89504e470d0a1a0a}\par"
|
|
"}"
|
|
)
|
|
|
|
|
|
# --- html: a picture and a caption -------------------------------------------
|
|
|
|
_HTML = """<!DOCTYPE html>
|
|
<html lang="no">
|
|
<head><title>Figur 84-1</title></head>
|
|
<body>
|
|
<h1>Figur 84-1</h1>
|
|
<p>Prinsippet for toleranseklasser.</p>
|
|
<img src="graphics/figur-84-1.png" alt="Prinsippskisse">
|
|
<table><tr><th>Klasse</th><th>Avvik</th></tr><tr><td>A</td><td>5 mm</td></tr></table>
|
|
<ul><li>Klasse A</li><li>Klasse B</li></ul>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
|
|
# --- sts: a citation, a formula, a figure with a caption, a table, a footnote -
|
|
|
|
_STS = """<standard>
|
|
<front><std-ident><doc-number>R762</doc-number><year>2025</year></std-ident></front>
|
|
<body>
|
|
<sec><label>85</label><title>Vegdekker</title>
|
|
<p>Dekket skal ha jevnhet etter <mixed-citation>NS-EN 13036-1:2010</mixed-citation>.</p>
|
|
<p>Kravet regnes som <mml:math xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mi>IRI</mml:mi><mml:mo><</mml:mo><mml:mn>2</mml:mn></mml:math>.</p>
|
|
<fig><label>Figur 85-1</label><caption><p>Maalepunkter langs vegbanen.</p></caption>
|
|
<graphic xlink:href="figur-84-1.png" xmlns:xlink="http://www.w3.org/1999/xlink"/></fig>
|
|
<table-wrap><label>Tabell 85-1</label>
|
|
<table><tr><th>Klasse</th><th>IRI</th></tr><tr><td>1</td><td>1,5</td></tr></table>
|
|
</table-wrap>
|
|
<list><list-item><p>Maales hvert 20. meter.</p></list-item></list>
|
|
<fn><p>Gjelder ikke gang- og sykkelveger.</p></fn>
|
|
</sec>
|
|
</body>
|
|
</standard>
|
|
"""
|
|
|
|
|
|
def main() -> int:
|
|
written = {
|
|
"topptekst-og-kommentar.docx": build_zip(_DOCX_PARTS),
|
|
"notater-og-skjult.pptx": build_zip(_PPTX_PARTS),
|
|
"skjult-ark-og-formel.xlsx": build_zip(_XLSX_PARTS),
|
|
"liste-og-bilde.odt": build_zip(_ODT_PARTS),
|
|
"bilde.rtf": _RTF.encode("latin-1"),
|
|
"figur.html": _HTML.encode("utf-8"),
|
|
"sts-rikt.xml": _STS.encode("utf-8"),
|
|
}
|
|
for name, payload in written.items():
|
|
(CORPUS / name).write_bytes(payload)
|
|
print(f"wrote {name}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|