krz/orgstar

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

Tests/OrgCoreTests/TableTests.swift

16086b4cf2caff5328774b2cd5ae3ffe1ab65ca4
orgstar/Tests/OrgCoreTests/TableTests.swift history · blame · raw

101 lines · 5482 bytes

  1import Foundation
  2import Testing
  3@testable import OrgCore
  4
  5struct TableTests {
  6    @Test func aligns() {
  7        let result = runCommand(TableCommand(.align), "|a|bb|\n|ccc|1|\n", caret: 1).text
  8        #expect(result == "| a   | bb |\n| ccc |  1 |\n")
  9    }
 10
 11    static let variants = [
 12        "|a|bb|\n|ccc|1|\n",
 13        "| Name | Qty |\n|-\n| apple | 3 |\n| pear | 12 |\n",
 14        "  | x | y |\n  |---+---|\n  | 1 | 2 |\n",
 15        "| [[https://example.com][link]] | *bold* | =code= |\n| a | b | c |\n",
 16        "| \\\\alpha | a_{bc} | x^2 |\n| 1 | 2 | 3 |\n",
 17        "| <r> | <c5> |\n| a | b |\n",
 18        "| 1 | 2 |\n| 3 | 4 |\n#+TBLFM: $2=$1*2::@2$1=7\n",
 19        "text\n| a |\n| b |\nmore\n",
 20        "| ü | 漢字 |\n| x | y |\n",
 21        "| a | b\n| c |\n",
 22        "|-+-|\n",
 23    ]
 24
 25    static let commands: [(command: any OrgCommand, form: String, headingLineOnly: Bool)] = [
 26        (TableCommand(.align), "(org-table-align)", false),
 27        (TableCommand(.nextField), "(org-table-next-field)", false),
 28        (TableCommand(.previousField), "(org-table-previous-field)", false),
 29        (TableCommand(.nextRow), "(org-table-next-row)", false),
 30        (TableCommand(.moveRowUp), "(org-table-move-row-up)", false),
 31        (TableCommand(.moveRowDown), "(org-table-move-row-down)", false),
 32        (TableCommand(.moveColumnLeft), "(org-table-move-column-left)", false),
 33        (TableCommand(.moveColumnRight), "(org-table-move-column-right)", false),
 34        (TableCommand(.insertColumn), "(org-table-insert-column)", false),
 35        (TableCommand(.deleteColumn), "(org-table-delete-column)", false),
 36        (TableCommand(.killRow), "(org-table-kill-row)", false),
 37        (TableCommand(.insertRowAbove), "(org-table-insert-row)", false),
 38        (TableCommand(.insertHline), "(org-table-insert-hline)", false),
 39    ]
 40
 41    static func tableCarets(_ text: String) -> [Int] {
 42        EmacsOracle.positions(text).filter { offset in
 43            let ns = text as NSString
 44            let line = ns.lineRange(for: NSRange(location: min(offset, max(0, ns.length - 1)), length: 0))
 45            return offset < ns.length && ns.substring(with: line).trimmingCharacters(in: .whitespaces).hasPrefix("|")
 46        }
 47    }
 48
 49    @Test(.enabled(if: ProcessInfo.processInfo.environment["ORGSTAR_SKIP_ORACLE"] == nil))
 50    func tableCommandsMatchEmacs() throws {
 51        let inputs = Self.variants.map { ($0, Self.tableCarets($0)) }
 52        try compareWithEmacs(inputs, Self.commands)
 53        try compareWithEmacs(inputs, Self.commands, options: doomOptions)
 54    }
 55
 56    @Test(.enabled(if: ProcessInfo.processInfo.environment["ORGSTAR_SKIP_ORACLE"] == nil))
 57    func createTableMatchesEmacs() throws {
 58        for (text, caret) in [("", 0), ("x\n", 1), ("  \n", 2)] {
 59            for size in ["3x2", "1x1", "2x3"] {
 60                let emacs = try EmacsOracle.run([EmacsOracle.Case(text: text, point: caret + 1, form: "(org-table-create \"\(size)\")")])[0]
 61                let ours = runCommand(CreateTable(), text, caret: caret, answers: ["size": size])
 62                #expect(ours.text == emacs.text && ours.caret == EmacsOracle.offset(emacs.point, in: emacs.text), "\(size) in \(text.debugDescription): ours \(ours.text.debugDescription)@\(ours.caret) emacs \(emacs.text.debugDescription)@\(EmacsOracle.offset(emacs.point, in: emacs.text))")
 63            }
 64        }
 65    }
 66
 67    /// Real tables: `ORGSTAR_ORACLE_CORPUS=<folder>` aligns and TABs in the first cell of up to
 68    /// eight table rows per file, in up to 60 files.
 69    @Test(.enabled(if: ProcessInfo.processInfo.environment["ORGSTAR_ORACLE_CORPUS"] != nil))
 70    func tablesMatchEmacsOnACorpus() throws {
 71        let root = URL(fileURLWithPath: ProcessInfo.processInfo.environment["ORGSTAR_ORACLE_CORPUS"]!)
 72        let files = FileManager.default.enumerator(at: root, includingPropertiesForKeys: nil)!
 73            .compactMap { $0 as? URL }.filter { $0.pathExtension == "org" }.sorted { $0.path < $1.path }
 74        var samples: [(String, [Int])] = []
 75        for file in files where samples.count < 60 {
 76            guard let text = try? String(contentsOf: file, encoding: .utf8), text.utf16.count <= 30_000, !text.contains("\r") else { continue }
 77            let ns = text as NSString
 78            var carets: [Int] = []
 79            var location = 0
 80            var inBlock = false
 81            while location < ns.length, carets.count < 8 {
 82                let line = ns.lineRange(for: NSRange(location: location, length: 0))
 83                let content = ns.substring(with: line)
 84                let trimmed = content.trimmingCharacters(in: .whitespaces).lowercased()
 85                if trimmed.hasPrefix("#+begin_") { inBlock = true }
 86                if trimmed.hasPrefix("#+end_") { inBlock = false }
 87                if !inBlock, trimmed.hasPrefix("|"), let bar = content.firstIndex(of: "|") {
 88                    carets.append(line.location + content.distance(from: content.startIndex, to: bar) + 2)
 89                }
 90                location = NSMaxRange(line)
 91            }
 92            if !carets.isEmpty { samples.append((text, carets)) }
 93        }
 94        let commands: [(command: any OrgCommand, form: String, headingLineOnly: Bool)] = [
 95            (TableCommand(.align), "(org-table-align)", false),
 96            (TableCommand(.nextField), "(org-table-next-field)", false),
 97        ]
 98        try compareWithEmacs(samples, commands)
 99        try compareWithEmacs(samples, commands, options: doomOptions)
100    }
101}