diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-26 13:08:55 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-26 13:08:55 +0200 |
| commit | 1a3046e2a3793fe52ec518818b19a6f15aadbb75 (patch) | |
| tree | 83bb4bcc6d646b74093d18324076c32e2fede74b /test | |
| parent | 37a3d4ee06717c24ad07db2bc365cad587ad6d5f (diff) | |
| download | colitur-1a3046e2a3793fe52ec518818b19a6f15aadbb75.tar.gz colitur-1a3046e2a3793fe52ec518818b19a6f15aadbb75.zip | |
fix(of): print the OF's own transcribed Latin/English names, not EF's
day_line_of/readings_line_of resolved every displayed name through
lang/la.ini -- data documented as "transcribed from the 1962 Missale
Romanum", i.e. EF vocabulary, keyed on slug. 18 of 222 shipped OF
sanctoral slugs collide with an EF slug there and all 18 printed the
WRONG (1962) title (e.g. "S. Marthae Virg.", a rank word the 1969
reform abolished, silently overriding amendment 011's own "Ss.
Marthae, Mariae et Lazari"); the other 204 had no entry and degraded
to a bare slug, so none of the 208 titles calendar-2002.sexp itself
transcribes (cited against the 2002 Missal) was ever reachable.
Add observed_name_of: prefer the observed celebration's own
Celebration.names (looked up by the requested language code), fall
back to the lang/*.ini table only on a miss. Under --raw this is a
no-op (Lang.raw's own code, "raw", is not a valid 2-letter ISO code,
so the names lookup always misses and falls through to the identity
table exactly as before). EF's day_line/readings_line are untouched --
EF's Celebration.names is almost always empty by design, lang/la.ini
is its real, independently-cited name table.
Regression net, two layers: test_lang_coverage.ml gains a test walking
the real assembled OF layer's entries, asserting that every genuine
lang/la.ini collision resolves to the OF's own name, never the EF
table's; test/cli.t pins one real collision (Martha, 29 July) through
the actual CLI in both la and en.
Diffstat (limited to 'test')
| -rw-r--r-- | test/cli.t | 12 | ||||
| -rw-r--r-- | test/test_lang_coverage.ml | 111 |
2 files changed, 122 insertions, 1 deletions
@@ -232,6 +232,18 @@ The OF rite resolves too, same two commands, its own vocabulary: $ colitur readings --rite of 2026 | head -1 2026-01-01 of-mary-mother-of-god | Num 6:22-27 | Luc 2:16-21 +Fix wave I1 (final-review.md): a sanctoral-origin OF day names through its +OWN transcribed title (calendar-2002.sexp, amended by 011's own rename), +never through lang/la.ini's EF (1962) table -- 29 July, Martha, is one of +the 18 real slug collisions between the two vocabularies the review found; +la.ini's own entry for "martha" is the pre-2021 EF title "S. Marthae Virg.", +and printing it here would be exactly the regression this guards. + + $ colitur day --rite of 2026 | grep '^2026-07-29 ' + 2026-07-29 wednesday ordinary-time 17 martha memoria-obligatoria white Ss. Marthae, Mariae et Lazari + $ colitur day --rite of 2026 --lang en | grep '^2026-07-29 ' + 2026-07-29 wednesday ordinary-time 17 martha memoria-obligatoria white Saints Martha, Mary and Lazarus + An unrecognised --rite value is a usage error, not a silent fallback to ef (exit 2): diff --git a/test/test_lang_coverage.ml b/test/test_lang_coverage.ml index d41f7e7..01aa9b7 100644 --- a/test/test_lang_coverage.ml +++ b/test/test_lang_coverage.ml @@ -295,6 +295,113 @@ let test_en_falls_back_to_latin () = Alcotest.(check bool) "empty table falls back to la.ini for a real slug" true (L.celebration chained "hilary" <> "hilary") +(* ------------------------------------------------------------------ *) +(* Fix wave I1 (final-review.md, 2026-08-25-colitur-of-phases-3-5): *) +(* --rite of used to resolve every displayed name through lang/la.ini, *) +(* EF's own independently-cited 1962-vocabulary table, keyed on slug. *) +(* 18 of 222 shipped OF sanctoral slugs happen to collide with an EF *) +(* slug there and all 18 printed the WRONG (1962) title -- e.g. *) +(* "S. Marthae Virg." (a title the 1969 reform deleted, and which also *) +(* silently overrode amendment 011's own rename) instead of *) +(* calendar-2002.sexp's own "Ss. Marthae, Mariae et Lazari". The other *) +(* 204 had no entry there at all and degraded to a bare slug, so none *) +(* of the 208 hand-verified titles Task 1 transcribed (cited against *) +(* the 2002 Missal) was ever reachable. *) +(* *) +(* bin/main.ml's [observed_name_of] now prefers the observed *) +(* celebration's OWN {!Colitur_kernel.Celebration.names}, falling back *) +(* to the lang table only on a miss. That priority order is duplicated *) +(* here -- bin/ and test/ are separate dune stanzas that do not share *) +(* code, the same reasoning test_support.ml's own header gives for why *) +(* it duplicates bin/main.ml's EF loaders rather than requiring them -- *) +(* and checked against every entry the REAL, assembled OF layer *) +(* actually carries (calendar-2002.sexp + all 13 amendments), not a *) +(* synthetic fixture. Walking {!Colitur_kernel.Layer.t.entries} directly *) +(* rather than a resolved calendar year: several of the 18 real *) +(* collisions (e.g. Margaret of Scotland, 16 November) never win the *) +(* alphabetical tie-break against a same-day rival in ANY year and so *) +(* are never the OBSERVED day at all -- the collision in the DATA is *) +(* real and load-bearing regardless (a future tie-break or Calendar *) +(* change could make her win one), so this test does not depend on *) +(* which slugs happen to be observed in any particular year range. *) +let of_base_path = "../data/of/calendar-2002.sexp" +let of_amendments_dir = "../data/of/amendments/" +let of_amendment_files = + [ "001-padre-pio.sexp"; "002-juan-diego-cuauhtlatoatzin.sexp"; "003-our-lady-of-guadalupe.sexp"; + "004-john-xxiii-john-paul-ii.sexp"; "005-mary-magdalene-rank.sexp"; "006-mary-mother-of-the-church.sexp"; + "007-paul-vi.sexp"; "008-our-lady-of-loreto.sexp"; "009-faustina-kowalska.sexp"; + "010-narek-avila-hildegard.sexp"; "011-martha-mary-lazarus.sexp"; "012-teresa-of-calcutta.sexp"; + "013-john-henry-newman.sexp" ] + +let of_layer () = + match Colitur_kernel.Layer.load Rite_of.Vocab_of.rank_of_sexp of_base_path with + | Error e -> Alcotest.failf "%s: %s" of_base_path e + | Ok base -> + let overlays = + List.map + (fun name -> + let path = of_amendments_dir ^ name in + match Colitur_kernel.Overlay.load Rite_of.Vocab_of.rank_of_sexp path with + | Ok o -> o + | Error e -> Alcotest.failf "%s: %s" path e) + of_amendment_files + in + let layer, diagnostics = Colitur_kernel.Overlay.merge base overlays in + if diagnostics <> [] then Alcotest.failf "unexpected OF amendment diagnostics on the shipped data"; + layer + +let la_lang = Colitur_kernel.Lang.of_string_exn "la" + +(* [observed_name_of]'s own resolution order (bin/main.ml), duplicated + rather than called -- see this section's own header. *) +let resolve_like_cli t (cel : Rite_of.Vocab_of.rank Colitur_kernel.Celebration.t) slug_s = + match Colitur_kernel.Names.find cel.Colitur_kernel.Celebration.names la_lang with + | Some n -> n + | None -> L.celebration t slug_s + +let test_of_names_win_over_ef_lang_table () = + let t = la () in + let layer = of_layer () in + let collisions = ref [] in + List.iter + (fun (e : _ Colitur_kernel.Layer.entry) -> + let cel = e.Colitur_kernel.Layer.cel in + let slug_s = Colitur_kernel.Slug.to_string cel.Colitur_kernel.Celebration.slug in + (* A miss returns the key itself (lang.mli's own TOTAL-lookup + contract): [L.celebration t slug_s <> slug_s] means la.ini really + does carry an entry for this OF slug -- a genuine collision, not + a hypothetical one. *) + if L.celebration t slug_s <> slug_s then collisions := slug_s :: !collisions) + layer.Colitur_kernel.Layer.entries; + (* Not asserting the collision count -- two independently-authored + vocabularies sharing a slug is expected (the whole reason this fix + exists) and not itself a defect; what matters, and what regresses + silently if [observed_name_of]'s priority order is ever reverted, is + what happens on EVERY one of them: *) + if !collisions = [] then + Alcotest.fail + "expected at least one real OF/EF slug collision (e.g. \"martha\") -- if this is now empty, \ + either lang/la.ini or calendar-2002.sexp changed and this test's own premise needs re-checking, \ + not silently passing on zero cases"; + List.iter + (fun slug_s -> + match Colitur_kernel.Layer.find layer (Colitur_kernel.Slug.of_string_exn slug_s) with + | None -> Alcotest.failf "%s: found in entries but not via Layer.find" slug_s + | Some (e : _ Colitur_kernel.Layer.entry) -> ( + let cel = e.Colitur_kernel.Layer.cel in + match Colitur_kernel.Names.find cel.Colitur_kernel.Celebration.names la_lang with + | None -> + Alcotest.failf + "%s: collides with lang/la.ini and calendar-2002.sexp carries NO own `la` name for it \ + -- would silently print EF's 1962 title (%s)" + slug_s (L.celebration t slug_s) + | Some own_name -> + Alcotest.(check string) + (Printf.sprintf "%s: resolves to the OF's own transcribed name, not lang/la.ini's EF one" + slug_s) + own_name (resolve_like_cli t cel slug_s))) + !collisions + let suite = ( "Lang/coverage", [ Alcotest.test_case "every slug has a Latin name" `Slow test_every_slug_has_a_latin_name; @@ -307,4 +414,6 @@ let suite = test_no_two_books_share_a_name; Alcotest.test_case "shipped styles round-trip" `Quick test_shipped_styles_round_trip; - Alcotest.test_case "en.ini falls back to Latin" `Quick test_en_falls_back_to_latin ] ) + Alcotest.test_case "en.ini falls back to Latin" `Quick test_en_falls_back_to_latin; + Alcotest.test_case "OF names win over lang/la.ini on every real slug collision" `Quick + test_of_names_win_over_ef_lang_table ] ) |
