diff options
| author | Craig Jennings <c@cjennings.net> | 2026-07-16 15:59:50 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-07-16 15:59:50 -0500 |
| commit | f30a96edd320337ea9a35d2f0fc0cf21f94deb05 (patch) | |
| tree | cf9f111293ef8d8772f2de3ff3016578e131cd0c | |
| parent | 5803bab70d4b6298877bd8c8b96b0813e875f1c0 (diff) | |
| download | dotemacs-f30a96edd320337ea9a35d2f0fc0cf21f94deb05.tar.gz dotemacs-f30a96edd320337ea9a35d2f0fc0cf21f94deb05.zip | |
INTEGRATION_TESTS globbed test-integration-%.el through $(wildcard ...), which
takes a shell glob where % is a literal character, not a pattern. It expanded to
nothing, so test-all skipped the integration phase and still printed "All tests
complete". Fourteen files never ran, for as long as the variable has existed.
The % is right one line up, because filter-out does take make patterns.
The glob is now test-integration-*.el, and the counts add up: 597 unit + 14
integration = the 611 test files on disk.
Turning the gate on surfaced five failures, none of them in production code.
Four were rotting fixtures. parse-ics drops events outside a rolling window of
today minus calendar-sync-past-months to plus future-months, and four tests fed
it dates pinned to November 2025 or February 2026. They passed when written and
began failing once the window slid past them. Their neighbours survived for two
reasons: some hand their fixture to parse-event, which applies no window, and
the weekly ones use unbounded RRULEs that keep generating into the window
however old the DTSTART is. The fixtures now build dates relative to now, which
is what the timezone file's own helper already did.
The fifth was a real regression, caught five months late. The recording toggle
test asserted on a fixture device and got whatever hardware is plugged into the
developer's machine. cj/recording-get-devices runs --validate-system-audio,
which shells to pactl, finds the fixture device isn't a real source, and
auto-fixes the configured device to the default sink's monitor. The test mocked
start-process-shell-command but not shell-command-to-string, so validation
reached the real machine. It passes at 5bdd3420~1 and fails at 5bdd3420, the
commit that added that validation in February.
I faked pactl at the shell boundary rather than stubbing --validate-system-audio
out, so the validation logic still runs, against a fixture machine.
| -rw-r--r-- | Makefile | 7 | ||||
| -rw-r--r-- | tests/test-integration-calendar-sync-timezone.el | 17 | ||||
| -rw-r--r-- | tests/test-integration-recording-toggle-workflow.el | 22 | ||||
| -rw-r--r-- | tests/test-integration-recurring-events.el | 92 |
4 files changed, 101 insertions, 37 deletions
@@ -32,8 +32,13 @@ EMACS_HOME = $(HOME)/.emacs.d OUT ?= themes # Test files +# NOTE: filter-out takes make patterns, where % is the wildcard; $(wildcard ...) +# takes a shell glob, where % is a literal character and * is the wildcard. The +# two are not interchangeable. INTEGRATION_TESTS used a % here and so expanded +# to nothing, which silently skipped every integration test in `make test' while +# it still printed ALL TESTS COMPLETE. UNIT_TESTS = $(filter-out $(TEST_DIR)/test-integration-%.el, $(wildcard $(TEST_DIR)/test-*.el)) -INTEGRATION_TESTS = $(wildcard $(TEST_DIR)/test-integration-%.el) +INTEGRATION_TESTS = $(wildcard $(TEST_DIR)/test-integration-*.el) ALL_TESTS = $(UNIT_TESTS) $(INTEGRATION_TESTS) BASH_TESTS = $(wildcard $(TEST_DIR)/*.bats) diff --git a/tests/test-integration-calendar-sync-timezone.el b/tests/test-integration-calendar-sync-timezone.el index 304d3233..a3f65146 100644 --- a/tests/test-integration-calendar-sync-timezone.el +++ b/tests/test-integration-calendar-sync-timezone.el @@ -187,12 +187,21 @@ Components integrated: Validates: - Org timestamp format is correct (<YYYY-MM-DD Day HH:MM-HH:MM>) -- Hour in timestamp is the converted local hour" - (let* ((source-time (list 2026 2 2 19 0)) +- Hour in timestamp is the converted local hour + +The date is generated relative to now because `calendar-sync--parse-ics' +drops events outside `calendar-sync--get-date-range' (today minus +`calendar-sync-past-months', plus `calendar-sync-future-months'). This test +used a hardcoded 2026-02-02, which sat inside that window when it was written +and fell out of it once three months had passed -- the event was filtered +before rendering and the assertion failed against an empty org buffer. The +sibling tests survived hardcoded dates only because they call +`calendar-sync--parse-event' directly, which applies no range filter." + (let* ((source-time (test-calendar-sync-time-days-from-now 7 19 0)) (ics (test-integration-tz--make-ics-with-tzid-event "Test Event" source-time "Europe/Lisbon")) - (expected-local (test-calendar-sync-convert-tz-via-date - 2026 2 2 19 0 "Europe/Lisbon")) + (expected-local (apply #'test-calendar-sync-convert-tz-via-date + (append source-time (list "Europe/Lisbon")))) (expected-hour (nth 3 expected-local)) (org-output (calendar-sync--parse-ics ics))) (should org-output) diff --git a/tests/test-integration-recording-toggle-workflow.el b/tests/test-integration-recording-toggle-workflow.el index e73ef87e..6258fc58 100644 --- a/tests/test-integration-recording-toggle-workflow.el +++ b/tests/test-integration-recording-toggle-workflow.el @@ -43,6 +43,24 @@ ;;; Setup and Teardown +(defun test-integration-toggle--fake-pactl (cmd &rest _) + "Answer pactl queries for CMD from fixture devices, never the real machine. + +`cj/recording-get-devices' runs `cj/recording--validate-system-audio', which +shells out to pactl, decides a fixture device name is not a real source, and +\"auto-fixes\" the configured device to the default sink's monitor. Left +unmocked that clobbers the test's device with whatever hardware the developer +has plugged in, so the assertions compare against a JDS Labs DAC rather than +the fixture. Answering at the shell boundary keeps the real validation logic +under test while the machine stays out of it." + (cond + ((string-match-p "get-default-sink" cmd) "fixture-sink\n") + ((string-match-p "list sources short" cmd) + "0\ttest-monitor\tmodule-x\ts16le 2ch 44100Hz\tIDLE\n1\tcached-monitor\tmodule-x\ts16le 2ch 44100Hz\tIDLE\n") + ((string-match-p "list sinks short" cmd) + "0\tfixture-sink\tmodule-x\ts16le 2ch 44100Hz\tRUNNING\n") + (t ""))) + (defun test-integration-toggle-setup () "Reset all variables before each test." (setq cj/video-recording-ffmpeg-process nil) @@ -102,6 +120,8 @@ Validates: (setq setup-called t) (setq cj/recording-mic-device "test-mic") (setq cj/recording-system-device "test-monitor"))) + ((symbol-function 'shell-command-to-string) + #'test-integration-toggle--fake-pactl) ((symbol-function 'file-directory-p) (lambda (_dir) t)) ((symbol-function 'start-process-shell-command) @@ -168,6 +188,8 @@ Validates: (ffmpeg-cmd nil)) (cl-letf (((symbol-function 'cj/recording-quick-setup) (lambda () (setq setup-called t))) + ((symbol-function 'shell-command-to-string) + #'test-integration-toggle--fake-pactl) ((symbol-function 'file-directory-p) (lambda (_dir) t)) ((symbol-function 'start-process-shell-command) diff --git a/tests/test-integration-recurring-events.el b/tests/test-integration-recurring-events.el index 3cae1a20..4e778547 100644 --- a/tests/test-integration-recurring-events.el +++ b/tests/test-integration-recurring-events.el @@ -34,53 +34,78 @@ ;;; Test Data -(defconst test-integration-recurring-events--weekly-ics - "BEGIN:VCALENDAR -VERSION:2.0 -PRODID:-//Test//Test//EN -BEGIN:VEVENT -DTSTART;TZID=America/Chicago:20251118T103000 -DTEND;TZID=America/Chicago:20251118T110000 -RRULE:FREQ=WEEKLY;BYDAY=SA -SUMMARY:GTFO -UID:test-weekly@example.com -END:VEVENT -END:VCALENDAR" - "Test ICS with weekly recurring event (GTFO use case).") +;; Fixtures that reach `calendar-sync--parse-ics' must carry dates relative to +;; now. That entry point drops any event outside +;; `calendar-sync--get-date-range' (today minus `calendar-sync-past-months', +;; plus `calendar-sync-future-months'), so a hardcoded DTSTART works only until +;; the rolling window moves past it. Three tests here rotted exactly that way: +;; their November-2025 fixtures aged out of the window, the events were filtered +;; before rendering, and the assertions failed against an empty org buffer. The +;; weekly fixtures survived only because an unbounded RRULE keeps generating +;; occurrences into the window no matter how old its DTSTART is. +;; +;; Fixtures given straight to `calendar-sync--parse-event' can stay static -- +;; that path applies no range filter. -(defconst test-integration-recurring-events--daily-with-count-ics - "BEGIN:VCALENDAR +(defun test-integration-recurring-events--ics-stamp (offset-days hour minute) + "Return an ICS UTC datetime OFFSET-DAYS from today at HOUR:MINUTE." + (let ((d (test-calendar-sync-time-days-from-now offset-days hour minute))) + (format "%04d%02d%02dT%02d%02d00Z" (nth 0 d) (nth 1 d) (nth 2 d) hour minute))) + +(defun test-integration-recurring-events--daily-with-count-ics () + "ICS with a COUNT=5 daily series starting inside the sync window." + (format "BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Test//Test//EN BEGIN:VEVENT -DTSTART:20251120T090000Z -DTEND:20251120T100000Z +DTSTART:%s +DTEND:%s RRULE:FREQ=DAILY;COUNT=5 SUMMARY:Daily Standup UID:test-daily@example.com END:VEVENT END:VCALENDAR" - "Test ICS with daily recurring event limited by COUNT.") + (test-integration-recurring-events--ics-stamp 2 9 0) + (test-integration-recurring-events--ics-stamp 2 10 0))) -(defconst test-integration-recurring-events--mixed-ics - "BEGIN:VCALENDAR +(defun test-integration-recurring-events--mixed-ics () + "ICS mixing a one-time event and a recurring one, both inside the window." + (format "BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Test//Test//EN BEGIN:VEVENT -DTSTART:20251125T140000Z -DTEND:20251125T150000Z +DTSTART:%s +DTEND:%s SUMMARY:One-time Meeting UID:test-onetime@example.com END:VEVENT BEGIN:VEVENT -DTSTART;TZID=America/Chicago:20251201T093000 -DTEND;TZID=America/Chicago:20251201T103000 +DTSTART;TZID=America/Chicago:%s +DTEND;TZID=America/Chicago:%s RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR SUMMARY:Recurring Standup UID:test-recurring@example.com END:VEVENT END:VCALENDAR" - "Test ICS with mix of recurring and non-recurring events.") + (test-integration-recurring-events--ics-stamp 3 14 0) + (test-integration-recurring-events--ics-stamp 3 15 0) + ;; TZID form carries no Z suffix. + (string-remove-suffix "Z" (test-integration-recurring-events--ics-stamp 5 9 30)) + (string-remove-suffix "Z" (test-integration-recurring-events--ics-stamp 5 10 30)))) + +(defconst test-integration-recurring-events--weekly-ics + "BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Test//Test//EN +BEGIN:VEVENT +DTSTART;TZID=America/Chicago:20251118T103000 +DTEND;TZID=America/Chicago:20251118T110000 +RRULE:FREQ=WEEKLY;BYDAY=SA +SUMMARY:GTFO +UID:test-weekly@example.com +END:VEVENT +END:VCALENDAR" + "Test ICS with weekly recurring event (GTFO use case).") ;;; Normal Cases - Complete Workflow @@ -133,7 +158,7 @@ Validates: - Exactly 5 occurrences created" (test-integration-recurring-events-setup) (unwind-protect - (let ((org-output (calendar-sync--parse-ics test-integration-recurring-events--daily-with-count-ics))) + (let ((org-output (calendar-sync--parse-ics (test-integration-recurring-events--daily-with-count-ics)))) (should (stringp org-output)) ;; Should generate exactly 5 Daily Standup entries @@ -160,7 +185,7 @@ Validates: - Events are sorted chronologically" (test-integration-recurring-events-setup) (unwind-protect - (let ((org-output (calendar-sync--parse-ics test-integration-recurring-events--mixed-ics))) + (let ((org-output (calendar-sync--parse-ics (test-integration-recurring-events--mixed-ics)))) (should (stringp org-output)) ;; Should have one-time meeting @@ -291,18 +316,21 @@ Validates: - Valid events still processed" (test-integration-recurring-events-setup) (unwind-protect - (let* ((incomplete-ics "BEGIN:VCALENDAR + (let* ((incomplete-ics (format "BEGIN:VCALENDAR VERSION:2.0 BEGIN:VEVENT -DTSTART:20251201T100000Z +DTSTART:%s RRULE:FREQ=DAILY;COUNT=2 END:VEVENT BEGIN:VEVENT SUMMARY:Valid Event -DTSTART:20251201T110000Z -DTEND:20251201T120000Z +DTSTART:%s +DTEND:%s END:VEVENT -END:VCALENDAR") +END:VCALENDAR" + (test-integration-recurring-events--ics-stamp 4 10 0) + (test-integration-recurring-events--ics-stamp 4 11 0) + (test-integration-recurring-events--ics-stamp 4 12 0))) (org-output (calendar-sync--parse-ics incomplete-ics))) ;; Should still generate output (for valid event) (should (stringp org-output)) |
