forked from andras-simonyi/citeproc-el
-
Notifications
You must be signed in to change notification settings - Fork 0
/
citeproc-test-human.el
207 lines (182 loc) · 7.75 KB
/
citeproc-test-human.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
;; citeproc-test-human.el --- support tests in CSL suite format -*- lexical-binding: t; -*-
;; Copyright (C) 2017-2021 András Simonyi
;; Author: András Simonyi <[email protected]>
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Functions to create ERT tests from tests in CSL test suite format. The
;; official tests can be found at
;; <https://github.com/citation-style-language/test-suite>.
;;; Code:
(require 'f)
(require 's)
(require 'json)
(require 'ert)
(require 'string-inflection)
(require 'citeproc)
(defvar citeproc-test-human--locale-dir "./test/locales")
(defun citeproc-test-human--parse-testfile (file)
"Return a parsed form of CSL test FILE."
(let (result
(json-array-type 'list)
(json-key-type 'symbol))
(with-temp-buffer
(insert-file-contents file)
(goto-char 1)
(setq result (list (cons 'YEAR-SUFF
(re-search-forward "variable=\"year-suffix\"" nil t))))
(goto-char 1)
(while (re-search-forward ">>=+ \\{1,2\\}\\([[:graph:]]+\\) =+>>" nil t)
(let ((section (intern (buffer-substring (nth 2 (match-data))
(nth 3 (match-data)))))
(start (1+ (point)))
end)
(re-search-forward "<<=" nil t)
(setq end (- (car (match-data)) 1))
(push (cons section (pcase section
('OUTPUT-FORMAT
(intern (buffer-substring-no-properties start end)))
('CSL (buffer-substring-no-properties start end))
((or 'INPUT 'CITATIONS 'CITATION-ITEMS)
(goto-char (- start 1)) (json-read))
(_ (buffer-substring start end))))
result))))
result))
(defun citeproc-test-human--create-getter (items)
"Return a getter function for ITEMS.
ITEMS is the parsed representation of the `INPUT' section of a
CSL test."
(lambda (itemids)
(let (result)
(dolist (item items result)
(let ((id (citeproc-s-from-num-or-s (alist-get 'id item))))
(when (member id itemids)
(push (cons id item) result)))))))
(defun citeproc-test-human--proc-from-style (style parsed-input)
"Create a processor from STYLE and PARSED-INPUT."
(citeproc-create style
(citeproc-test-human--create-getter parsed-input)
(citeproc-locale-getter-from-dir citeproc-test-human--locale-dir)))
(defun citeproc-test-human--proc-from-testfile (file)
"Create an (itemless) processor from a test FILE."
(let ((style-string (alist-get 'CSL (citeproc-test-human--parse-testfile file)))
(locale-getter (citeproc-locale-getter-from-dir citeproc-test-human--locale-dir)))
(citeproc-create style-string nil locale-getter)))
(defun citeproc-test-human--parse-citation (ct-desc &optional cites-only)
"Parse test citations description CT-DESC.
Return a list of citation structures. If CITES-ONLY is non-nil
then the input is list of cites."
(if cites-only
(citeproc-citation-create
:cites (-map #'citeproc-test-human--normalize-cite ct-desc))
(let ((citation-info (car ct-desc)))
(let-alist (alist-get 'properties citation-info)
(citeproc-citation-create
:cites (-map #'citeproc-test-human--normalize-cite
(alist-get 'citationItems citation-info))
:note-index .noteIndex
:mode (citeproc-lib-intern .mode)
:capitalize-first .capitalize-first
:suppress-affixes .suppress-affixes
:ignore-et-al .ignore-et-al)))))
(defun citeproc-test-human--normalize-cite (cite)
"Normalize a test CITE."
(--map (let ((val (cdr it)))
(if (numberp val) (cons (car it) (number-to-string val)) it))
cite))
(defun citeproc-test-human--run-parsed (parsed)
"Run the parsed CSL test PARSED.
Return the resulting output."
(-let* (((&alist 'CSL style
'INPUT input
'MODE mode
'CITATION-ITEMS citation-items
'CITATIONS citations
'OUTPUT-FORMAT output-format)
parsed)
(output-format (or output-format 'csl-test))
(proc (citeproc-test-human--proc-from-style style input)))
(--each input
(citeproc-proc-put-item-by-id proc
(citeproc-s-from-num-or-s (alist-get 'id it))))
(when (string= mode "citation")
(cond
(citation-items
(citeproc-append-citations (--map (citeproc-test-human--parse-citation it t)
citation-items)
proc))
(citations
(citeproc-append-citations (mapcar #'citeproc-test-human--parse-citation citations)
proc))
(t (citeproc-append-citations (list (citeproc-test-human--parse-citation input t))
proc))))
(let ((output (if (string= mode "citation")
(citeproc-render-citations proc output-format
(when (eq 'csl-test output-format)
'no-links))
(car (citeproc-render-bib proc output-format 'no-links)))))
(if (string= mode "citation") (s-join "\n" output) output))))
(defun citeproc-test-human--expected-from-parsed (parsed)
"Return the expected output of parsed CSL test PARSED."
(let ((expected (alist-get 'RESULT parsed)))
(if (or (string= (s-left 5 expected) "..[0]")
(string= (s-left 5 expected) ">>[0]"))
(s-join "\n" (--map (substring it 6)
(split-string expected "\n")))
expected)))
(defun citeproc-test-human-create-from-file (file expected-fails &optional name-prefix)
"Create an ERT test from a CSL test FILE.
If optional NAME-PREFIX is non-nil then it is added the name of
the created test after the obligatory `citeproc'."
(let* ((parsed (citeproc-test-human--parse-testfile file))
(expected (citeproc-test-human--expected-from-parsed parsed))
(file-name (f-filename file))
(test-name (intern
(concat "citeproc-"
(if name-prefix (concat name-prefix "-") "")
(string-inflection-kebab-case-function
(substring file-name 0 -4)))))
(expected-fail (memq test-name expected-fails)))
(eval `(ert-deftest ,test-name ()
:expected-result ,(if expected-fail :failed :passed)
(let ((citeproc-disambiguation-cite-pos 'subsequent))
(should (string=
,expected
(citeproc-test-human--run-parsed ',parsed))))))))
(defun citeproc-test-human---read-expected-fails (expected-fails-file)
"Read the list of tests expected to fail from EXPECTED-FAILS-FILE."
(let* ((list-as-str (with-temp-buffer
(insert-file-contents expected-fails-file)
(buffer-string)))
(split (split-string list-as-str "\n")))
(--map (intern it) (butlast split))))
(defun citeproc-test-human-create-from-dir (dir &optional
expected-fails-file name-prefix)
"Create all CSL tests from DIR.
Each file in DIR having the `txt' extension is read as a
human-readable CSL test, and a corresponding ERT test is created.
The created test's name will be constructed by prefixing the
test's filename (without the extension) with `citeproc-'. If the
optional EXPECTED-FAILS-FILE is non-nil then read that file as a
list of tests whose failure is expected. If optional NAME-PREFIX
is non-nil then it is added the names of the created tests after
the obligatory `citeproc'. The file should contain one test-name
per line (together with the `citeproc-' prefix)."
(let ((expected-fails
(if expected-fails-file
(citeproc-test-human---read-expected-fails expected-fails-file)
nil)))
(dolist (test-file (f-glob (concat dir "/*.txt")))
(citeproc-test-human-create-from-file test-file expected-fails
name-prefix))))
(provide 'citeproc-test-human)
;;; citeproc-test-human.el ends here