From 45bcbde502e07ad73b96039fcbb62471a3c94781 Mon Sep 17 00:00:00 2001 From: Lukasz Kasprzak Date: Wed, 19 Aug 2026 13:34:53 +0200 Subject: 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. --- test/test_lang.ml | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 test/test_lang.ml (limited to 'test/test_lang.ml') 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 ] ) -- cgit v1.3