diff options
Diffstat (limited to 'test/test_temporal_of.ml')
| -rw-r--r-- | test/test_temporal_of.ml | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/test/test_temporal_of.ml b/test/test_temporal_of.ml new file mode 100644 index 0000000..7560413 --- /dev/null +++ b/test/test_temporal_of.ml @@ -0,0 +1,393 @@ +(* Phase 1 of the OF rite module: Vocab_of + Temporal_of. Modelled on + test_temporal_ef.ml's own shape (example-based unit tests for the named + days and boundaries, QCheck properties for the domain-wide invariants), + scoped to what Phase 1 actually delivers -- no Rite.t exists yet, so + these properties are written directly against Temporal_of rather than + through Colitur_kernel.Validate.run (which needs a full Rite.t bundle, + Phase 2's own deliverable). *) + +module V = Rite_of.Vocab_of +module T = Rite_of.Temporal_of +module D = Colitur_kernel.Date + +let d y m dd = match D.make ~year:y ~month:m ~day:dd with Ok t -> t | Error e -> Alcotest.failf "%s" e +let iso = D.to_iso8601 + +(* ---- vocabulary ---- *) + +let test_vocab_roundtrips () = + List.iter + (fun s -> Alcotest.(check bool) "season string roundtrip" true (V.season_of_string (V.season_to_string s) = Some s)) + V.seasons; + List.iter + (fun r -> Alcotest.(check bool) "rank string roundtrip" true (V.rank_of_string (V.rank_to_string r) = Some r)) + V.ranks; + Alcotest.(check int) "five seasons" 5 (List.length V.seasons); + Alcotest.(check int) "five ranks" 5 (List.length V.ranks) + +(* ---- season boundaries (Normae n. 28/30/33/40/44 -- see temporal_of.ml's + own citations) ---- *) + +let season_str dt = V.season_to_string (T.season dt) + +let test_advent_start () = + Alcotest.(check string) "advent 2026" "2026-11-29" (iso (T.advent_start 2026)); + Alcotest.(check bool) "always a Sunday" true (D.weekday (T.advent_start 2026) = D.Sun) + +let test_seasons () = + (* Easter 2026 = 5 April; Ash Wednesday = 18 Feb; Palm Sunday = 29 Mar. *) + Alcotest.(check string) "1 Dec 2026" "advent" (season_str (d 2026 12 1)); + Alcotest.(check string) "24 Dec 2026" "advent" (season_str (d 2026 12 24)); + Alcotest.(check string) "25 Dec 2026" "christmas" (season_str (d 2026 12 25)); + Alcotest.(check string) "1 Jan 2027 (Mary Mother of God)" "christmas" (season_str (d 2027 1 1)); + (* Baptism of the Lord 2027 = 10 Jan: Christmas Time runs through it inclusive. *) + Alcotest.(check string) "10 Jan 2027 (Baptism Sunday)" "christmas" (season_str (d 2027 1 10)); + Alcotest.(check string) "11 Jan 2027 (Monday after Baptism)" "ordinary-time" (season_str (d 2027 1 11)); + Alcotest.(check string) "Ash Wed 18 Feb 2026" "lent" (season_str (d 2026 2 18)); + Alcotest.(check string) "Palm Sunday 29 Mar 2026" "lent" (season_str (d 2026 3 29)); + (* Normae n. 30 makes Holy Week Lent's own week VI: Holy Thursday, Good + Friday and Holy Saturday all stay Lent (vocab_of.ml's own citation). *) + Alcotest.(check string) "Holy Thursday 2 Apr 2026" "lent" (season_str (d 2026 4 2)); + Alcotest.(check string) "Good Friday 3 Apr 2026" "lent" (season_str (d 2026 4 3)); + Alcotest.(check string) "Holy Saturday 4 Apr 2026" "lent" (season_str (d 2026 4 4)); + Alcotest.(check string) "Easter 5 Apr 2026" "easter" (season_str (d 2026 4 5)); + Alcotest.(check string) "Pentecost 24 May 2026" "easter" (season_str (d 2026 5 24)); + Alcotest.(check string) "day after Pentecost 25 May 2026" "ordinary-time" (season_str (d 2026 5 25)); + Alcotest.(check string) "last Sunday before Advent, 22 Nov 2026" "ordinary-time" (season_str (d 2026 11 22)) + +(* ---- the three movable Christmas-time Sundays ---- *) + +let test_baptism_of_the_lord () = + Alcotest.(check string) "2026" "2026-01-11" (iso (T.baptism_of_the_lord 2026)); + Alcotest.(check string) "2027" "2027-01-10" (iso (T.baptism_of_the_lord 2027)); + Alcotest.(check string) "2024 (6 Jan is a Saturday)" "2024-01-07" (iso (T.baptism_of_the_lord 2024)); + (* 2030: 6 January itself is a Sunday. Normae n. 38 says "dominica POST + diem 6 ianuarii" -- strictly after, never 6 January itself -- so the + Baptism jumps a full week to 13 January, not 6. *) + Alcotest.(check bool) "2030: 6 Jan is a Sunday" true (D.weekday (d 2030 1 6) = D.Sun); + Alcotest.(check string) "2030 (Epiphany itself a Sunday)" "2030-01-13" (iso (T.baptism_of_the_lord 2030)); + Alcotest.(check bool) "always a Sunday" true (D.weekday (T.baptism_of_the_lord 2026) = D.Sun) + +let test_holy_family () = + Alcotest.(check string) "2026 (26 Dec is a Sunday)" "2026-12-27" (iso (T.holy_family 2026)); + (* 2022: no Sunday falls 26-31 December that year -- the n. 35(a) fallback, + 30 December, fires. *) + Alcotest.(check bool) "2022: no Sunday 26-31 Dec" + (List.for_all (fun dd -> D.weekday (d 2022 12 dd) <> D.Sun) [ 26; 27; 28; 29; 30; 31 ]) + true; + Alcotest.(check string) "2022 fallback" "2022-12-30" (iso (T.holy_family 2022)) + +let test_second_sunday_of_christmas () = + Alcotest.(check (option string)) "2026" (Some "2026-01-04") (Option.map iso (T.second_sunday_of_christmas 2026)); + (* 2024: Baptism is already 7 January (6 Jan a Saturday), so no Sunday + falls in the 2-5 January window that year -- a genuine, uncovered + [None], with no fallback in the primary text. *) + Alcotest.(check (option string)) "2024 (none)" None (Option.map iso (T.second_sunday_of_christmas 2024)) + +let test_christ_the_king () = + Alcotest.(check string) "2026" "2026-11-22" (iso (T.christ_the_king 2026)); + Alcotest.(check bool) "always a Sunday" true (D.weekday (T.christ_the_king 2026) = D.Sun); + (* Normae n. 43's own heading (temporal_of.ml's own citation): always week 34. *) + Alcotest.(check (option int)) "week 34" (Some 34) (T.week (T.christ_the_king 2026)) + +(* ---- named days ---- *) + +let named_slug dt = match T.named dt with Some (_, slug, _, _) -> slug | None -> "<none>" +let named_colour dt = match T.named dt with Some (_, _, c, _) -> Some c | None -> None +let named_rank dt = match T.named dt with Some (_, _, _, r) -> Some r | None -> None + +let test_named_feasts () = + (* Easter 2026 = 5 April. *) + Alcotest.(check string) "nativity vigil" "of-nativity-vigil" (named_slug (d 2026 12 24)); + Alcotest.(check string) "nativity" "of-nativity" (named_slug (d 2026 12 25)); + Alcotest.(check string) "mary mother of god" "of-mary-mother-of-god" (named_slug (d 2027 1 1)); + Alcotest.(check string) "epiphany" "of-epiphany" (named_slug (d 2027 1 6)); + Alcotest.(check string) "ash wednesday" "of-ash-wednesday" (named_slug (d 2026 2 18)); + Alcotest.(check string) "palm sunday" "of-palm-sunday" (named_slug (d 2026 3 29)); + Alcotest.(check string) "easter" "of-easter-sunday" (named_slug (d 2026 4 5)); + Alcotest.(check string) "ascension" "of-ascension" (named_slug (d 2026 5 14)); + Alcotest.(check string) "pentecost" "of-pentecost" (named_slug (d 2026 5 24)); + Alcotest.(check string) "trinity" "of-trinity" (named_slug (d 2026 5 31)); + Alcotest.(check string) "corpus christi" "of-corpus-christi" (named_slug (d 2026 6 4)); + Alcotest.(check string) "christ the king" "of-christ-the-king" (named_slug (d 2026 11 22)); + Alcotest.(check string) "an ordinary day is not named" "<none>" (named_slug (d 2026 7 15)) + +let test_named_colours_and_ranks () = + (* IGMR 346(b): Palm/Passion Sunday and Pentecost are red, not their + season's own colour. *) + Alcotest.(check (option string)) "palm sunday is red" (Some "red") + (Option.map Colitur_kernel.Colour.to_string (named_colour (d 2026 3 29))); + Alcotest.(check (option string)) "pentecost is red" (Some "red") + (Option.map Colitur_kernel.Colour.to_string (named_colour (d 2026 5 24))); + Alcotest.(check (option string)) "nativity is white" (Some "white") + (Option.map Colitur_kernel.Colour.to_string (named_colour (d 2026 12 25))); + (* Ash Wednesday: rank Feria, not an elevated grade -- vocab_of.ml's own + top-of-file argument for why OF's rank vocabulary keeps grade and + precedence-table position separate, unlike EF's. *) + Alcotest.(check (option string)) "ash wednesday is Feria" (Some "feria") + (Option.map V.rank_to_string (named_rank (d 2026 2 18))); + Alcotest.(check (option string)) "christmas is Sollemnitas" (Some "sollemnitas") + (Option.map V.rank_to_string (named_rank (d 2026 12 25))) + +let test_easter_octave () = + (* Easter 2026 = 5 April; the octave's six weekdays are 6-11 April. *) + Alcotest.(check string) "easter monday" "of-easter-octave-day-2" (named_slug (d 2026 4 6)); + Alcotest.(check string) "easter saturday" "of-easter-octave-day-7" (named_slug (d 2026 4 11)); + Alcotest.(check (option string)) "all octave weekdays are Sollemnitas white" (Some "sollemnitas white") + (match T.named (d 2026 4 6) with + | Some (_, _, c, r) -> Some (V.rank_to_string r ^ " " ^ Colitur_kernel.Colour.to_string c) + | None -> None); + (* The closing Sunday (12 Apr) is deliberately NOT in [named] -- it falls + through to the generic Sunday-of-Easter slug, week 2. *) + Alcotest.(check string) "closing Sunday is not in named" "<none>" (named_slug (d 2026 4 12)); + Alcotest.(check string) "closing Sunday's own slug" "of-easter-sunday-2" + (match T.sunday_slug (d 2026 4 12) with Some s -> s | None -> "<none>") + +(* ---- week numbering ---- *) + +let test_week_numbers () = + Alcotest.(check (option int)) "Advent I 2026" (Some 1) (T.week (T.advent_start 2026)); + Alcotest.(check (option int)) "Advent IV 2026" (Some 4) (T.week (D.add_days (T.advent_start 2026) 21)); + Alcotest.(check (option int)) "Christmas has no week" None (T.week (d 2026 12 27)); + (* Lent I Sunday = Easter - 42 = 2026-02-22; Ash Wednesday and the three + days after it belong to no numbered week. *) + Alcotest.(check (option int)) "Ash Wednesday has no week" None (T.week (d 2026 2 18)); + Alcotest.(check (option int)) "Lent I Sunday is week 1" (Some 1) (T.week (d 2026 2 22)); + (* Normae n. 30: Holy Week is Lent's own week VI. *) + Alcotest.(check (option int)) "Palm Sunday is Lent week 6" (Some 6) (T.week (d 2026 3 29)); + Alcotest.(check (option int)) "Holy Saturday is Lent week 6" (Some 6) (T.week (d 2026 4 4)); + Alcotest.(check (option int)) "Easter Sunday is Easter week 1" (Some 1) (T.week (d 2026 4 5)); + Alcotest.(check (option int)) "Pentecost is Easter week 8" (Some 8) (T.week (d 2026 5 24)) + +(* ---- Ordinary Time's own resumption arithmetic: the hard part, pinned + concretely (temporal_of.ml's own [ordinary_time_week] citation has the + full derivation and its evidence). ---- *) + +let test_ordinary_time_block_a () = + (* 2026: Baptism Sunday 11 Jan is week 1's own origin; the Missale's own + heading ("Dominica prima 'per annum' fit festum Baptismatis Domini") + puts that Sunday itself at week 1, so the following Monday (12 Jan, + the first day actually IN the Ordinary Time season) is ALSO week 1, + and the next Sunday (18 Jan, "Dominica II 'per annum'") is week 2. *) + Alcotest.(check (option int)) "12 Jan 2026 (Mon after Baptism) is week 1" (Some 1) (T.week (d 2026 1 12)); + Alcotest.(check (option int)) "18 Jan 2026 (Dominica II) is week 2" (Some 2) (T.week (d 2026 1 18)); + (* Ash Wednesday 2026 = 18 Feb; Shrove Tuesday (17 Feb) is block A's own + last day, week 6. *) + Alcotest.(check (option int)) "17 Feb 2026 (Shrove Tuesday) is week 6" (Some 6) (T.week (d 2026 2 17)) + +(* Every day of CIVIL year [y] -- deliberately not a liturgical-year walk + ([T.year_start y] through [T.year_start (y + 1)]): that span crosses into + the FOLLOWING civil year's January-onward Ordinary Time, governed by a + DIFFERENT year's Easter, which would silently test the wrong year's own + resumption arithmetic. *) +let civil_year_days y = + let start = d y 1 1 in + List.init (if Colitur_kernel.Date.is_leap y then 366 else 365) (fun i -> D.add_days start i) + +let test_ordinary_time_resumption () = + (* 2026: block A tops out at week 6; Pentecost is 24 May; the Monday + after (25 May) resumes at week 8, SKIPPING week 7 entirely that + year -- a concrete, verified instance of Normae n. 43's own "thirty- + three OR thirty-four weeks". *) + Alcotest.(check (option int)) "2026: 25 May (Mon after Pentecost) resumes at week 8" (Some 8) + (T.week (d 2026 5 25)); + Alcotest.(check bool) "2026: Ordinary Time week 7 never occurs (Easter's own week 7, 17-23 May, is a + different axis and does occur that year -- this checks only Ordinary-Time-season days)" true + (List.for_all + (fun dt -> T.season dt <> V.Ordinary_time || T.week dt <> Some 7) + (civil_year_days 2026)); + (* 2024: block A tops out at week 6 (Ash Wed 14 Feb, Shrove Tue 13 Feb); + Pentecost is 19 May; the Monday after (20 May) resumes at week 7 -- + NO skip that year, all 34 weeks used. *) + Alcotest.(check (option int)) "2024: 13 Feb (Shrove Tuesday) is week 6" (Some 6) (T.week (d 2024 2 13)); + Alcotest.(check (option int)) "2024: 20 May (Mon after Pentecost) resumes at week 7" (Some 7) + (T.week (d 2024 5 20)); + Alcotest.(check bool) "2024: every Ordinary Time week 1..34 occurs somewhere in civil 2024" true + (let weeks = + List.filter_map (fun dt -> if T.season dt = V.Ordinary_time then T.week dt else None) (civil_year_days 2024) + in + List.for_all (fun n -> List.mem n weeks) (List.init 34 (fun i -> i + 1))) + +(* ---- Sunday and ferial slugs ---- *) + +let sunday_slug_of dt = match T.sunday_slug dt with Some s -> s | None -> "<none>" +let slug_of dt = match T.temporal dt with t -> Colitur_kernel.Slug.to_string t.Colitur_kernel.Temporal.office.Colitur_kernel.Celebration.slug + +let test_sunday_slugs () = + Alcotest.(check string) "Advent I 2026" "of-advent-sunday-1" (sunday_slug_of (T.advent_start 2026)); + Alcotest.(check string) "Lent I 2026" "of-lent-sunday-1" (sunday_slug_of (d 2026 2 22)); + Alcotest.(check string) "18 Jan 2026 (Dominica II per annum)" "of-ordinary-time-sunday-2" (sunday_slug_of (d 2026 1 18)); + Alcotest.(check string) "holy family 2026" "of-holy-family" (sunday_slug_of (T.holy_family 2026)); + Alcotest.(check string) "baptism of the lord 2026" "of-baptism-of-the-lord" (sunday_slug_of (T.baptism_of_the_lord 2026)); + Alcotest.(check string) "2nd sunday of christmas 2026" "of-christmas-sunday-2" + (sunday_slug_of (Option.get (T.second_sunday_of_christmas 2026))) + +let test_ferial_slugs () = + Alcotest.(check string) "26 Dec 2026 (stretch 0)" "of-christmas-0-saturday" (slug_of (d 2026 12 26)); + Alcotest.(check string) "2 Jan 2027 (stretch 1)" "of-christmas-1-saturday" (slug_of (d 2027 1 2)); + Alcotest.(check string) "7 Jan 2027 (stretch 2)" "of-christmas-2-thursday" (slug_of (d 2027 1 7)); + Alcotest.(check string) "after-ashes Thursday 2026" "of-lent-after-ashes-thursday" (slug_of (d 2026 2 19)); + Alcotest.(check string) "ordinary weekday" "of-ordinary-time-2-monday" (slug_of (d 2026 1 19)); + Alcotest.(check string) "lent weekday" "of-lent-6-monday" (slug_of (d 2026 3 30)) + +let test_colours () = + let colour_of dt = (T.temporal dt).Colitur_kernel.Temporal.office.Colitur_kernel.Celebration.colour in + Alcotest.(check string) "Good Friday is red" "red" (Colitur_kernel.Colour.to_string (colour_of (d 2026 4 3))); + Alcotest.(check string) "Holy Thursday is white" "white" (Colitur_kernel.Colour.to_string (colour_of (d 2026 4 2))); + Alcotest.(check string) "ordinary time weekday is green" "green" + (Colitur_kernel.Colour.to_string (colour_of (d 2026 1 19))); + Alcotest.(check string) "advent weekday is violet" "violet" (Colitur_kernel.Colour.to_string (colour_of (d 2026 12 1))); + Alcotest.(check string) "gaudete sunday is rose" "rose" + (Colitur_kernel.Colour.to_string (colour_of (D.add_days (T.advent_start 2026) 14))); + Alcotest.(check string) "laetare sunday is rose" "rose" + (Colitur_kernel.Colour.to_string (colour_of (D.add_days (Colitur_kernel.Computus.gregorian_easter 2026) (-21)))) + +(* ---- properties over the whole domain ---- *) + +(* Walk one liturgical year, [T.year_start y] through the day before + [T.year_start (y + 1)]. Callers must keep [y] within 1583..9998 -- + [T.year_start (y + 1)] calls [Date.make] internally (via [mk]) and + raises for civil year 10000, out of the kernel's domain, the same + reason Colitur_kernel.Validate.run's own walk clamps at 9999. *) +let walk_year y = + let start = T.year_start y in + let stop = D.add_days (T.year_start (y + 1)) (-1) in + let rec go dt acc = if D.compare dt stop > 0 then List.rev acc else go (D.add_days dt 1) ((dt, T.temporal dt) :: acc) in + go start [] + +let compressed_seasons days = + List.fold_left + (fun acc (_, t) -> match acc with s :: _ when s = t.Colitur_kernel.Temporal.season -> acc | _ -> t.Colitur_kernel.Temporal.season :: acc) + [] days + |> List.rev + +let expected_season_runs = [ V.Advent; V.Christmas; V.Ordinary_time; V.Lent; V.Easter; V.Ordinary_time ] + +(* Normae n. 43-44: seasons contiguous, no gaps or overlaps, over the whole + liturgical year -- the run-length-compressed sequence is always these + six runs, in this order (Ordinary Time genuinely appears twice, per + design 2026-08-24-colitur-of-rite-module-design.md ยง4.2). *) +let prop_season_runs_contiguous y = compressed_seasons (walk_year y) = expected_season_runs + +(* Ordinary Time weeks are always 1..34 (Normae n. 43), and every + Ordinary-Time-season day carries a week number at all (the two-block + arithmetic never silently drops one). *) +let prop_ordinary_time_week_in_range y = + List.for_all + (fun (_, t) -> + match t.Colitur_kernel.Temporal.season with + | V.Ordinary_time -> ( match t.Colitur_kernel.Temporal.week with Some n -> n >= 1 && n <= 34 | None -> false) + | _ -> true) + (walk_year y) + +(* Advent always has exactly four Sundays (Normae n. 41). *) +let prop_advent_four_sundays y = + List.length + (List.filter + (fun (dt, t) -> t.Colitur_kernel.Temporal.season = V.Advent && D.weekday dt = D.Sun) + (walk_year y)) + = 4 + +(* No two dates within one liturgical year share a slug (spec's own + requirement 3; Colitur_kernel.Validate's own ["slugs"] check, for EF, + asserts the identical thing once a Rite.t exists -- this is that same + invariant, checked directly here since Phase 1 has no Rite.t yet). *) +let prop_slug_uniqueness y = + let slugs = + List.map (fun (_, t) -> Colitur_kernel.Slug.to_string t.Colitur_kernel.Temporal.office.Colitur_kernel.Celebration.slug) (walk_year y) + in + List.length slugs = List.length (List.sort_uniq String.compare slugs) + +(* [Temporal.t.weekday] always agrees with [Date.weekday] of the date it was + computed for. *) +let prop_weekday_agrees y = List.for_all (fun (dt, t) -> t.Colitur_kernel.Temporal.weekday = D.weekday dt) (walk_year y) + +(* [temporal] is a pure, deterministic function: calling it twice on the + same date gives structurally equal results. *) +let prop_determinism y = List.for_all (fun (dt, t) -> T.temporal dt = t) (walk_year y) + +(* The year is covered exactly once: three consecutive liturgical years' + walks, concatenated and sorted by date, form one strictly consecutive + run with no duplicate and no gap -- catches an off-by-one in + [advent_start]/[season] that a single year's own walk could not (it + would show up only as a wrong boundary DATE within that walk, not as a + missing or doubled civil day, since [walk_year] itself always produces a + contiguous sequence by construction). *) +let prop_liturgical_years_partition_civil_time y = + let dates = List.concat_map (fun yy -> List.map fst (walk_year yy)) [ y - 1; y; y + 1 ] in + let sorted = List.sort D.compare dates in + let rec check = function + | a :: (b :: _ as rest) -> D.to_rata b - D.to_rata a = 1 && check rest + | _ -> true + in + check sorted + +let year_gen = QCheck.int_range 1584 9997 (* leaves room for [y - 1] and [y + 1] *) + +let make_prop name f = QCheck.Test.make ~count:200 ~name year_gen f + +let prop_tests = + [ make_prop "OF: season runs are Advent,Christmas,Ordinary_time,Lent,Easter,Ordinary_time" prop_season_runs_contiguous; + make_prop "OF: Ordinary Time weeks are always 1..34" prop_ordinary_time_week_in_range; + make_prop "OF: Advent always has exactly four Sundays" prop_advent_four_sundays; + make_prop "OF: no two dates in one liturgical year share a slug" prop_slug_uniqueness; + make_prop "OF: temporal's weekday always agrees with Date.weekday" prop_weekday_agrees; + make_prop "OF: temporal is deterministic" prop_determinism; + make_prop "OF: consecutive liturgical years partition civil time with no gap or overlap" + prop_liturgical_years_partition_civil_time ] + +(* ---- the committed exhaustive sweep, mirroring test_validate.ml's own + COLITUR_EXHAUSTIVE_SWEEP convention: every year 1583..9998, not a + 200-year sample, gated behind the same environment variable so the + default `dune test` stays fast and reports the skip honestly. *) +let colitur_exhaustive_sweep_env = "COLITUR_EXHAUSTIVE_SWEEP" + +let test_exhaustive_domain_sweep () = + if Sys.getenv_opt colitur_exhaustive_sweep_env = None then Alcotest.skip () + else + for y = 1584 to 9997 do + let days = walk_year y in + if compressed_seasons days <> expected_season_runs then + Alcotest.failf "%d: season runs %s" y + (String.concat "," (List.map V.season_to_string (compressed_seasons days))); + if not (prop_ordinary_time_week_in_range y) then Alcotest.failf "%d: an Ordinary Time week is out of 1..34" y; + if not (prop_advent_four_sundays y) then Alcotest.failf "%d: Advent does not have exactly four Sundays" y; + if not (prop_slug_uniqueness y) then Alcotest.failf "%d: a slug repeats within the liturgical year" y; + if not (prop_weekday_agrees y) then Alcotest.failf "%d: a weekday disagrees with Date.weekday" y + done + +(* 1583 and 9999 themselves sit at the domain edges, where the [y-1]/[y+1] + probes the properties above use would call [T.year_start] on an + out-of-range civil year and raise -- covered separately here instead, + confirming [T.temporal] itself stays total right up to both edges. *) +let test_domain_edges_do_not_raise () = + let check_civil_year y = + let start = match D.make ~year:y ~month:1 ~day:1 with Ok t -> t | Error e -> Alcotest.failf "%s" e in + for i = 0 to 364 do + ignore (T.temporal (D.add_days start i)) + done + in + check_civil_year 1583; + check_civil_year 9999 + +let suite = + ( "temporal-of", + [ Alcotest.test_case "vocab roundtrips" `Quick test_vocab_roundtrips; + Alcotest.test_case "advent start" `Quick test_advent_start; + Alcotest.test_case "seasons" `Quick test_seasons; + Alcotest.test_case "baptism of the lord" `Quick test_baptism_of_the_lord; + Alcotest.test_case "holy family" `Quick test_holy_family; + Alcotest.test_case "second sunday of christmas" `Quick test_second_sunday_of_christmas; + Alcotest.test_case "christ the king" `Quick test_christ_the_king; + Alcotest.test_case "named feasts" `Quick test_named_feasts; + Alcotest.test_case "named colours and ranks" `Quick test_named_colours_and_ranks; + Alcotest.test_case "easter octave" `Quick test_easter_octave; + Alcotest.test_case "week numbers" `Quick test_week_numbers; + Alcotest.test_case "ordinary time block A" `Quick test_ordinary_time_block_a; + Alcotest.test_case "ordinary time resumption" `Quick test_ordinary_time_resumption; + Alcotest.test_case "sunday slugs" `Quick test_sunday_slugs; + Alcotest.test_case "ferial slugs" `Quick test_ferial_slugs; + Alcotest.test_case "colours" `Quick test_colours; + Alcotest.test_case "domain edges do not raise" `Quick test_domain_edges_do_not_raise; + Alcotest.test_case "exhaustive domain sweep" `Slow test_exhaustive_domain_sweep ] + @ List.map QCheck_alcotest.to_alcotest prop_tests ) |
