Tests/OrgCoreTests/BlockCommandTests.swift
97 lines · 4671 bytes
4 symbols in this file
1import Foundation
2import Testing
3@testable import OrgCore
4
5struct BlockCommandTests {
6 static let oracle = ProcessInfo.processInfo.environment["ORGSTAR_SKIP_ORACLE"] == nil
7
8 static let text = """
9 * Heading
10 Some text here.
11 Indented line
12 * Starred
13 #+keyword
14 last
15 """
16
17 @Test(.enabled(if: oracle))
18 func insertsTemplatesLikeOrg() throws {
19 let ns = Self.text as NSString
20 let points = [ns.range(of: "Some").location, ns.range(of: "text").location, ns.range(of: "Indented").location, ns.length]
21 let regions = [("Some", "here."), ("Indented", "#+keyword"), ("Some", "line\n")]
22 let types = ["src", "quote", "EXAMPLE", "export html", "verse"]
23 var cases: [EmacsOracle.Case] = []
24 var ours: [(String, Int)] = []
25 for type in types {
26 for offset in points {
27 cases.append(EmacsOracle.Case(text: Self.text, point: EmacsOracle.point(offset, in: Self.text), form: "(org-insert-structure-template \(lispString(type)))"))
28 ours.append(Self.run(type, offset..<offset))
29 }
30 for (from, to) in regions {
31 let start = ns.range(of: from).location
32 let end = NSMaxRange(ns.range(of: to))
33 cases.append(EmacsOracle.Case(text: Self.text, point: EmacsOracle.point(end, in: Self.text), form: "(progn (transient-mark-mode 1) (set-mark \(EmacsOracle.point(start, in: Self.text))) (activate-mark) (org-insert-structure-template \(lispString(type))))"))
34 ours.append(Self.run(type, start..<end))
35 }
36 }
37 for (i, (mine, theirs)) in zip(ours, try EmacsOracle.run(cases)).enumerated() {
38 #expect(mine.0 == theirs.text, "\(cases[i].form)\nours: \(mine.0.debugDescription)\nemacs: \(theirs.text.debugDescription)")
39 if mine.0 == theirs.text { #expect(mine.1 == EmacsOracle.offset(theirs.point, in: theirs.text), "\(cases[i].form) caret") }
40 }
41 }
42
43 static func run(_ type: String, _ selection: Range<Int>) -> (String, Int) {
44 let key = InsertStructureTemplate.templates.first { $0.type == type }.map { String($0.key) } ?? "\t"
45 let context = EditContext(revision: 0, text: text, tree: OrgParser.parse(text), selection: [selection], answers: ["template": key, "type": type])
46 guard case .commit(let result) = InsertStructureTemplate().run(in: context) else { return (text, 0) }
47 var new = text
48 for edit in result.edits.sorted(by: { $0.range.lowerBound > $1.range.lowerBound }) { new = edit.apply(to: new) }
49 return (new, result.selection?.first?.lowerBound ?? 0)
50 }
51
52 static let blocks = """
53 * Code
54 #+begin_src python :results output
55 def f():
56 return 1
57 ,* not a heading
58 ,#+not a keyword
59 #+end_src
60 #+begin_example
61 indented
62 #+end_example
63 #+begin_src sh
64 #+end_src
65 """
66
67 @Test(.enabled(if: oracle))
68 func editsBlocksLikeOrgEditSpecial() throws {
69 let ns = Self.blocks as NSString
70 let points = [ns.range(of: "def f").location, ns.range(of: "#+begin_example").location, ns.range(of: "#+begin_src sh").location + 3]
71 let added = "* new star\n#+new keyword\nplain"
72 let forms = points.map { point in
73 """
74 (progn (erase-buffer) (insert \(lispString(Self.blocks))) (goto-char \(EmacsOracle.point(point, in: Self.blocks)))
75 (org-edit-special)
76 (let ((content (buffer-string)))
77 (goto-char (point-max)) (insert \(lispString(added)))
78 (org-edit-src-exit)
79 (format "%s|%s" content (buffer-string))))
80 """
81 }
82 let emacs = try EmacsOracle.evaluate("", "(list \(forms.joined(separator: " ")))", prelude: "(setq org-src-preserve-indentation t org-src-window-setup 'current-window)")
83 let tree = OrgParser.parse(Self.blocks)
84 for (point, expected) in zip(points, emacs) {
85 guard let edit = BlockEdit.at(point, text: Self.blocks, tree: tree) else {
86 Issue.record("no block at \(point)")
87 continue
88 }
89 let context = EditContext(revision: 0, text: Self.blocks, tree: tree, selection: [point..<point])
90 var after = Self.blocks
91 if case .commit(let result) = ReplaceBlockLines(edit: edit, content: edit.content + added).run(in: context) {
92 for e in result.edits { after = e.apply(to: after) }
93 }
94 #expect("\(edit.content)|\(after)" == expected, "at \(point)")
95 }
96 }
97}