;; Tests of .bst parsing and functionality, using only the externally ;; visible functions. For more thorough tests, see test-btxhak-internal.scm ;; ;; This file is part of Beastie ;; SPDX-FileCopyrightText: 2023 Norman Gray ;; SPDX-License-Identifier: BSD-2-Clause (module "s7unit.scm" 'bst 'unicode 'subtex) ;; the following locale setting is a no-op when we're not using ;; ICU, but with it, the POSIX order ensures that uppercase ;; consistently sorts before lower (unicode-set-locale! "en_US_POSIX") (test-suite "Working .bst programs" (assert-equal (with-output-to-string (λ () (process-bibs/bst 'all '("bib-t01-simple.bib") "bst-t01-minimal.bst"))) #""" \bibitem{T1-2:UPPERCASE} Type: book -- authors=Another Author; title=Book title; date=Januarius 2000; global.var=1. \bibitem{t1-1} Article: authors=Sample {Author} and First Last, title=Article title, date=1999. \bibitem{t3} Article: authors=, title=, date=9999. \bibitem{t4} Article: authors=, title=, date=9999. """) ;; the same, but with an explicit list of symbol citations (assert-equal (with-output-to-string (λ () (process-bibs/bst '(t1-1) '("bib-t01-simple.bib") "bst-t01-minimal.bst"))) #""" \bibitem{t1-1} Article: authors=Sample {Author} and First Last, title=Article title, date=1999. """)) (test-suite "Failing .bst programs" ;; a program which tries to dereference an empty stack (catch #t (λ () (process-bibs/bst 'all '("bib-t01-simple.bib") ;; string starting "!" means literal .bst ;; see process-bibs/bst special case "!\n\nfunction{test1}\n{#1 +}\nread\niterate{test1}\n") (eprintf "bad succeed (1)~%") (assert-fail "bst program should have failed (1)")) (λ (tag info) (assert-equal tag 'beastie) ;; the precise error message doesn't much matter, nor the ;; content of the 'calls item in the assoc, but they should ;; be something like this (assert-equal info '("stack empty when calling +" ((subtag . bst-stack) (calls . "+ <- test1:4 <- iterate:6")))))) ;; wrong type argument to chr.to.int$, detected inside an inner block (catch #t (λ () (process-bibs/bst 'all '("bib-t01-simple.bib") "!% stuff\n\nfunction{test2}\n{ #1 #1 =\n {#1 chr.to.int$}\n {\"\" write$ }\nif$}\nread\niterate{test2}") (eprintf "bad succeed (2)~%") (assert-fail "bst program should have failed (2)")) (λ (tag info) (assert-equal tag 'beastie) (assert-equal info '("calling chr.to.int$ with stack (1): expected ustring? for item 1, got 1" ((subtag . bst-stack) (calls . "chr.to.int$ <- {}:5 <- test2:4 <- iterate:9"))))))) (test-suite "string manipulations" ;(other than SRFI ones) ;; The following tests of *case-string/bst overlap with the tests of ;; change.case$ in test-btxhak.scm, but have a different interface. ;; Those tests also confirm that this implementation matches BibTeX. (assert-equal (lowercase-string/bst #"oNe: TWO: three") #"one: two: three") (assert-equal (uppercase-string/bst #"oNe: TWO: three") #"ONE: TWO: THREE") ;; here, note that the 'O' of "ONE" and 'T' characters are left ;; alone, rather than being lowercased, because they come after colon+space (assert-equal (titlecase-string/bst #"oNe: TWO: three") #"one: Two: three") (assert-equal (titlecase-string/bst #"ONE: TWO: THREE") #"One: Two: Three") ;; multiple spaces following colon (assert-equal (titlecase-string/bst #"ONE: TWO: THREE") #"One: Two: Three") ;; a character immediately following a colon ;; (ie, not a ‘colon and then nonnull white space‘) ;; isn't excepted from the lowercasing (assert-equal (titlecase-string/bst #"O:ABC:abc") #"O:abc:abc") ;; Now with braces. ;; Also, give the input as a string rather than ustring, ;; to verify that we accept (and convert) that. (assert-equal (titlecase-string/bst "{ONE}: {two}: {THREE}") #"{ONE}: {two}: {THREE}") (assert-equal (uppercase-string/bst "{ONE}: {two}: {THREE}") #"{ONE}: {two}: {THREE}") (assert-equal (lowercase-string/bst "{ONE}: {two}: {THREE}") #"{ONE}: {two}: {THREE}") ;; the following cases typically won't occur within BibTeX, ;; but this is sensible behaviour -- at any rate these arguments shouldn't cause errors (assert-false (titlecase-string/bst #f)) (assert-false (uppercase-string/bst #f)) (assert-false (lowercase-string/bst #f)) ;; (assert-equal (string->symbol/downcase "HeLlO") 'hello) ;; (assert-equal (string->symbol/downcase "Élàn") 'élàn) (assert-equal (map string->page-range '("1" "2-3" "4–5" "6-" "7+")) '(("1" . single) ("2" . "3") ("4" . "5") ("6" . inf) ("7" . inf))) (assert-equal (map string->page-range '("10=" "-11" "12-13--14" "foo" symbol)) '(#f #f #f #f #f)) (assert-equal (map en-dashify '("1" ;no punctuation "1-" "1--" ;hyphens "1–" ;unicode 'EN DASH', U+2013 ;;"-2" ;invalid in btxdoc terms, we should object ;;"--2" ;;"–2" ;EN DASH, again "3-4" "3--4" "3–4" ;EN DASH "3-4--3" ;hyphen then double-hyphen 1 ;not a string "5+")) ;; btxdoc doesn't indicate what "-2" or "1-2-3" should turn into. ;; The following seem reasonable, and are here to avoid regressions. ;; What we are comparing with here is the implementation of n.dashify in plain.bst ;; (see test-btxhak-internal.scm): ;; "1-2" n.dashify -> "1--2" ;; "1--2" n.dashify -> "1--2" ;; "1+" n.dashify -> "1+" (unhelpful) ;; "1-2-3" n.dashify -> "1--2--3" (but more-or-less by accident, I think) '("1" "1–" "1–" "1–" ;all en-dashes ;"2" "2" "2" "3–4" "3–4" "3–4" #f ;not "3–4" #f "5–")) ;; should I make this a built-in function? (define (first-page-number s) (let ((range (string->page-range s))) (if range (car range) #f))) (assert-equal (map first-page-number '("1" "11-22" "11–22" ;en-dash "111+" 'foo ;not a string "foo")) ;no digits '("1" "11" "11" "111" #f #f)) (let ((us #"ab~c{d}")) (assert-equal (ustring-uppercase us) #"AB~C{D}") (assert-equal (bstring->ustring (list->bstring (map uchar-upcase (ustring-iterator/bstrings us)))) #"AB~C{d}")))