krz/orgstar

A native macOS editor for org-mode files. editor org-mode swift

Tests/OrgCoreTests/ListCommandTests.swift

778058aafc7ddacee4a613b1da87c60aa0c8e113
orgstar/Tests/OrgCoreTests/ListCommandTests.swift history · blame · raw

82 lines · 4320 bytes

6 symbols in this file
 1import Foundation
 2import Testing
 3@testable import OrgCore
 4
 5struct ListCommandTests {
 6    @Test func basics() {
 7        #expect(runCommand(InsertItem(), "- a\n", caret: 3).text == "- a\n- \n")
 8        #expect(runCommand(InsertItem(), "1. a\n2. b\n", caret: 4).text == "1. a\n2. \n3. b\n")
 9        #expect(runCommand(IndentItem(), "- a\n- b\n", caret: 4).text == "- a\n  - b\n")
10        #expect(runCommand(OutdentItem(), "- a\n  - b\n", caret: 6).text == "- a\n- b\n")
11        #expect(runCommand(MoveItemDown(), "- a\n- b\n", caret: 0).text == "- b\n- a\n")
12        #expect(runCommand(ToggleCheckbox(), "* h [0/1]\n- [ ] a\n", caret: 12).text == "* h [1/1]\n- [X] a\n")
13        #expect(runCommand(IndentItem(), "text\n", caret: 0).failure != nil)
14    }
15}
16
17struct ListOracleTests {
18    static let variants = [
19        "- a\n- b\n- c\n",
20        "1. one\n2. two\n3. three\n",
21        "- a\n  - b\n  - c\n- d\n",
22        "- [ ] a\n- [X] b\n",
23        "* T [1/3]\n- [X] a\n- [ ] b\n- [ ] c\n",
24        "* T [50%]\n- [ ] a\n  - [X] b\n  - [ ] c\n",
25        "- a\n\n- b\n",
26        "- term :: def\n- other :: x\n",
27        "text\n- a\n  more\n- b\nafter\n",
28        "1) x\n2) y\n",
29        "- a 😀\n  - b\n",
30        "#+begin_src\n- a\n#+end_src\n",
31        "- a\n- b\n        - c\n- d\n",
32    ]
33
34    static let commands: [(command: any OrgCommand, form: String, headingLineOnly: Bool)] = [
35        (InsertItem(), "(or (org-insert-item) (error \"Not in an item\"))", false),
36        (InsertCheckboxItem(), "(or (org-insert-item 'checkbox) (error \"Not in an item\"))", false),
37        (IndentItem(), "(org-indent-item)", false),
38        (OutdentItem(), "(org-outdent-item)", false),
39        (IndentItemTree(), "(org-indent-item-tree)", false),
40        (OutdentItemTree(), "(org-outdent-item-tree)", false),
41        (MoveItemUp(), "(org-move-item-up)", false),
42        (MoveItemDown(), "(org-move-item-down)", false),
43        (ToggleCheckbox(), "(if (org-at-heading-p) (error \"heading\") (org-toggle-checkbox))", false),
44    ]
45
46    @Test(.enabled(if: ProcessInfo.processInfo.environment["ORGSTAR_SKIP_ORACLE"] == nil))
47    func listCommandsMatchEmacs() throws {
48        try compareWithEmacs(Self.variants.map { ($0, EmacsOracle.positions($0)) }, Self.commands)
49    }
50
51    @Test(.enabled(if: ProcessInfo.processInfo.environment["ORGSTAR_SKIP_ORACLE"] == nil))
52    func listCommandsMatchEmacsWithDoomOptions() throws {
53        let alphabetical = ["a. x\nb. y\n", "A) one\n   - sub\nB) two\n", "- [@c] x\n- y\n", "1. a\n2. b\n"]
54        try compareWithEmacs((Self.variants + alphabetical).map { ($0, EmacsOracle.positions($0)) }, Self.commands, options: doomOptions)
55    }
56
57    /// Real lists: `ORGSTAR_ORACLE_CORPUS=<folder>` runs every list command in files up to
58    /// 20 KB with the caret at the start, bullet end and end of up to six item lines per file.
59    @Test(.enabled(if: ProcessInfo.processInfo.environment["ORGSTAR_ORACLE_CORPUS"] != nil))
60    func listCommandsMatchEmacsOnACorpus() throws {
61        let root = URL(fileURLWithPath: ProcessInfo.processInfo.environment["ORGSTAR_ORACLE_CORPUS"]!)
62        let files = FileManager.default.enumerator(at: root, includingPropertiesForKeys: nil)!
63            .compactMap { $0 as? URL }.filter { $0.pathExtension == "org" }.sorted { $0.path < $1.path }
64        let item = try NSRegularExpression(pattern: "^[ \\t]*(?:[-+]|[0-9]+[.)]) ")
65        var samples: [(String, [Int])] = []
66        for file in files where samples.count < 60 {
67            guard let text = try? String(contentsOf: file, encoding: .utf8), text.utf16.count <= 20_000, !text.contains("\r") else { continue }
68            let ns = text as NSString
69            var carets: [Int] = []
70            var location = 0
71            while location < ns.length, carets.count < 18 {
72                let line = ns.lineRange(for: NSRange(location: location, length: 0))
73                if let match = item.firstMatch(in: text, options: .anchored, range: line) {
74                    carets += [line.location, NSMaxRange(match.range), NSMaxRange(line) - (ns.substring(with: line).hasSuffix("\n") ? 1 : 0)]
75                }
76                location = NSMaxRange(line)
77            }
78            if !carets.isEmpty { samples.append((text, carets)) }
79        }
80        try compareWithEmacs(samples, Self.commands)
81    }
82}