diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-20 14:59:03 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-08-20 14:59:03 +0200 |
| commit | 56e36188c6afc208b96b15b9a1d3d62bbbe2f705 (patch) | |
| tree | 5d60c75005c36ab9595a70ef06f261ed9b7efed4 /test/test_citation.ml | |
| parent | a4eb9ed3cd93cbac493a873991dd5817906aad56 (diff) | |
| download | colitur-56e36188c6afc208b96b15b9a1d3d62bbbe2f705.tar.gz colitur-56e36188c6afc208b96b15b9a1d3d62bbbe2f705.zip | |
feat(citation): parse citations into structure
The parsed form is a book and a LIST of chapter-parts: the data cites
across chapters and lists disjoint verse ranges within one.
Two rules the shipped data forces and that are not obvious: a
semicolon-separated part may inherit the previous chapter rather than
restate it, and a chapter may be separated from its verses by a comma.
Diffstat (limited to 'test/test_citation.ml')
| -rw-r--r-- | test/test_citation.ml | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/test/test_citation.ml b/test/test_citation.ml index 7bc8ac7..75fffe8 100644 --- a/test/test_citation.ml +++ b/test/test_citation.ml @@ -146,3 +146,57 @@ let suite = test_tokens_has_no_duplicate_spelling; Alcotest.test_case "every data file token resolves" `Quick test_every_data_file_token_resolves ] ) + +module P = Colitur_citation.Parse + +(* Render a parse back to a debug string so a test can assert shape + compactly: "book|chapter:v-v,v-v|chapter:v". *) +let show (t : P.t) = + let range (r : P.verse_range) = + match r.P.last with + | None -> string_of_int r.P.first + | Some l -> Printf.sprintf "%d-%d" r.P.first l + in + let part (p : P.part) = + Printf.sprintf "%d:%s" p.P.chapter + (String.concat "," (List.map range p.P.verses)) + in + B.to_string t.P.book ^ "|" ^ String.concat "|" (List.map part t.P.parts) + +let parses input expected () = + match P.parse input with + | Error e -> Alcotest.failf "%s did not parse: %s" input e + | Ok t -> Alcotest.(check s) input expected (show t) + +let test_rejects_unknown_book () = + Alcotest.(check bool) "error" true (Result.is_error (P.parse "Nonesuch 1:1")) + +let test_rejects_garbage () = + List.iter + (fun bad -> + Alcotest.(check bool) bad true (Result.is_error (P.parse bad))) + [ ""; "Luke"; "Luke :"; "Luke 1:"; "Luke abc:1" ] + +let parse_suite = + [ ("simple", `Quick, parses "1 Cor 11:20-32" "corinthians_1|11:20-32"); + ("trailing period", `Quick, parses "1 John 3:13-18." "john_1|3:13-18"); + ("single verse", `Quick, parses "Luke 2:21" "luke|2:21"); + ("dotted spelling", `Quick, parses "Isa. 1:16-19" "isaiah|1:16-19"); + ("verse list", `Quick, parses "Acts 10:34, 42-48" "acts|10:34,42-48"); + ("new chapter", `Quick, parses "1 Cor. 9:24-27; 10:1-5" + "corinthians_1|9:24-27|10:1-5"); + (* Rule 1: the second part names no chapter, so it inherits chapter 2. *) + ("inherited chapter", `Quick, parses "Joel 2:23-24; 26-27" "joel|2:23-24|2:26-27"); + (* Rule 2: comma separates chapter from verses in the second part. *) + ("comma chapter", `Quick, parses "Mark 14:32-72; 15, 1-46" + "mark|14:32-72|15:1-46"); + ("four ranges", `Quick, parses "Dan 13:1-9, 15-17, 19-30, 33-62." + "daniel|13:1-9,15-17,19-30,33-62"); + ("mixed", `Quick, parses "Num 20:1, 3; 6-13." "numbers|20:1,3|20:6-13"); + ("trailing semicolon", `Quick, parses "1 Cor 1:18-25; 1:30;" + "corinthians_1|1:18-25|1:30"); + ("four parts", `Quick, parses "Eccli 24:5; 14:7; 14:9-11; 24:30-31" + "ecclesiasticus|24:5|14:7|14:9-11|24:30-31"); + ("modern name, vulgate id", `Quick, parses "Rev 12:1" "apocalypse|12:1"); + ("unknown book", `Quick, test_rejects_unknown_book); + ("garbage", `Quick, test_rejects_garbage) ] |
