summaryrefslogtreecommitdiff
path: root/tests/testutil-calendar-sync.el
diff options
context:
space:
mode:
Diffstat (limited to 'tests/testutil-calendar-sync.el')
-rw-r--r--tests/testutil-calendar-sync.el198
1 files changed, 198 insertions, 0 deletions
diff --git a/tests/testutil-calendar-sync.el b/tests/testutil-calendar-sync.el
new file mode 100644
index 00000000..d1a94b01
--- /dev/null
+++ b/tests/testutil-calendar-sync.el
@@ -0,0 +1,198 @@
+;;; testutil-calendar-sync.el --- Test utilities for calendar-sync -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Utilities for testing calendar-sync module, especially dynamic timestamp generation.
+;; Following quality-engineer.org guidelines: no hardcoded dates!
+
+;;; Code:
+
+(require 'calendar)
+
+;;; Test Environment Setup
+
+;; Provide stub for cj/log-silently if not already defined
+;; This function is defined in system-lib.el but tests should run standalone
+(unless (fboundp 'cj/log-silently)
+ (defun cj/log-silently (format-string &rest args)
+ "Stub for testing: silently ignore log messages."
+ nil))
+
+;;; Dynamic Timestamp Generation
+
+(defun test-calendar-sync-time-today-at (hour minute)
+ "Generate time for today at HOUR:MINUTE.
+Returns (year month day hour minute) list suitable for tests."
+ (let* ((now (decode-time))
+ (year (nth 5 now))
+ (month (nth 4 now))
+ (day (nth 3 now)))
+ (list year month day hour minute)))
+
+(defun test-calendar-sync-time-tomorrow-at (hour minute)
+ "Generate time for tomorrow at HOUR:MINUTE."
+ (let* ((tomorrow (time-add (current-time) (* 24 3600)))
+ (decoded (decode-time tomorrow))
+ (year (nth 5 decoded))
+ (month (nth 4 decoded))
+ (day (nth 3 decoded)))
+ (list year month day hour minute)))
+
+(defun test-calendar-sync-time-days-from-now (days hour minute)
+ "Generate time for DAYS from now at HOUR:MINUTE."
+ (let* ((future (time-add (current-time) (* days 24 3600)))
+ (decoded (decode-time future))
+ (year (nth 5 decoded))
+ (month (nth 4 decoded))
+ (day (nth 3 decoded)))
+ (list year month day hour minute)))
+
+(defun test-calendar-sync-time-days-ago (days hour minute)
+ "Generate time for DAYS ago at HOUR:MINUTE."
+ (let* ((past (time-subtract (current-time) (* days 24 3600)))
+ (decoded (decode-time past))
+ (year (nth 5 decoded))
+ (month (nth 4 decoded))
+ (day (nth 3 decoded)))
+ (list year month day hour minute)))
+
+(defun test-calendar-sync-time-date-only (offset-days)
+ "Generate date-only timestamp for OFFSET-DAYS from now.
+Returns (year month day) list for all-day events and UNTIL dates."
+ (let* ((future (time-add (current-time) (* offset-days 24 3600)))
+ (decoded (decode-time future))
+ (year (nth 5 decoded))
+ (month (nth 4 decoded))
+ (day (nth 3 decoded)))
+ (list year month day)))
+
+(defun test-calendar-sync-time-date-only-ago (offset-days)
+ "Generate date-only timestamp for OFFSET-DAYS ago.
+Returns (year month day) list for UNTIL dates in the past."
+ (let* ((past (time-subtract (current-time) (* offset-days 24 3600)))
+ (decoded (decode-time past))
+ (year (nth 5 decoded))
+ (month (nth 4 decoded))
+ (day (nth 3 decoded)))
+ (list year month day)))
+
+(defun test-calendar-sync-date-only-from-datetime (datetime)
+ "Extract date-only (year month day) from DATETIME list.
+DATETIME is (year month day hour minute).
+Returns (year month day) suitable for UNTIL dates."
+ (list (nth 0 datetime) (nth 1 datetime) (nth 2 datetime)))
+
+;;; .ics Test Data Generation
+
+(defun test-calendar-sync-ics-datetime (time-list)
+ "Convert TIME-LIST to iCal DATETIME format.
+TIME-LIST is (year month day hour minute).
+Returns string like '20251116T140000Z'."
+ (format "%04d%02d%02dT%02d%02d00Z"
+ (nth 0 time-list)
+ (nth 1 time-list)
+ (nth 2 time-list)
+ (nth 3 time-list)
+ (nth 4 time-list)))
+
+(defun test-calendar-sync-ics-date (time-list)
+ "Convert TIME-LIST to iCal DATE format.
+TIME-LIST is (year month day).
+Returns string like '20251116'."
+ (format "%04d%02d%02d"
+ (nth 0 time-list)
+ (nth 1 time-list)
+ (nth 2 time-list)))
+
+(defun test-calendar-sync-make-vevent (summary start end &optional description location)
+ "Create a VEVENT block for testing.
+START and END are time lists from test-calendar-sync-time-* functions.
+Returns .ics formatted VEVENT string."
+ (let* ((dtstart (if (= (length start) 5)
+ (test-calendar-sync-ics-datetime start)
+ (test-calendar-sync-ics-date start)))
+ (dtend (when end
+ (if (= (length end) 5)
+ (test-calendar-sync-ics-datetime end)
+ (test-calendar-sync-ics-date end)))))
+ (concat "BEGIN:VEVENT\n"
+ "SUMMARY:" summary "\n"
+ "DTSTART:" dtstart "\n"
+ (when dtend (concat "DTEND:" dtend "\n"))
+ (when description (concat "DESCRIPTION:" description "\n"))
+ (when location (concat "LOCATION:" location "\n"))
+ "END:VEVENT")))
+
+(defun test-calendar-sync-make-ics (&rest events)
+ "Create complete .ics file with EVENTS.
+Each event should be a VEVENT string from `test-calendar-sync-make-vevent'."
+ (concat "BEGIN:VCALENDAR\n"
+ "VERSION:2.0\n"
+ "PRODID:-//Test//Test//EN\n"
+ (string-join events "\n")
+ "\nEND:VCALENDAR"))
+
+;;; Property Test Helpers
+
+(defun test-calendar-sync-random-future-date ()
+ "Generate random date 1-180 days in future with random time.
+Returns (year month day hour minute) list."
+ (test-calendar-sync-time-days-from-now
+ (1+ (random 180))
+ (random 24)
+ (random 60)))
+
+(defun test-calendar-sync-random-past-date ()
+ "Generate random date 1-90 days in past with random time.
+Returns (year month day hour minute) list."
+ (test-calendar-sync-time-days-ago
+ (1+ (random 90))
+ (random 24)
+ (random 60)))
+
+(defun test-calendar-sync-random-weekday-subset ()
+ "Generate random non-empty subset of weekdays.
+Returns list of weekday strings like (\"MO\" \"WE\" \"FR\")."
+ (let ((days '("MO" "TU" "WE" "TH" "FR" "SA" "SU"))
+ (result '()))
+ (dolist (day days)
+ (when (zerop (random 2))
+ (push day result)))
+ ;; Ensure non-empty
+ (or result (list (nth (random 7) days)))))
+
+(defun test-calendar-sync-random-freq ()
+ "Return random RRULE frequency symbol."
+ (nth (random 4) '(daily weekly monthly yearly)))
+
+(defun test-calendar-sync-days-between (date1 date2)
+ "Calculate days between DATE1 and DATE2.
+Both dates are (year month day ...) lists.
+Returns float number of days (positive if date2 > date1)."
+ (let ((t1 (calendar-sync--date-to-time (list (nth 0 date1) (nth 1 date1) (nth 2 date1))))
+ (t2 (calendar-sync--date-to-time (list (nth 0 date2) (nth 1 date2) (nth 2 date2)))))
+ (/ (float-time (time-subtract t2 t1)) 86400.0)))
+
+(defun test-calendar-sync-wide-range ()
+ "Generate wide date range: 90 days past to 365 days future.
+Returns (start-time end-time) suitable for expansion functions."
+ (list (time-subtract (current-time) (* 90 86400))
+ (time-add (current-time) (* 365 86400))))
+
+(defun test-calendar-sync-narrow-range ()
+ "Generate narrow date range: today to 30 days future.
+Returns (start-time end-time) suitable for expansion functions."
+ (list (current-time)
+ (time-add (current-time) (* 30 86400))))
+
+(defun test-calendar-sync-date-to-time-value (date)
+ "Convert DATE list to Emacs time value.
+DATE is (year month day) or (year month day hour minute)."
+ (let ((year (nth 0 date))
+ (month (nth 1 date))
+ (day (nth 2 date))
+ (hour (or (nth 3 date) 0))
+ (minute (or (nth 4 date) 0)))
+ (encode-time 0 minute hour day month year)))
+
+(provide 'testutil-calendar-sync)
+;;; testutil-calendar-sync.el ends here