diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/test_calendar.ml | 13 | ||||
| -rw-r--r-- | test/test_differential.ml | 10 | ||||
| -rw-r--r-- | test/test_golden.ml | 12 | ||||
| -rw-r--r-- | test/test_lectionary_ef.ml | 59 | ||||
| -rw-r--r-- | test/test_oracle.ml | 10 | ||||
| -rw-r--r-- | test/test_rite_ef.ml | 21 | ||||
| -rw-r--r-- | test/test_validate.ml | 12 |
7 files changed, 124 insertions, 13 deletions
diff --git a/test/test_calendar.ml b/test/test_calendar.ml index 2f6df6d..aeaeea0 100644 --- a/test/test_calendar.ml +++ b/test/test_calendar.ml @@ -464,6 +464,13 @@ let real_ef_layer_for_transfer_probes = if diagnostics <> [] then failwith "unexpected overlay diagnostics loading the real EF layer"; layer) +(* [Rite_ef.context] takes [~lectionary] (fix round 1, coordinator review) -- + caller-supplied, same as the sanctoral layer above. *) +let real_ef_lectionary_for_transfer_probes = + match Colitur_kernel.Lectionary.load "../data/ef/lectionary.sexp" with + | Error e -> failwith ("../data/ef/lectionary.sexp: " ^ e) + | Ok l -> l + let test_transferred_commemoration_only_capped_out_at_target_settles_cleanly () = let probe_date = match Colitur_kernel.Date_spec.fixed ~month:4 ~day:26 with Ok d -> d | Error e -> failwith e @@ -479,7 +486,11 @@ let test_transferred_commemoration_only_capped_out_at_target_settles_cleanly () let augmented_layer = Colitur_kernel.Layer.set real_ef_layer_for_transfer_probes probe in (* Liturgical year "2010" (Advent 2010 -- eve of Advent 2011) covers both 25 and 26 April 2011. *) - let year = C.year Rite_ef.context augmented_layer 2010 in + let year = + C.year + (Rite_ef.context ~lectionary:real_ef_lectionary_for_transfer_probes) + augmented_layer 2010 + in let find_date target = match Array.to_list year |> List.find_opt (fun d -> D.compare d.LD.date target = 0) with | Some d -> d diff --git a/test/test_differential.ml b/test/test_differential.ml index a4e550d..0b68f90 100644 --- a/test/test_differential.ml +++ b/test/test_differential.ml @@ -207,6 +207,13 @@ let real_layer () = (List.map Overlay.diagnostic_to_string diagnostics); layer +(* [Rite_ef.context] takes [~lectionary] (fix round 1, coordinator review) -- + caller-supplied, same as [real_layer] above. *) +let real_lectionary () = + match Colitur_kernel.Lectionary.load "../data/ef/lectionary.sexp" with + | Ok l -> l + | Error e -> Alcotest.failf "../data/ef/lectionary.sexp: failed to load: %s" e + (* --- The seven-column row both streams share (commemorations excluded, --- *) (* limit 1 above). *) type row = { @@ -255,9 +262,10 @@ let lectio_rows () = List.map row_of_line (read_lines fixture_path) test_rite_ef.ml already made for [real_layer] above. *) let colitur_rows_2005_2050 () = let layer = real_layer () in + let rite = Rite_ef.context ~lectionary:(real_lectionary ()) in let by_rata : (int, (V.season, V.rank) LD.t) Hashtbl.t = Hashtbl.create 20000 in for y = 2004 to 2050 do - let days = Cal.year Rite_ef.context layer y in + let days = Cal.year rite layer y in Array.iter (fun (d : (V.season, V.rank) LD.t) -> Hashtbl.replace by_rata (Date.to_rata d.LD.date) d) days done; let mk y m d = match Date.make ~year:y ~month:m ~day:d with Ok t -> t | Error e -> failwith e in diff --git a/test/test_golden.ml b/test/test_golden.ml index a873738..188e37f 100644 --- a/test/test_golden.ml +++ b/test/test_golden.ml @@ -63,6 +63,7 @@ module V = Rite_ef.Vocab_ef runs from _build/default/test/). *) let sanctoral_path = "../data/ef/sanctoral.sexp" let adjustments_path = "../data/ef/adjustments.sexp" +let lectionary_path = "../data/ef/lectionary.sexp" (* Loaded once at module init, same convention test_validate.ml's own [real_ef_layer] uses (not test_oracle.ml/test_differential.ml's @@ -83,12 +84,21 @@ let real_ef_layer = (String.concat "; " (List.map Overlay.diagnostic_to_string diagnostics))); layer) +(* [Rite_ef.context] takes [~lectionary] (fix round 1, coordinator review) -- + caller-supplied, same as [real_ef_layer] above. *) +let real_ef_lectionary = + match Colitur_kernel.Lectionary.load lectionary_path with + | Error e -> failwith (Printf.sprintf "%s: failed to load: %s" lectionary_path e) + | Ok l -> l + +let real_ef_rite = Rite_ef.context ~lectionary:real_ef_lectionary + let mk y m d = match Date.make ~year:y ~month:m ~day:d with Ok t -> t | Error e -> failwith e (* [Cal.day] recomputes its whole liturgical year on every call (calendar.mli -- "pure, no cache"); acceptable here, same as test_rite_ef.ml's own use of [Cal.year] for a handful of dates, not a hot loop. *) -let fetch y m d = Cal.day Rite_ef.context real_ef_layer (mk y m d) +let fetch y m d = Cal.day real_ef_rite real_ef_layer (mk y m d) let slug_s (c : V.rank Cel.t) = Slug.to_string c.Cel.slug let rank_s (c : V.rank Cel.t) = V.rank_to_string c.Cel.rank diff --git a/test/test_lectionary_ef.ml b/test/test_lectionary_ef.ml index a1a324f..b1c9e7d 100644 --- a/test/test_lectionary_ef.ml +++ b/test/test_lectionary_ef.ml @@ -10,6 +10,7 @@ open Rite_ef declares both as deps. *) let sanctoral_path = "../data/ef/sanctoral.sexp" let adjustments_path = "../data/ef/adjustments.sexp" +let lectionary_path = "../data/ef/lectionary.sexp" let real_layer () = let layer = @@ -27,6 +28,18 @@ let real_layer () = (List.map Overlay.diagnostic_to_string diagnostics); layer +(* [Rite_ef.context] takes [~lectionary] (fix round 1, coordinator review): + the module used to load data/ef/lectionary.sexp itself, as a side effect + of being linked, which meant a bare `dune build` produced a `colitur` + that died at startup on EVERY subcommand -- including ones (`easter`) + that touch no lectionary data at all -- the moment that file was absent + from the default build target. Caller-supplied now, same as the + sanctoral layer above. *) +let real_lectionary () = + match Lectionary.load lectionary_path with + | Ok l -> l + | Error e -> Alcotest.failf "%s: failed to load: %s" lectionary_path e + (* [Calendar.day] (not [year]): the liturgical year "opening in civil year y" is Advent-anchored (RG 61), so [Calendar.year _ _ 2030] covers Advent 2030 through November 2031 -- it would never contain 13 January 2030, which @@ -35,7 +48,7 @@ let real_layer () = let day y m d = let date = match Date.make ~year:y ~month:m ~day:d with | Ok x -> x | Error e -> Alcotest.fail e in - Calendar.day Rite_ef.context (real_layer ()) date + Calendar.day (Rite_ef.context ~lectionary:(real_lectionary ())) (real_layer ()) date let refs (ld : _ Liturgical_day.t) = List.map (fun c -> c.Citation.reference) ld.citations @@ -63,6 +76,48 @@ let test_step2_lenten_feria_has_its_own () = [ "Ezech 34:11-16"; "Matt 25:31-46" ] (refs (day 2026 2 23)) +(* Fix round 1 (coordinator review, Important finding 2): neither test above + actually distinguishes step 1 from step 2 -- both survive swapping the + chain order. 13 January 2026's temporal slug (ef-time-after-epiphany-1- + tuesday) has no lectionary entry at all, so a swapped chain falls through + to the same []-then-sanctoral answer; 23 February 2026's OBSERVED + celebration IS the temporal office (ef-lent-1-monday carries no sanctoral + entry of its own), so [observed.citations] and the temporal-slug lookup + are the same lookup wearing two names -- order is a no-op either way. + + 19 March 2026 (St Joseph) genuinely needs step 1 to run FIRST: the + observed celebration (Joseph, Class1, a real sanctoral entry with its own + citations) and the day's own temporal slug (ef-lent-4-thursday, ALSO a + real lectionary entry, with different citations) disagree. Verified + directly against the real data (not transcribed): both value pairs below + were read off the actual resolved day and the actual + data/ef/lectionary.sexp entry, not assumed. *) +let test_step1_wins_over_a_competing_step2_entry () = + Alcotest.(check (list string)) + "19 March 2026: Joseph's own proper wins over Lent 4 Thursday's, which the temporal slug also has" + [ "Ecclus 45:1-6"; "Matt 1:18-21" ] + (refs (day 2026 3 19)) + +(* Fix round 1 (coordinator review): the mirror-image pin for step 2 -- a day + whose OBSERVED celebration carries no citations of its own (Holy Family, + synthesised by [Temporal_ef] itself, not sourced from + data/ef/sanctoral.sexp, so [Celebration.citations] is empty) falls + through to the temporal slug, and the temporal slug's own lectionary + entry is genuinely Holy Family's Mass, not the Baptism's -- RG 112(a) + (see test_golden.ml's own [test_holy_family_excludes_baptism_2030]) + excludes the Baptism from this day entirely, so there is no sanctoral + citation anywhere to fall back to even in principle. Verified directly + against the real data. *) +let test_step2_holy_family_reached_through_temporal_slug () = + Alcotest.(check (list string)) + "13 January 2030: Holy Family reached via the temporal slug, the Baptism entirely absent" + [ "Col 3:12-17"; "Luke 2:42-52" ] + (refs (day 2030 1 13)) + let suite = [ ("step 1: sanctoral proper", `Quick, test_step1_sanctoral_proper); - ("step 2: own temporal proper", `Quick, test_step2_lenten_feria_has_its_own) ] + ("step 2: own temporal proper", `Quick, test_step2_lenten_feria_has_its_own); + ("step 1 wins over a competing step 2 entry", `Quick, + test_step1_wins_over_a_competing_step2_entry); + ("step 2: Holy Family reached through the temporal slug, Baptism absent", `Quick, + test_step2_holy_family_reached_through_temporal_slug) ] diff --git a/test/test_oracle.ml b/test/test_oracle.ml index c1d0e6a..e1e4184 100644 --- a/test/test_oracle.ml +++ b/test/test_oracle.ml @@ -220,6 +220,13 @@ let real_layer () = (List.map Overlay.diagnostic_to_string diagnostics); layer +(* [Rite_ef.context] takes [~lectionary] (fix round 1, coordinator review) -- + caller-supplied, same as [real_layer] above. *) +let real_lectionary () = + match Colitur_kernel.Lectionary.load "../data/ef/lectionary.sexp" with + | Ok l -> l + | Error e -> Alcotest.failf "../data/ef/lectionary.sexp: failed to load: %s" e + (* ---------------------------------------------------------------------- *) (* The oracle side: one line per day, as tools/extract_missalemeum_oracle *) (* .py's own header documents. *) @@ -321,9 +328,10 @@ let en = Lang.of_string_exn "en" let colitur_rows_2026_2027 () = let layer = real_layer () in + let rite = Rite_ef.context ~lectionary:(real_lectionary ()) in let by_rata : (int, (V.season, V.rank) LD.t) Hashtbl.t = Hashtbl.create 800 in for y = 2025 to 2027 do - let days = Cal.year Rite_ef.context layer y in + let days = Cal.year rite layer y in Array.iter (fun (d : (V.season, V.rank) LD.t) -> Hashtbl.replace by_rata (Date.to_rata d.LD.date) d) days done; let mk y m d = match Date.make ~year:y ~month:m ~day:d with Ok t -> t | Error e -> failwith e in diff --git a/test/test_rite_ef.ml b/test/test_rite_ef.ml index 67b42ce..7680545 100644 --- a/test/test_rite_ef.ml +++ b/test/test_rite_ef.ml @@ -49,6 +49,15 @@ let real_layer () = (List.map Overlay.diagnostic_to_string diagnostics); layer +(* [Rite_ef.context] takes [~lectionary] (fix round 1, coordinator review) -- + caller-supplied, same as [real_layer] above. *) +let real_lectionary () = + match Colitur_kernel.Lectionary.load "../data/ef/lectionary.sexp" with + | Ok l -> l + | Error e -> Alcotest.failf "../data/ef/lectionary.sexp: failed to load: %s" e + +let context () = Rite_ef.context ~lectionary:(real_lectionary ()) + let slug_of (c : V.rank Cel.t) = Slug.to_string c.Cel.slug (* Finding 3: the suppression's ONLY observable effect is on 24 December's @@ -64,7 +73,7 @@ let slug_of (c : V.rank Cel.t) = Slug.to_string c.Cel.slug alone structurally cannot. *) let test_vigil_of_christmas_suppressed () = let layer = real_layer () in - let days = Cal.year Rite_ef.context layer 2026 in + let days = Cal.year (context ()) layer 2026 in let christmas_eve = Array.to_list days |> List.find (fun d -> Date.compare d.LD.date (mk 2026 12 24) = 0) in @@ -251,7 +260,7 @@ let test_barbara_added () = Alcotest.(check bool) "barbara: Red (RG124(e), \"Virg. et Mart.\")" true (e.Layer.cel.Cel.colour = Colour.Red); Alcotest.(check bool) "barbara: appears as a commemoration on 4 December in real output" true - (let days = Cal.year Rite_ef.context layer 2026 in + (let days = Cal.year (context ()) layer 2026 in Array.to_list days |> List.exists (fun d -> Date.compare d.LD.date (mk 2026 12 4) = 0 @@ -281,7 +290,7 @@ let test_transfer_search_does_not_raise_at_domain_ceiling () = in let layer, _diagnostics = Overlay.apply layer overlay in (* Must not raise -- the whole point of the fix. *) - let days = Cal.year Rite_ef.context layer 9999 in + let days = Cal.year (context ()) layer 9999 in Alcotest.(check bool) "year 9999 resolves without raising, even with an impeded Christmas Day" true (Array.length days > 0); let impeder_placed_or_recorded = @@ -350,7 +359,7 @@ let test_no_transfer_lands_in_easter_octave () = let violations = ref [] in List.iter (fun y -> - let days = Cal.year Rite_ef.context layer y in + let days = Cal.year (context ()) layer y in Array.iter (fun (d : (V.season, V.rank) LD.t) -> (match d.LD.transferred_in with @@ -388,7 +397,7 @@ let test_major_litanies_transfers_inside_easter_octave_exactly_when_rg80_require let landings = ref [] in List.iter (fun y -> - let days = Cal.year Rite_ef.context layer y in + let days = Cal.year (context ()) layer y in Array.iter (fun (d : (V.season, V.rank) LD.t) -> List.iter @@ -491,7 +500,7 @@ let test_maurice_thomas_band_fidelity_end_to_end () = is reached via [Cal.year ... 2026], not 2027 -- the same [y-1]/straddling indexing test_oracle.ml's own header comment already documents for exactly this reason. *) - let days = Cal.year Rite_ef.context layer 2026 in + let days = Cal.year (context ()) layer 2026 in let day = Array.to_list days |> List.find (fun d -> Date.compare d.LD.date (mk 2027 9 22) = 0) in Alcotest.(check string) "the September Ember Wednesday office itself is observed (band entry 18)" "ef-september-ember-wed" (slug_of day.LD.observed); diff --git a/test/test_validate.ml b/test/test_validate.ml index 9fa3c5c..8e63a1b 100644 --- a/test/test_validate.ml +++ b/test/test_validate.ml @@ -15,6 +15,7 @@ module T = Rite_ef.Temporal_ef of the (test ...) stanza. *) let sanctoral_path = "../data/ef/sanctoral.sexp" let adjustments_path = "../data/ef/adjustments.sexp" +let lectionary_path = "../data/ef/lectionary.sexp" (* Loaded once at module init, not per call: [run] below is called by every test and by the 200-sample property, and Calendar.year's own resolution @@ -34,7 +35,16 @@ let real_ef_layer = (String.concat "; " (List.map Overlay.diagnostic_to_string diagnostics))); layer) -let run year = Val.run Rite_ef.context real_ef_layer ~year +(* [Rite_ef.context] takes [~lectionary] (fix round 1, coordinator review) -- + caller-supplied, same as [real_ef_layer] above. *) +let real_ef_lectionary = + match Colitur_kernel.Lectionary.load lectionary_path with + | Error e -> failwith (Printf.sprintf "%s: failed to load: %s" lectionary_path e) + | Ok l -> l + +let real_ef_rite = Rite_ef.context ~lectionary:real_ef_lectionary + +let run year = Val.run real_ef_rite real_ef_layer ~year let check_year year = match run year with |
