Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# The artwork under docs/art/ is generated, and a gate compares the committed
# bytes to a fresh render. Normalizing line endings per platform would make that
# comparison depend on who checked the repository out.
# The art under docs/art/ is generated. render_repo_art.py writes LF on every
# platform, so pin the checkout to LF as well and each file's byte count and
# SHA-256 stay the same whoever cloned the repository.
docs/art/*.svg text eol=lf
docs/art/*.json text eol=lf
1 change: 1 addition & 0 deletions tests/test_repo_art.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"art.every_illustration_is_shown",
"art.tagline_stays_inside_its_rule",
"art.outcome_fits_its_box",
"art.the_gate_can_fail",
)

DRAWINGS = (
Expand Down
41 changes: 38 additions & 3 deletions tools/check_repo_art.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,12 @@ def check_note_survives_the_wrapper(_unused: list[Path]) -> list[str]:
"""Card notes wrap to three lines and the wrapper drops the rest, so an
edited sentence can lose its ending in the drawing while reading fine
in the spec."""
return _notes_the_wrapper_cuts(_loaded())


def _notes_the_wrapper_cuts(specs: list[dict]) -> list[str]:
bad = []
for spec in _loaded():
for spec in specs:
for flow in spec.get("flows", []):
for stage in flow["stages"]:
drawn = " ".join(FLOW._wrap(stage["note"]))
Expand Down Expand Up @@ -167,8 +171,12 @@ def check_every_illustration_is_shown(_unused: list[Path]) -> list[str]:
def check_tagline_stays_inside_its_rule(_unused: list[Path]) -> list[str]:
"""The tagline is one unwrapped line under a rule that ends at x=700. Past
that it runs on toward the aperture and nothing about the render fails."""
return _taglines_that_overrun(_loaded())


def _taglines_that_overrun(specs: list[dict]) -> list[str]:
bad = []
for spec in _loaded():
for spec in specs:
tagline = spec["header"]["tagline"]
if len(tagline) > TAGLINE_BUDGET:
bad.append(f"{len(tagline)} characters runs past the rule: {tagline!r}")
Expand All @@ -185,8 +193,12 @@ def _outcome_budgets(count: int) -> tuple[int, int]:
def check_outcome_fits_its_box(_unused: list[Path]) -> list[str]:
"""An outcome box is one unwrapped label over one unwrapped note, and
neither is clipped, so an over-long note runs into the next box."""
return _outcomes_that_overflow(_loaded())


def _outcomes_that_overflow(specs: list[dict]) -> list[str]:
bad = []
for spec in _loaded():
for spec in specs:
for flow in spec.get("flows", []):
label_budget, note_budget = _outcome_budgets(len(flow["outcomes"]))
for item in flow["outcomes"]:
Expand All @@ -198,6 +210,28 @@ def check_outcome_fits_its_box(_unused: list[Path]) -> list[str]:
return bad


# A spec built to break all three geometry budgets at once. Every other check
# here reports clean, which says it ran and not that it works.
CONTROL = [{
"header": {"tagline": "x" * (TAGLINE_BUDGET + 1)},
"flows": [{
"stages": [{"title": "CARD", "note": "word " * 60}],
"outcomes": [{"label": "OK", "note": "x" * 200},
{"label": "y" * 200, "note": "short"}],
}],
}]


def check_the_gate_can_fail(_unused: list[Path]) -> list[str]:
"""Feed the three geometry checks input they have to reject."""
return [f"the gate missed {what}" for caught, what in (
(len(_notes_the_wrapper_cuts(CONTROL)) == 1, "a truncated note"),
(len(_taglines_that_overrun(CONTROL)) == 1, "a tagline past its rule"),
(len(_outcomes_that_overflow(CONTROL)) == 2,
"an over-wide label and an over-long note"),
) if not caught]


CHECKS = [
("spec.present", check_spec_present),
("art.matches_spec", check_artwork_matches_spec),
Expand All @@ -211,6 +245,7 @@ def check_outcome_fits_its_box(_unused: list[Path]) -> list[str]:
("art.every_illustration_is_shown", check_every_illustration_is_shown),
("art.tagline_stays_inside_its_rule", check_tagline_stays_inside_its_rule),
("art.outcome_fits_its_box", check_outcome_fits_its_box),
("art.the_gate_can_fail", check_the_gate_can_fail),
]


Expand Down
Loading