summaryrefslogtreecommitdiff
path: root/test/test_lang.ml
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-19 13:34:53 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-19 13:34:53 +0200
commit45bcbde502e07ad73b96039fcbb62471a3c94781 (patch)
tree515bd1465c75072ece7c70f8e2bcabd493f80417 /test/test_lang.ml
parent6762ce46af3cb12bc6ae37cda762c5d95add7903 (diff)
downloadcolitur-45bcbde502e07ad73b96039fcbb62471a3c94781.tar.gz
colitur-45bcbde502e07ad73b96039fcbb62471a3c94781.zip
feat(naming): the language table
Maps strings to strings and nothing else -- no calendars, no dates, no filesystem. That is what lets every command use it without the kernel learning about presentation. Every lookup is total, and a miss returns THE KEY rather than the empty string. A partial translation is therefore usable from its first line, and the fully-degraded case is exactly today's output (bare slugs) rather than a blank page. --raw is a real identity table, not a special case threaded through every call site: one value the whole program passes around. Reuses Overlay_ini's INI reader rather than growing a second one that would drift in its comment, quoting and trimming rules; parse_sections is exposed in the .mli for that, with no behaviour change. Fixes one defect found while running the brief's own tests rather than transcribing them blind: weekday's internal lookup key is an English day-name word (month's is already the numeral string), so on a miss it echoed that word instead of the documented numeral, breaking both the 0=Sunday convention and Lang.raw's own identity contract for weekday. weekday/month now fall back to string_of_int n directly on a miss instead of through get's generic echo-the-search-key path; month is byte-identical since its key already equals string_of_int n.
Diffstat (limited to 'test/test_lang.ml')
-rw-r--r--test/test_lang.ml85
1 files changed, 85 insertions, 0 deletions
diff --git a/test/test_lang.ml b/test/test_lang.ml
new file mode 100644
index 0000000..0b16b30
--- /dev/null
+++ b/test/test_lang.ml
@@ -0,0 +1,85 @@
+module L = Colitur_naming.Lang
+
+let ok = function Ok x -> x | Error e -> Alcotest.failf "parse: %s" e
+
+let sample =
+ "[meta]\n\
+ lang = xx\n\
+ fallback = la\n\
+ [celebration]\n\
+ ef-lent-3-monday = Feria II hebdomadae III Quadragesimae\n\
+ francis-de-sales = S. Francisci Salesii\n\
+ [weekday]\n\
+ sunday = Dominica\n\
+ monday = Feria II\n\
+ [month]\n\
+ 1 = Ianuarius\n\
+ [season]\n\
+ lent = Quadragesima\n\
+ [rank]\n\
+ class-1 = I classis\n\
+ [colour]\n\
+ white = albus\n\
+ [term]\n\
+ epistle = Epistola\n"
+
+let test_meta () =
+ let t = ok (L.of_string sample) in
+ Alcotest.(check string) "code" "xx" (L.code t);
+ Alcotest.(check (option string)) "fallback" (Some "la") (L.fallback_code t)
+
+let test_lookups () =
+ let t = ok (L.of_string sample) in
+ Alcotest.(check string) "celebration" "Feria II hebdomadae III Quadragesimae"
+ (L.celebration t "ef-lent-3-monday");
+ Alcotest.(check string) "weekday 0 is Sunday" "Dominica" (L.weekday t 0);
+ Alcotest.(check string) "month 1" "Ianuarius" (L.month t 1);
+ Alcotest.(check string) "season" "Quadragesima" (L.season t "lent");
+ Alcotest.(check string) "rank" "I classis" (L.rank t "class-1");
+ Alcotest.(check string) "colour" "albus" (L.colour t "white");
+ Alcotest.(check string) "term" "Epistola" (L.term t "epistle")
+
+(* THE load-bearing property: a missing key degrades to the key itself, never to
+ empty. A partial translation must be usable from its first line, and an
+ untranslated day must still say something a reader can act on. *)
+let test_missing_degrades_to_key () =
+ let t = ok (L.of_string sample) in
+ Alcotest.(check string) "unknown celebration" "ef-advent-1-monday"
+ (L.celebration t "ef-advent-1-monday");
+ Alcotest.(check string) "unknown colour" "rose" (L.colour t "rose");
+ Alcotest.(check string) "unknown term" "gospel" (L.term t "gospel")
+
+let test_fallback_chain () =
+ let base = ok (L.of_string "[meta]\nlang = la\n[celebration]\na = ALPHA\nb = BETA\n") in
+ let over = ok (L.of_string "[meta]\nlang = xx\nfallback = la\n[celebration]\nb = BETA-XX\n") in
+ let t = L.with_fallback over base in
+ Alcotest.(check string) "own key wins" "BETA-XX" (L.celebration t "b");
+ Alcotest.(check string) "falls back" "ALPHA" (L.celebration t "a");
+ Alcotest.(check string) "neither: the key" "c" (L.celebration t "c")
+
+(* --raw must be a real identity table, not a special case threaded through every
+ call site: one table the whole program can pass around. *)
+let test_raw_is_identity () =
+ Alcotest.(check string) "celebration" "ef-epiphany" (L.celebration L.raw "ef-epiphany");
+ Alcotest.(check string) "colour" "white" (L.colour L.raw "white");
+ Alcotest.(check string) "weekday" "0" (L.weekday L.raw 0)
+
+let test_malformed_is_error_not_crash () =
+ match L.of_string "[celebration\nbroken" with
+ | Error _ -> ()
+ | Ok _ -> Alcotest.fail "a malformed language file must be an Error, never accepted"
+
+let test_missing_meta_lang_is_error () =
+ match L.of_string "[celebration]\na = B\n" with
+ | Error _ -> ()
+ | Ok _ -> Alcotest.fail "a language file with no [meta] lang must be an Error"
+
+let suite =
+ ( "Lang",
+ [ Alcotest.test_case "meta" `Quick test_meta;
+ Alcotest.test_case "lookups" `Quick test_lookups;
+ Alcotest.test_case "missing degrades to key" `Quick test_missing_degrades_to_key;
+ Alcotest.test_case "fallback chain" `Quick test_fallback_chain;
+ Alcotest.test_case "raw is identity" `Quick test_raw_is_identity;
+ Alcotest.test_case "malformed is error" `Quick test_malformed_is_error_not_crash;
+ Alcotest.test_case "missing meta lang is error" `Quick test_missing_meta_lang_is_error ] )