Tests/OrgCoreTests/CitationTests.swift
141 lines · 7262 bytes
1import Foundation
2import Testing
3@testable import OrgCore
4
5/// Citations against `oc-basic` through ox-md: each paragraph and the bibliography.
6struct CitationTests {
7 static let oracle = ProcessInfo.processInfo.environment["ORGSTAR_SKIP_ORACLE"] == nil
8
9 static let bib = #"""
10 @string{jos = "Journal of Stuff"}
11 @article{doe2020,
12 author = {Doe, Jane and Roe, Richard},
13 title = {A Study of {Things}},
14 journal = jos,
15 year = {2020},
16 pages = {1--10}
17 }
18 @book{smith1999,
19 author = "Smith, John",
20 title = {The Book},
21 publisher = {Pub House},
22 year = 1999
23 }
24 @misc{lee,
25 author = {Lee, Kim and Park, Min and Choi, Su},
26 title = {Three Authors},
27 year = {2021}
28 }
29 @article{doe2020b,
30 author = {Doe, Jane and Roe, Richard},
31 title = {Another Study},
32 journal = {Other Journal},
33 year = {2020}
34 }
35 @report{org,
36 editor = {{Org Team}},
37 title = {Manual},
38 institution = {GNU},
39 date = {2022-03-04}
40 }
41 @comment{ignored}
42 """#
43
44 static let json = #"""
45 [{"id": "csl1", "author": [{"family": "Zed", "given": "Ann"}], "title": "From JSON", "issued": {"date-parts": [[2018, 5]]}, "publisher": "Pubs"}]
46 """#
47
48 static let body = """
49 Plain [cite:@doe2020] and [cite:@smith1999 p. 5] and [cite/t:@lee].
50
51 Several [cite:see @doe2020;@smith1999] and common [cite:pre; @lee; post].
52
53 Same year [cite:@doe2020b] then [cite/t:@doe2020] and [cite/text/c:@lee].
54
55 Noauthor [cite/na:@doe2020], bare [cite/na/b:@smith1999], author [cite/a:@lee] [cite/author/caps:@lee].
56
57 Bare default [cite//b:@smith1999] and text bare [cite/t/b:@org], missing [cite:@nope], json [cite:@csl1].
58
59 Nocite here [cite/n:@lee] and there [cite/nocite:@smith1999].
60
61 Markup [cite:/see/ @doe2020 *p. 3*].
62
63 #+print_bibliography:
64 """
65
66 func compare(_ text: String) throws {
67 let folder = FileManager.default.temporaryDirectory.appendingPathComponent("orgstar-cite-\(UUID().uuidString)")
68 try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
69 defer { try? FileManager.default.removeItem(at: folder) }
70 try Self.bib.write(to: folder.appendingPathComponent("refs.bib"), atomically: true, encoding: .utf8)
71 try Self.json.write(to: folder.appendingPathComponent("more.json"), atomically: true, encoding: .utf8)
72 let path = folder.appendingPathComponent("doc.org").path
73 try text.write(toFile: path, atomically: true, encoding: .utf8)
74 let text = "#+OPTIONS: toc:nil\n" + text
75 try text.write(toFile: path, atomically: true, encoding: .utf8)
76 let emacs = try EmacsOracle.evaluate("", """
77 (with-current-buffer (find-file-noselect \(lispString(path)))
78 (require 'ox-md) (require 'oc-basic)
79 (list (org-export-as 'md nil nil t)))
80 """)[0]
81 let ours = MarkdownExport.document(text, path: path)
82 func paragraphs(_ s: String) -> [String] {
83 s.components(separatedBy: "\n\n").map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }
84 }
85 #expect(paragraphs(ours) == paragraphs(emacs), "ours:\n\(ours)\nemacs:\n\(emacs)")
86 }
87
88 @Test func htmlUsesTheSameCitations() throws {
89 let folder = FileManager.default.temporaryDirectory.appendingPathComponent("orgstar-cite-\(UUID().uuidString)")
90 try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
91 defer { try? FileManager.default.removeItem(at: folder) }
92 try Self.bib.write(to: folder.appendingPathComponent("refs.bib"), atomically: true, encoding: .utf8)
93 let page = HTMLExport.page("#+bibliography: refs.bib\nSee [cite:@doe2020 *p. 3*] [cite/n:@lee].\n\n#+print_bibliography:\n", fallbackTitle: "t",
94 path: folder.appendingPathComponent("doc.org").path)
95 #expect(page.contains("<p>See (Doe, Jane and Roe, Richard, 2020 <strong>p. 3</strong>).</p>"))
96 #expect(page.contains("<p>Doe, Jane and Roe, Richard (2020). <i>A Study of Things</i>, Journal of Stuff.</p>"))
97 #expect(page.contains("<p>Lee, Kim and Park, Min and Choi, Su (2021). <i>Three Authors</i>.</p>"))
98 #expect(!page.contains("[cite"))
99 }
100
101 @Test func aTitleCitationLeavesTheBodysAlone() throws {
102 let folder = FileManager.default.temporaryDirectory.appendingPathComponent("orgstar-cite-\(UUID().uuidString)")
103 try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
104 defer { try? FileManager.default.removeItem(at: folder) }
105 try Self.bib.write(to: folder.appendingPathComponent("refs.bib"), atomically: true, encoding: .utf8)
106 let page = HTMLExport.page("#+bibliography: refs.bib\n#+TITLE: On [cite:@doe2020]\nSee [cite:@doe2020].\n", fallbackTitle: "t",
107 path: folder.appendingPathComponent("doc.org").path)
108 #expect(page.contains("<h1 class=\"title\">On</h1>"))
109 #expect(page.contains("<p>See (Doe, Jane and Roe, Richard, 2020).</p>"))
110 }
111
112 /// As ox-md exports them: footnotes after the punctuation, holding text-style citations.
113 @Test func noteCitationsBecomeFootnotes() throws {
114 let folder = FileManager.default.temporaryDirectory.appendingPathComponent("orgstar-cite-\(UUID().uuidString)")
115 try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
116 defer { try? FileManager.default.removeItem(at: folder) }
117 try Self.bib.write(to: folder.appendingPathComponent("refs.bib"), atomically: true, encoding: .utf8)
118 let text = "#+bibliography: refs.bib\nFirst claim [cite/ft:@doe2020]. Second [cite/note:see @smith1999 p. 5], and third [cite/ft:@lee].\n\nEnd [cite/ft/c:@lee]\n"
119 let markdown = MarkdownExport.document(text, path: folder.appendingPathComponent("doc.org").path)
120 #expect(markdown.contains("First claim.[^1] Second,[^2] and third.[^3]"))
121 #expect(markdown.contains("End[^4]"))
122 #expect(markdown.contains("[^1]: Doe, Jane and Roe, Richard (2020)"))
123 #expect(markdown.contains("[^2]: see Smith, John (1999) p. 5"))
124 #expect(markdown.contains("[^4]: Lee, Kim And Park, Min And Choi, Su (2021)"))
125 }
126
127 @Test(.enabled(if: oracle))
128 func authorYearMatchesOcBasic() throws {
129 try compare("#+bibliography: refs.bib\n#+bibliography: more.json\n" + Self.body)
130 }
131
132 @Test(.enabled(if: oracle))
133 func numericMatchesOcBasic() throws {
134 try compare("#+bibliography: refs.bib\n#+cite_export: basic numeric numeric\n" + "Cites [cite:@smith1999;@doe2020;@lee] and [cite:@doe2020b] and [cite:see @lee p. 2] and [cite:pre; @lee;@org; post].\n\n#+print_bibliography:\n")
135 }
136
137 @Test(.enabled(if: oracle))
138 func plainBibliographyMatchesOcBasic() throws {
139 try compare("#+bibliography: refs.bib\n#+cite_export: basic plain\n" + "A [cite:@lee] [cite:@doe2020] [cite:@org].\n\n#+print_bibliography:\n")
140 }
141}