diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-20 16:05:34 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-20 16:05:34 +0200 |
| commit | 263d0d889073d214229aef9655d3db29540c0d12 (patch) | |
| tree | 895753cf5ec4e2aaacff5da50a8abfdf32eacb74 | |
| parent | 9dfa264f2941f1ca38eff08b6157a94d332cffbd (diff) | |
| download | colitur-263d0d889073d214229aef9655d3db29540c0d12.tar.gz colitur-263d0d889073d214229aef9655d3db29540c0d12.zip | |
fix(citation): complete the modern tradition, and test the shipped file
Apocalypse -> Revelation was missing from [modern] while `revelation` was
already a declared tradition target, so the target existed with nothing
mapping onto it. Same Vulgate-to-modern shape as Ecclesiasticus -> Sirach.
The injectivity test used to hand-copy the mapping table, which asserted
things about the copy and nothing about the artifact: a mapping added to
traditions.ini and forgotten in the test would have passed. It now reads
lang/traditions.ini and additionally asserts that [vulgate] is empty --
an entry there would silently renumber the DEFAULT, the one thing this
design promises never happens -- and that no declared target is left
unreachable.
Mutation-proved three ways: declaring an unused target, deleting a
mapping, and adding an entry to [vulgate] each redden it, the first two
naming the offending book.
| -rw-r--r-- | lang/traditions.ini | 1 | ||||
| -rw-r--r-- | test/test_citation.ml | 44 |
2 files changed, 41 insertions, 4 deletions
diff --git a/lang/traditions.ini b/lang/traditions.ini index e2d53ab..46f6ee7 100644 --- a/lang/traditions.ini +++ b/lang/traditions.ini @@ -19,3 +19,4 @@ esdras_2 = nehemiah ecclesiasticus = sirach osee = hosea jonas = jonah +apocalypse = revelation diff --git a/test/test_citation.ml b/test/test_citation.ml index d9be86e..1efaa10 100644 --- a/test/test_citation.ml +++ b/test/test_citation.ml @@ -180,13 +180,49 @@ let test_every_data_file_token_resolves () = always a typo -- it would silently merge two books -- so this asserts the mapping is injective, not merely that every id is known. *) let test_modern_tradition_is_injective () = - let fields = - [ ("kings_3", "kings_1"); ("kings_4", "kings_2"); - ("esdras_2", "nehemiah"); ("ecclesiasticus", "sirach"); - ("osee", "hosea"); ("jonas", "jonah") ] + (* READ the shipped file. An earlier version of this test hand-copied the + table, which asserted things about the copy and nothing at all about the + artifact -- a mapping added to traditions.ini and forgotten here would + have passed. *) + let text = + let ic = open_in_bin "../lang/traditions.ini" in + let s = really_input_string ic (in_channel_length ic) in + close_in ic; s in + let sections = + match Colitur_kernel.Overlay_ini.parse_sections text with + | Ok ss -> ss + | Error e -> Alcotest.failf "lang/traditions.ini: %s" e + in + let section name = + List.filter_map + (fun (sc : Colitur_kernel.Overlay_ini.section) -> + if sc.Colitur_kernel.Overlay_ini.name = name then + Some sc.Colitur_kernel.Overlay_ini.fields + else None) + sections + |> List.concat + in + (* [vulgate] must be the identity: an entry there would silently renumber + the DEFAULT, which is the one thing this design promises never happens. *) + Alcotest.(check (list (pair string string))) "vulgate is identity" [] + (section "vulgate"); + let fields = section "modern" in + Alcotest.(check bool) "modern is not empty" true (fields <> []); Alcotest.(check (list string)) "all ids known" [] (B.unknown_fields fields); + (* every declared tradition target is actually reachable -- an unused target + is either a forgotten mapping or dead weight *) let targets = List.map snd fields in + (* [B.tokens] is (spelling, id) pairs, so membership of an ID is a lookup + over the VALUES, not [mem_assoc] over the keys. Getting that backwards + flags every ordinary book as an unused target. *) + let cited = List.map (fun (_, i) -> B.to_string i) B.tokens in + List.iter + (fun id -> + let n = B.to_string id in + if (not (List.mem n cited)) && not (List.mem n targets) then + Alcotest.failf "%s is a declared target no tradition maps onto" n) + B.all; let uniq = List.sort_uniq compare targets in Alcotest.(check int) "no two ids share a target" (List.length targets) (List.length uniq) |
