krz/orgstar

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

Commit c35e9255d3

c35e9255d3906a55882611063a733c9ed6f3a7b2

parent: 39a494d592

Verified · cmc

cmc <hello@cleberg.net> · 2026-10-04 23:18 UTC

Add round-trip fuzz, encoding and corpus tests

Layout: unified · split

Tests/OrgCoreTests/RoundTripTests.swift added +75
@@ -0,0 +1,75 @@
1import Foundation
2import Testing
3@testable import OrgCore
4
5/// SplitMix64, so failures reproduce from the seed.
6struct SeededGenerator: RandomNumberGenerator {
7 var state: UInt64
8 mutating func next() -> UInt64 {
9 state &+= 0x9E37_79B9_7F4A_7C15
10 var z = state
11 z = (z ^ (z >> 30)) &* 0xBF58_476D_1CE4_E5B9
12 z = (z ^ (z >> 27)) &* 0x94D0_49BB_1331_11EB
13 return z ^ (z >> 31)
14 }
15}
16
17let fragments = [
18 "* ", "** TODO [#A] title :a:b:", "*** DONE", "#+TODO: NEXT | DONE", "#+begin_src sh", "#+end_src",
19 "#+BEGIN_QUOTE", "#+end_quote", "#+BEGIN: clocktable", "#+END:", ":PROPERTIES:", ":ID: x", ":END:",
20 ":LOGBOOK:", "CLOCK: [2026-10-04 Sun 10:00]", "SCHEDULED: <2026-10-04 Sun>", "- item", " - nested",
21 "\t+ tab", "1. one", "| a | b |", "|---+---|", "#+TBLFM: $2=$1", "# comment", ": fixed", "-----",
22 "[fn:1] note", "#+NAME: x", "plain text", "é", "😀", "e\u{301}", " ", "\t", "\n", "\n", "\r\n", "\r", "",
23]
24
25func randomDocument(_ rng: inout SeededGenerator) -> String {
26 (0..<Int.random(in: 0...40, using: &rng)).map { _ in
27 fragments.randomElement(using: &rng)! + (Bool.random(using: &rng) ? "\n" : "")
28 }.joined()
29}
30
31func checkLengths(_ node: SyntaxNode) -> Bool {
32 let sum = node.green.children.reduce(0) { $0 + $1.length }
33 return sum == node.green.length && node.children.allSatisfy(checkLengths)
34}
35
36struct RoundTripTests {
37 @Test func fuzzedDocumentsRoundTrip() {
38 var rng = SeededGenerator(state: 20261004)
39 for n in 0..<2_000 {
40 let text = randomDocument(&rng)
41 let tree = OrgParser.parse(text)
42 #expect(tree.text == text, "document \(n)")
43 #expect(checkLengths(tree.root), "document \(n)")
44 }
45 }
46
47 @Test(arguments: [
48 [0xEF, 0xBB, 0xBF] + Array("* a\r\n".utf8),
49 Array("* a\r\n- b\n\tc".utf8),
50 Array("* a".utf8),
51 Array("😀 e\u{301}\n".utf8),
52 [0x2A, 0x20, 0xFF, 0x0A] as [UInt8],
53 ])
54 func encodingFixturesRoundTrip(bytes: [UInt8]) {
55 let source = SourceText(bytes: bytes)
56 let tree = OrgParser.parse(source.text)
57 #expect(source.encode(tree.text) == bytes)
58 }
59
60 /// Private corpus: `ORGSTAR_CORPUS=/path/to/org swift test --filter corpus`.
61 @Test(.enabled(if: ProcessInfo.processInfo.environment["ORGSTAR_CORPUS"] != nil))
62 func corpusRoundTrips() throws {
63 let root = URL(fileURLWithPath: ProcessInfo.processInfo.environment["ORGSTAR_CORPUS"]!)
64 let files = FileManager.default.enumerator(at: root, includingPropertiesForKeys: nil)!
65 .compactMap { $0 as? URL }
66 .filter { ["org", "org_archive"].contains($0.pathExtension) }
67 #expect(!files.isEmpty)
68 for file in files {
69 let bytes = try [UInt8](Data(contentsOf: file))
70 let source = SourceText(bytes: bytes)
71 let tree = OrgParser.parse(source.text)
72 #expect(source.encode(tree.text) == bytes, "\(file.path)")
73 }
74 }
75}