Tests/OrgCoreTests/DiarySexpTests.swift
74 lines · 3664 bytes
1import Foundation
2import Testing
3@testable import OrgCore
4
5/// Diary sexps in the agenda (`org-agenda-get-sexps`, diary timestamps and planning).
6struct DiarySexpTests {
7 static let oracle = ProcessInfo.processInfo.environment["ORGSTAR_SKIP_ORACLE"] == nil
8
9 static let file = """
10 #+FILETAGS: :ft:
11 * Holidays :hol:
12 %%(diary-float t 4 4) Fourth Thursday
13 %%(diary-anniversary 10 7 1990) Birthday %d%s
14 %%(org-anniversary 2000 10 8) Org %d years
15 %%(diary-anniversary 2 29 2000) Leap %d
16 %%(diary-block 10 5 2026 10 7 2026) Conference
17 %%(diary-cyclic 3 10 1 2026) Every third, %d%s time
18 %%(diary-date t 6 t) Sixth of month
19 %%(diary-date '(10 11) '(5 9) 2026) List date
20 %%(diary-remind '(diary-date 10 9 2026) 2) Remind me
21 %%(diary-remind '(diary-date 10 12 2026) -3) Remind range
22 %%(diary-remind '(diary-anniversary 10 14 2000) 7) Week ahead %d
23 %%(org-class 2026 9 1 2026 12 20 2 41) Class on Tuesdays
24 %%(org-block 2026 10 6 2026 10 6) Meeting at 10:30
25 %%(memq (calendar-day-of-week date) '(1 3 5)) Gym
26 %%(and (= (calendar-extract-day date) 2) "Custom; second part")
27 %%(let ((d (calendar-extract-day date))) (when (= (% d 5) 0) (format "Day %d is a fifth" d)))
28 %%(diary-float 10 1 -1) Last Monday of October
29 %%(diary-float t 0 1 8) First Sunday after the 8th
30 %%(diary-date 10 8 2026)
31 &%%(diary-date 10 9 2026) Ampersand
32 * TODO Task with a diary stamp <%%(diary-float t 2 1)>
33 * Meeting
34 <%%(diary-date t 7 t) 14:00>
35 * Due
36 DEADLINE: <%%(diary-date 10 8 2026)>
37 * Sched
38 SCHEDULED: <%%(diary-float t 5 1)>
39 * COMMENT Skipped tree
40 %%(diary-date t t t) Never shown
41 """ + "\n"
42
43 @Test func evaluatesDiaryFunctions() throws {
44 let day = Days.absolute(year: 2026, month: 10, day: 7)
45 func run(_ text: String, _ entry: String = "E") throws -> [String]? {
46 DiarySexp.entries(try LispReader.readFirst(text).sexp, entry: entry, day: day)
47 }
48 #expect(try run("(diary-anniversary 10 7 1990)", "%d%s") == ["36th"])
49 #expect(try run("(diary-date 10 8 2026)") == nil)
50 #expect(try run("(diary-float 10 3 1)") == ["E"])
51 #expect(try run("\"a; b\"") == ["a", "b"])
52 #expect(try run("(undefined)") == nil)
53 #expect(try run("(diary-ordinal-suffix -3)") == nil)
54 }
55
56 @Test func skipsSexpsBeforeTheFirstHeading() {
57 let source = AgendaSource(path: "a.org", text: "%%(diary-date t t t) Before\n* H\n%%(diary-date t t t) Under\n")
58 let day = Days.absolute(year: 2026, month: 10, day: 7)
59 #expect(Agenda.list([source], start: day, days: 1, today: day, now: (9, 0))[0].items.map(\.text) == ["Under"])
60 }
61
62 @Test(.enabled(if: oracle))
63 func agendaMatchesEmacs() throws {
64 let files = [("diary.org", Self.file)]
65 let october = Days.absolute(year: 2026, month: 10, day: 1)
66 let ours = Agenda.list([AgendaSource(path: "diary.org", text: Self.file)], start: october, days: 15, today: october + 4, now: (9, 30))
67 let kinds = Set(ours.flatMap(\.items).map(\.kind))
68 #expect(kinds.isSuperset(of: [.sexp, .timestamp, .deadline, .pastScheduled]))
69 try AgendaOracle.compare(files: files, start: october, span: 15, today: october + 4)
70 try AgendaOracle.compare(files: files, start: october + 20, span: 10, today: october + 26)
71 let leap = Days.absolute(year: 2027, month: 2, day: 27)
72 try AgendaOracle.compare(files: files, start: leap, span: 4, today: leap)
73 }
74}