diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/cli.t | 38 | ||||
| -rw-r--r-- | test/test_overlay.ml | 66 |
2 files changed, 100 insertions, 4 deletions
@@ -17,7 +17,7 @@ A year outside the supported domain is rejected (exit 2): No/garbage arguments give a usage error (exit 2): $ colitur - colitur: usage: colitur easter <year> | colitur temporal <year> | colitur day <year> | colitur readings <year> (try: colitur --help) + colitur: usage: colitur easter <year> | colitur temporal <year> | colitur day <year> | colitur readings <year> | colitur check FILE | colitur new-overlay (try: colitur --help) [2] The EF temporal cycle for a year, one line per day: @@ -327,17 +327,49 @@ A flag needing a value, given none: $ colitur day 2026 --overlay colitur: --overlay needs a file path - colitur: usage: colitur easter <year> | colitur temporal <year> | colitur day <year> | colitur readings <year> (try: colitur --help) + colitur: usage: colitur easter <year> | colitur temporal <year> | colitur day <year> | colitur readings <year> | colitur check FILE | colitur new-overlay (try: colitur --help) [2] An unknown option is rejected rather than treated as a positional word: $ colitur day 2026 --diocese colitur: unknown option --diocese - colitur: usage: colitur easter <year> | colitur temporal <year> | colitur day <year> | colitur readings <year> (try: colitur --help) + colitur: usage: colitur easter <year> | colitur temporal <year> | colitur day <year> | colitur readings <year> | colitur check FILE | colitur new-overlay (try: colitur --help) [2] The shipped example overlay is runnable documentation, and it must actually load -- an example that silently rotted would be worse than none. The cram sandbox cannot reach data/, so the assertion that it loads and applies lives in test_lectionary_ef.ml, which reads it from the source tree directly. + +`new-overlay` prints a starter file to stdout for redirection, and what it +prints must itself be valid -- a template that does not load is worse than no +template, because it teaches the wrong shape. Round-tripped here rather than +pinned line by line, so editing the template's prose does not fail this test +while a syntax error in it still does: + + $ colitur new-overlay > starter.sexp + $ colitur check starter.sexp + starter.sexp: ok -- overlay my-parish, 2 directive(s): 2 add, 0 suppress, 0 replace, 0 edit + every directive found its target + add my-local-patron + add my-dedication + +`check` exits 2 when a directive matches nothing, so it is usable in a +Makefile or a pre-commit hook, not merely readable: + + $ printf '((id p) (directives ((Suppress no-such-slug))))' > bad.sexp + $ colitur check bad.sexp + bad.sexp: ok -- overlay p, 1 directive(s): 0 add, 1 suppress, 0 replace, 0 edit + MATCHED NOTHING: overlay p: suppress no-such-slug: slug not present; nothing to suppress + suppress no-such-slug + [2] + +`citations` and `layer` may be omitted from an added celebration; they default +to empty and to the overlay's own id: + + $ printf '((id tiny) (directives ((Add ((date (Fixed (month 5) (day 20))) (cel ((slug tiny-feast) (names ((en "Tiny"))) (rank Class3) (status Feast) (colour White) (subject Saint))))))))' > tiny.sexp + $ colitur check tiny.sexp + tiny.sexp: ok -- overlay tiny, 1 directive(s): 1 add, 0 suppress, 0 replace, 0 edit + every directive found its target + add tiny-feast diff --git a/test/test_overlay.ml b/test/test_overlay.ml index f3d1aee..12eb56d 100644 --- a/test/test_overlay.ml +++ b/test/test_overlay.ml @@ -307,9 +307,73 @@ let prop_deterministic = let a, da = O.apply (base ()) o and b, db = O.apply (base ()) o in a = b && da = db) +(* Overlay ERGONOMICS (branch overlay-ergonomics). A user-supplied overlay is + hand-written, unlike every other sexp this engine reads, and two of + [Celebration.t]'s eight fields carry no information a user could supply + meaningfully: [citations] is always empty for a local feast (colitur emits + citations from the lectionary, not the calendar), and [layer] just repeats + the overlay file's own [id]. Requiring both meant the commonest first + mistake -- omitting them -- produced + "lib/kernel/celebration.ml.t_of_sexp: the following record elements were + undefined: citations layer", which names a source file the author will + never open. + + {!Overlay.load} now fills both in before the strict parser runs. This is + deliberately scoped to OVERLAY loading: {!Layer.load}, which reads the + SHIPPED sanctoral, stays strict, because that data is the project's own and + a missing field there is a defect rather than a convenience. *) +let overlay_missing_citations_and_layer = + {|((id my-parish) + (directives + ((Add + ((date (Fixed (month 5) (day 20))) + (cel + ((slug st-example) + (names ((en "St Example"))) + (rank Class3) (status Feast) (colour White) (subject Saint))))))))|} + +let test_overlay_defaults_citations_and_layer () = + with_temp_file (fun path -> + let oc = open_out path in + output_string oc overlay_missing_citations_and_layer; + close_out oc; + match O.load rank_of_sexp path with + | Error e -> Alcotest.failf "expected the overlay to load, got: %s" e + | Ok o -> + Alcotest.(check string) "id" "my-parish" o.O.id; + (match o.O.directives with + | [ O.Add e ] -> + Alcotest.(check string) "citations defaulted to empty" "0" + (string_of_int (List.length e.L.cel.Cel.citations)); + (* [layer] defaults to the overlay's own id: the value the author + would have typed, so a later Suppress/Edit naming it works. *) + Alcotest.(check string) "layer defaulted to the overlay id" "my-parish" + e.L.cel.Cel.layer + | _ -> Alcotest.fail "expected exactly one Add directive")) + +(* An explicit [layer] must still win: an overlay may deliberately claim a + different layer name from its own id (e.g. one file shipping two logical + layers), and defaulting must not overwrite a stated value. *) +let test_overlay_explicit_layer_wins () = + with_temp_file (fun path -> + let oc = open_out path in + output_string oc + (replace_first ~sub:"(subject Saint)" + ~by:"(subject Saint) (layer stated-explicitly)" overlay_missing_citations_and_layer); + close_out oc; + match O.load rank_of_sexp path with + | Ok { O.directives = [ O.Add e ]; _ } -> + Alcotest.(check string) "explicit layer preserved" "stated-explicitly" e.L.cel.Cel.layer + | Ok _ -> Alcotest.fail "expected exactly one Add directive" + | Error e -> Alcotest.failf "expected the overlay to load, got: %s" e) + let suite = ( "Layer/Overlay", - [ Alcotest.test_case "layer basics" `Quick test_layer_basics; + [ Alcotest.test_case "overlay: citations and layer default when omitted" `Quick + test_overlay_defaults_citations_and_layer; + Alcotest.test_case "overlay: an explicit layer is not overwritten" `Quick + test_overlay_explicit_layer_wins; + Alcotest.test_case "layer basics" `Quick test_layer_basics; Alcotest.test_case "layer by-date index" `Quick test_layer_index; Alcotest.test_case "layer by-date index accumulates same date" `Quick test_layer_index_same_date; |
