aboutsummaryrefslogtreecommitdiff
path: root/test/test_precedence.ml
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-11 19:23:06 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-11 19:23:06 +0200
commit7e29712aadf2436b83de2d5e8d1f98b30d2f6d59 (patch)
tree7d883df1b0a412164c49912c414b5fa50645a6d0 /test/test_precedence.ml
parent19d5bbaab8fbf40f6fa6de8906169e3bb7144e1f (diff)
downloadcolitur-7e29712aadf2436b83de2d5e8d1f98b30d2f6d59.tar.gz
colitur-7e29712aadf2436b83de2d5e8d1f98b30d2f6d59.zip
test(precedence): strengthen accounting test, cover order and empty sanctoral
The accounting test only checked bucket lengths, which a mutant satisfies by duplicating a candidate across two buckets while dropping another entirely. Replace it with a sorted slug-set comparison (Alcotest.slist), which a duplicate-or-missing slug both fail. Add two cases the brief's three properties call for but nothing exercised: input-order independence (permuting the sanctoral list must not change the outcome) and a temporal-only day (empty sanctoral list), the case the temporal/sanctoral split exists to make safe.
Diffstat (limited to 'test/test_precedence.ml')
-rw-r--r--test/test_precedence.ml46
1 files changed, 39 insertions, 7 deletions
diff --git a/test/test_precedence.ml b/test/test_precedence.ml
index 39be06c..369c490 100644
--- a/test/test_precedence.ml
+++ b/test/test_precedence.ml
@@ -56,15 +56,45 @@ let test_commemoration_only_never_observed () =
Alcotest.(check (list string)) "suppressed commemorated" [ "suppressed" ]
(List.map (fun (c, _) -> slug_of c) r.P.commemorations)
-(* Anything the admit limit drops is recorded in `omitted`, never dropped silently. *)
+(* Anything the admit limit drops is recorded in `omitted`, never dropped silently
+ -- and each input candidate lands in exactly one bucket, not zero or two. *)
let test_nothing_silently_lost () =
let r = P.resolve rules ctx ~temporal:(cand ~origin:P.Temporal ~rank:Hi "sunday")
~sanctoral:[ cand ~rank:Lo "a"; cand ~rank:Lo "b"; cand ~rank:Lo "c" ] in
- Alcotest.(check int) "two admitted" 2 (List.length r.P.commemorations);
- Alcotest.(check int) "one recorded as omitted" 1 (List.length r.P.omitted);
- let total = 1 + List.length r.P.commemorations + List.length r.P.deferred
- + List.length r.P.omitted in
- Alcotest.(check int) "every candidate accounted for" 4 total
+ let bucketed =
+ (slug_of r.P.observed
+ :: List.map (fun (c, _) -> slug_of c) r.P.commemorations)
+ @ List.map slug_of r.P.deferred
+ @ List.map (fun (c, _) -> slug_of c) r.P.omitted
+ in
+ Alcotest.(check (slist string compare)) "every candidate appears exactly once"
+ [ "a"; "b"; "c"; "sunday" ] bucketed;
+ Alcotest.(check int) "two admitted" 2 (List.length r.P.commemorations)
+
+(* The result must not depend on the order sanctoral candidates arrive in;
+ ties break on slug, not on list position. *)
+let test_order_independent () =
+ let a = cand ~rank:Lo "a" and b = cand ~rank:Lo "b" and c = cand ~rank:Lo "c" in
+ let t = cand ~origin:P.Temporal ~rank:Hi "sunday" in
+ let observed_for sanctoral = slug_of (P.resolve rules ctx ~temporal:t ~sanctoral).P.observed in
+ let comms_for sanctoral =
+ List.map (fun (x, _) -> slug_of x) (P.resolve rules ctx ~temporal:t ~sanctoral).P.commemorations
+ in
+ List.iter
+ (fun perm ->
+ Alcotest.(check string) "same observed" (observed_for [ a; b; c ]) (observed_for perm);
+ Alcotest.(check (list string)) "same commemorations" (comms_for [ a; b; c ]) (comms_for perm))
+ [ [ c; b; a ]; [ b; a; c ]; [ c; a; b ] ]
+
+(* The case the "pass temporal separately" design exists to make safe: a day
+ with no sanctoral candidate at all. *)
+let test_temporal_only_day () =
+ let r = P.resolve rules ctx ~temporal:(cand ~origin:P.Temporal ~rank:Lo "feria")
+ ~sanctoral:[] in
+ Alcotest.(check string) "feria observed" "feria" (slug_of r.P.observed);
+ Alcotest.(check int) "no commemorations" 0 (List.length r.P.commemorations);
+ Alcotest.(check int) "nothing deferred" 0 (List.length r.P.deferred);
+ Alcotest.(check int) "nothing omitted" 0 (List.length r.P.omitted)
let suite =
( "Precedence",
@@ -73,4 +103,6 @@ let suite =
Alcotest.test_case "loser dispositions" `Quick test_loser_dispositions;
Alcotest.test_case "commemoration-only never observed" `Quick
test_commemoration_only_never_observed;
- Alcotest.test_case "nothing silently lost" `Quick test_nothing_silently_lost ] )
+ Alcotest.test_case "nothing silently lost" `Quick test_nothing_silently_lost;
+ Alcotest.test_case "order independent" `Quick test_order_independent;
+ Alcotest.test_case "temporal-only day" `Quick test_temporal_only_day ] )