diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/test_litcal_of.ml | 43 | ||||
| -rw-r--r-- | test/test_precedence_of.ml | 60 | ||||
| -rw-r--r-- | test/test_rite_of.ml | 65 |
3 files changed, 138 insertions, 30 deletions
diff --git a/test/test_litcal_of.ml b/test/test_litcal_of.ml index 6a638a7..9dfc5ba 100644 --- a/test/test_litcal_of.ml +++ b/test/test_litcal_of.ml @@ -640,6 +640,44 @@ let is_l7_2033_tie_identity (l : litcal_row) = exact date shape) -- NOT fixed here, per this task's own brief. *) let is_l8_l9_joseph_2035 (l : litcal_row) = String.equal l.event_key "StJoseph" && String.equal l.date "2035-03-17" +(* L10 -- W3 (of-known-wrongs, 2026-08-26). Precedence_of.band now gives the + two SHIPPED movable BVM obligatory memorials (Mary, Mother of the Church, + event_key "MaryMotherChurch"; the Immaculate Heart of Mary, event_key + "ImmaculateHeart") a sub-rank of 95 -- strictly between Tabula entries 9 + and 10, never the ordinary 100 -- so a real collision with a FIXED + obligatory memorial (e.g. 2011-06-13, Mater Ecclesiae vs Anthony of + Padua) resolves in the Marian memorial's favour instead of falling to + Colitur_kernel.Precedence.resolve's own alphabetical tie-break. See + Rite_of.Precedence_of.movable_bvm_memorials's own comment for the primary + source (the CDWDS's own 24 March 2018 Notification) and the full + reasoning. + + litcal's own [grade_lcl] text is coarser than this: EVERY + Memoria_obligatoria reads "Memorial", with no way to represent colitur's + own finer sub-rank -- so [expected_bands]'s [100;110] bucket cannot match + 95. This is a genuine, cited REPRESENTATIONAL gap in litcal's own + vocabulary, not a computational disagreement: the IDENTITY axis (which + day is actually observed) is untouched by this fix and stays unexplained + here on purpose -- [test_identity_matches_or_is_explained] needed no new + entry, confirmed by running it after this fix with no change. + + Fires on every fixture row naming either event_key where colitur's own + band is exactly 95 -- ANY date that entity is observed, not only an + actual collision date, because the sub-rank is unconditional (see + [movable_bvm_memorials]'s own comment for why: neither entity's date + range can ever reach a Tabula 1-9 day, so 95 is always safe and never + needs a live-collision guard). 15 rows over this fixture's 12 years, + measured directly against a real `dune test` run, not guessed: + MaryMotherChurch in 11 of 12 years (2033's own occurrence is a DIFFERENT, + already-explained shape, band 30 not 95 -- not this entity's date at + all that year; see L6) and ImmaculateHeart in 4 (the other 8 years' + ImmaculateHeart-window rows are excluded upstream by litcal's own + "optional memorial" grade quirk, [expected_bands]'s [None] case above, + or by L6/L7's own 2033 tie). *) +let is_l10_movable_bvm_subrank (l : litcal_row) band = + (String.equal l.event_key "MaryMotherChurch" || String.equal l.event_key "ImmaculateHeart") + && band = 95 + let test_grade_matches_or_is_explained () = let litcal = litcal_rows () in let allow_list = load_allow_list () in @@ -663,6 +701,7 @@ let test_grade_matches_or_is_explained () = else if is_l5_mary_magdalene l then bump "L5" else if is_l6_2033_tie_grade l then bump "L6" else if is_l8_l9_joseph_2035 l then bump "L8" + else if is_l10_movable_bvm_subrank l band then bump "L10" else unexplained := Printf.sprintf "%s: colitur band=%d, litcal grade=%S (event_key=%S, expected one of [%s])" l.date band @@ -679,7 +718,7 @@ let test_grade_matches_or_is_explained () = match List.assoc_opt id by_id with | None -> Alcotest.failf "allow-list entry %s is used by the comparator but not declared in %s" id allow_list_path | Some e -> Alcotest.(check int) (Printf.sprintf "%s expected_rows (grade)" id) e.expected_rows actual) - [ "L4"; "L5"; "L6"; "L8" ] + [ "L4"; "L5"; "L6"; "L8"; "L10" ] (* The complement, mirroring [test_unwitnessed_ordinary_time_is_counted]: every row [expected_bands] returns [None] for, classified by which of @@ -796,7 +835,7 @@ let test_no_holy_family_2033_divergence () = (* [is_holy_family_2033]'s own header. *) (* ---------------------------------------------------------------------- *) -let recognized_allow_ids = [ "L1"; "L4"; "L5"; "L6"; "L7"; "L8"; "L9" ] +let recognized_allow_ids = [ "L1"; "L4"; "L5"; "L6"; "L7"; "L8"; "L9"; "L10" ] let test_allow_list_has_no_orphan_entries () = let allow_list = load_allow_list () in diff --git a/test/test_precedence_of.ml b/test/test_precedence_of.ml index 23c26c2..e2e8540 100644 --- a/test/test_precedence_of.ml +++ b/test/test_precedence_of.ml @@ -524,6 +524,60 @@ let test_all_souls_beats_an_ordinary_sunday () = Alcotest.(check bool) "All Souls, not the Sunday, is observed" true (resolution.Precedence.observed = all_souls_candidate) +(* W3 (of-known-wrongs, 2026-08-26). Mary, Mother of the Church (Easter+50) + and a fixed obligatory memorial (e.g. Anthony of Padua, 13 June) both + used to band at Tabula III.10 (100) whenever they coincide -- a tie + {!Rite_of.Precedence_of.band} could not break, silently resolved instead + by {!Colitur_kernel.Precedence.resolve}'s own generic alphabetical + tie-break. See {!Rite_of.Precedence_of.movable_bvm_memorials}'s own + comment for the primary source (the CDWDS's own 24 March 2018 + Notification) this fix cites: the Marian memorial now bands at 95 and + always prevails. This is the direct unit-level counterpart to + test_rite_of.ml's own end-to-end + [test_mater_ecclesiae_anthony_tie_2011_resolved] -- this one calls + {!Colitur_kernel.Precedence.resolve} directly, the same shape every + other test in this file uses, and checks BOTH real years the sweep in + this task's own report found (2011 for Mater Ecclesiae, 2015 for the + Immaculate Heart -- three years apart via the other offset, colliding + with the SAME rival, Anthony of Padua, both confirmed via + {!Colitur_kernel.Computus.gregorian_easter} rather than trusted). *) +let test_movable_bvm_memorial_beats_a_fixed_one () = + let anthony = + mk ~slug:"anthony-of-padua-priest-and-doctor" ~rank:Vocab_of.Memoria_obligatoria + ~origin:Precedence.Sanctoral () + in + List.iter + (fun (label, slug, easter_offset, year, expected_date) -> + let movable = + mk ~slug ~rank:Vocab_of.Memoria_obligatoria ~subject:Subject.Bvm + ~origin:Precedence.Sanctoral () + in + Alcotest.(check bool) + (Printf.sprintf "%s: alphabetically \"anthony-of-padua...\" sorts BEFORE %S -- confirms this \ + test exercises the tie-break the fix overrides, not a vacuous check" + label slug) + true (String.compare "anthony-of-padua-priest-and-doctor" slug < 0); + let easter = Computus.gregorian_easter year in + let d = Date.add_days easter easter_offset in + Alcotest.(check string) + (Printf.sprintf "%s: Easter+%d %d really is %s" label easter_offset year expected_date) + expected_date (Date.to_iso8601 d); + let temporal_c = + mk ~slug:"of-ordinary-time-feria" ~rank:Vocab_of.Feria ~origin:Precedence.Temporal () + in + let ctx = { Precedence.date = d; season = Vocab_of.Ordinary_time; weekday = Date.weekday d } in + let resolution = + Precedence.resolve Rite_of.Precedence_of.rules ctx ~temporal:temporal_c + ~sanctoral:[ anthony; movable ] + in + Alcotest.(check bool) + (Printf.sprintf "%s prevails over Anthony of Padua, not the alphabetical order" label) + true (resolution.Precedence.observed = movable)) + [ ("Mary, Mother of the Church vs Anthony of Padua", "mary-mother-of-the-church", 50, 2011, + "2011-06-13"); + ("Immaculate Heart of Mary vs Anthony of Padua", "immaculate-heart-of-mary", 69, 2015, + "2015-06-13") ] + let suite = ( "precedence-of", [ Alcotest.test_case "Tabula part I (entries 1-4)" `Quick test_part1; @@ -549,5 +603,9 @@ let suite = test_target_is_free_of_entries_1_to_8; Alcotest.test_case "fix round 2: shipped All Souls beats an ordinary per-annum Sunday (2025-11-02)" `Quick - test_all_souls_beats_an_ordinary_sunday ] + test_all_souls_beats_an_ordinary_sunday; + Alcotest.test_case + "W3: a movable BVM obligatory memorial beats a fixed one, per the CDWDS's 24 March 2018 \ + Notification" `Quick + test_movable_bvm_memorial_beats_a_fixed_one ] @ List.map QCheck_alcotest.to_alcotest [ prop_band_total ] ) diff --git a/test/test_rite_of.ml b/test/test_rite_of.ml index 42c9c98..1de3614 100644 --- a/test/test_rite_of.ml +++ b/test/test_rite_of.ml @@ -373,21 +373,32 @@ let test_holy_family_fallback_1583_now_correct () = worked example that amendment header itself names (Easter 24 April 2011, so Easter+50 = 13 June -- re-derived here, not trusted from the comment). - KNOWN ARBITRARY, not KNOWN WRONG: unlike the two tests above, there is no - rubric this contradicts. The 2018 decree (AAS 110, 437-438) is silent on - a universal-calendar coincidence with an existing obligatory memorial, and - Normae n. 14's own third clause resolves only the AD-LIBITUM/AD-LIBITUM - case ("Si eodem die plures inscribuntur in calendario memoriae ad - libitum") -- it does not reach two OBLIGATORY memorials. So colitur's - Slug.compare tie-break ("anthony-of-padua-priest-and-doctor" sorts before - "mary-mother-of-the-church") is not this project inventing a rule; it is - the kernel's own generic, rite-agnostic fallback, applied here for lack of - a rite-specific one. This test pins that CURRENT behaviour so a future - Calendar/kernel change, a slug rename, or a future overlay cannot flip - which of the two is observed with no test going red -- the same shape of - invisible-drift risk the EF side's RG 113/[band] fidelity work (`ef-rg16a`, - register ยง6.1) eliminated on that side. *) -let test_mater_ecclesiae_anthony_tie_2011_known_arbitrary () = + RESOLVED (W3, of-known-wrongs, 2026-08-26) ON A RUBRIC, NOT COLITUR'S OWN + JUDGEMENT -- this test used to pin the OPPOSITE outcome ("KNOWN + ARBITRARY", the kernel's generic alphabetical Slug.compare tie-break, + for lack of anything rite-specific). That framing is now WRONG, not + merely superseded: colitur's own two previously-checked primary sources + really are silent (the 2018 decree, AAS 110, 437-438, on universal- + calendar coincidence; Normae n. 14's third clause, which resolves only + the AD-LIBITUM/AD-LIBITUM case, "Si eodem die plures inscribuntur in + calendario memoriae ad libitum") -- but a THIRD primary source, found by + searching the corpus rather than invented, does resolve it: the CDWDS's + own "Notification on the Memorial of the Blessed Virgin Mary, Mother of + the Church" (24 March 2018, same dicastery and signatories as the decree; + published only on vatican.va, confirmed absent from the AAS 2018 + combined index) states plainly that when this memorial "coincides with + another Memorial of a Saint or Blessed... the Memorial of the Blessed + Virgin Mary is to prevail." See {!Rite_of.Precedence_of.movable_bvm_ + memorials}'s own comment for the full Latin, the official English, and + why the same rule also covers the Immaculate Heart of Mary (the + Notification names both, by the identical link-to-a-Lord's-solemnity + argument). {!Rite_of.Precedence_of.band} now gives both a sub-rank of + 95, strictly between Tabula entries 9 and 10, so this is no longer a + coin-flip the kernel settles for lack of a rite rule -- it is the rite + rule, cited. This test still pins CURRENT behaviour, for the identical + invisible-drift reason the original comment gave; what changed is which + behaviour is correct to pin. *) +let test_mater_ecclesiae_anthony_tie_2011_resolved () = let easter_2011 = Computus.gregorian_easter 2011 in Alcotest.(check string) "2011 Easter is really 24 April (the Easter+50 trigger)" "2011-04-24" (Date.to_iso8601 easter_2011); @@ -404,18 +415,18 @@ let test_mater_ecclesiae_anthony_tie_2011_known_arbitrary () = let days = Cal.year real_of_rite real_of_layer 2010 in let june13 = Array.to_list days |> List.find (fun d -> Date.compare d.LD.date tie_date = 0) in Alcotest.(check string) - "KNOWN ARBITRARY (no rubric decides this; see this test's own header): the kernel's generic \ - alphabetical tie-break observes Anthony of Padua, not Mary Mother of the Church" - "anthony-of-padua-priest-and-doctor" (slug_of june13.LD.observed); - let mater_ecclesiae_omitted = - List.exists (fun (c, _) -> slug_of c = "mary-mother-of-the-church") june13.LD.omitted + "RESOLVED (W3): the CDWDS's own 24 March 2018 Notification (see this test's own header) makes \ + Mary, Mother of the Church prevail, not Anthony of Padua" + "mary-mother-of-the-church" (slug_of june13.LD.observed); + let anthony_omitted = + List.exists (fun (c, _) -> slug_of c = "anthony-of-padua-priest-and-doctor") june13.LD.omitted in Alcotest.(check bool) - "and Mary, Mother of the Church is recorded in `omitted`, not silently dropped -- {!Colitur_kernel \ + "and Anthony of Padua is recorded in `omitted`, not silently dropped -- {!Colitur_kernel \ .Liturgical_day.t.omitted}'s own contract" - true mater_ecclesiae_omitted; - Alcotest.(check bool) "she carries no commemoration either (OF's own admit always returns [])" false - (List.exists (fun (c, _) -> slug_of c = "mary-mother-of-the-church") june13.LD.commemorations) + true anthony_omitted; + Alcotest.(check bool) "he carries no commemoration either (OF's own admit always returns [])" false + (List.exists (fun (c, _) -> slug_of c = "anthony-of-padua-priest-and-doctor") june13.LD.commemorations) let suite = ( "Rite_of (real data: assembled Rite.t end-to-end via Calendar/Validate)", @@ -434,6 +445,6 @@ let suite = Day is a Sunday (1583)" `Quick test_holy_family_fallback_1583_now_correct; Alcotest.test_case - "KNOWN ARBITRARY pinned: Mary Mother of the Church vs Anthony of Padua tie, 2011-06-13 \ - (Easter+50)" `Quick - test_mater_ecclesiae_anthony_tie_2011_known_arbitrary ] ) + "RESOLVED (W3): Mary Mother of the Church beats Anthony of Padua's tie, 2011-06-13 \ + (Easter+50), per the CDWDS's 24 March 2018 Notification" `Quick + test_mater_ecclesiae_anthony_tie_2011_resolved ] ) |
