| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
| |
Every integration test is tagged :slow, so the default run selected none of them and then printed "All integration tests passed" over four "Ran 0 tests" lines.
The target now sums the tests actually run. Zero means it says skipped and names make test-all; a real run reports its count. A failing file still exits non-zero.
This is the third false-green in this Makefile. The other two read $? after a pipeline (7e03862).
|
| |
|
|
|
|
|
|
| |
process-live-p goes nil the moment the child exits, but its sentinel is queued and runs on a later turn of the event loop. Two sound tests stopped waiting at process death, so the sentinel's message landed inside the next test's message mock.
make coverage caught it. Instrumentation slowed things just enough that "false exited with status 1" showed up in the silent-player test. The plain suite had been passing by luck.
Both tests now wait for the sentinel to have run.
|
| |
|
|
|
|
| |
The watchdog suite stubs the whole process layer, so nothing anywhere verified the signal semantics the watchdog rests on. These :slow tests spawn a shell that traps SIGINT and let the kernel deliver the real signals.
The first one pins the premise: the child survives interrupt-process and dies to chime--kill-async-process.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Three faults in one path, all made reachable by the watchdog.
A callback returning just past the timeout ran against state that no longer belonged to it. It nil'd chime--process, which by then held the replacement child, so the overlap guard broke and a third child could spawn. It also reset the failure counter the watchdog had just incremented. Each spawn now captures a generation, and a result whose generation is stale is discarded whole.
interrupt-process only asks. A child stuck in a blocking read ignores SIGINT and lives on as a zombie, invisible because chime--process was already cleared. The watchdog and chime--stop now call chime--kill-async-process, which silences the sentinel and calls delete-process.
async.el kills a child's process buffer only on a zero exit, so every signalled child leaked one. With a persistent hang that's a buffer per chime-async-timeout until Emacs restarts. The kill path reaps the buffer itself.
chime--stop also clears chime--consecutive-async-failures and chime--process-start-time, so a later chime-mode doesn't resume with the old count.
|
| |
|
|
|
|
| |
The tests cover a fresh result being processed, a stale one discarded without touching the replacement child's handle or the failure counter, the watchdog and chime--stop both superseding the generation, and chime--kill-async-process reaping a real child and its buffer.
Where a mock can't show the behavior, the tests spawn real processes. async.el's buffer handling and the sentinel silencing are the thing under test.
|
| |
|
|
|
|
| |
chime--process-notifications maps chime--notify over every due event, and the alert call was unguarded. One dbus error partway through dropped every notification after it, and the caller's condition-case then miscounted the whole thing as a fetch failure, walking the counter toward the persistent-failure warning.
Each event's alert now fails on its own and reports to *Messages*.
|
| |
|
|
| |
One test asserts that an alert error is reported rather than signalled. The other asserts that a failing event doesn't cost us the notifications after it, driving chime--process-notifications over three events with the middle one raising.
|
| |
|
|
|
|
| |
alsa-lib parses its configuration once per process, so an emacs --daemon started before the config landed keeps the old view. play-sound-file keeps failing there while it succeeds in a fresh emacs -Q, which reads as the system fix not having worked.
Hit this on my own daemon after adding the pipewire default symlink: nine hours' uptime, still no usable default PCM.
|
| |
|
|
|
|
|
|
| |
Three sound tests mocked message and counted everything it captured. Under make coverage, chime.el loads as instrumented source, so the sentinel lambda compiles on first call and emits "Warning: argument '_event' not left unused" through message. That warning landed in the capture list, the counts came out one high, and the Coverage job failed while all five Emacs versions passed.
The tests now filter captured messages to chime's own. What they mean is that chime reported the failure once, not that nothing in Emacs said anything.
make test and make test-all never load the instrumented source, so they can't catch this. make coverage reproduces it.
|
| |
|
|
|
|
|
|
|
|
| |
Both targets read $? after a pipeline, so they got the exit status of the last command in it rather than of Emacs.
test-file piped Emacs through tee, so it printed "All tests passed" over a wall of FAILED lines and exited 0. It now redirects to the log, saves the status, and cats the log back.
validate piped Emacs through grep, and grep matched the failure line it was looking for, so a file with unbalanced parens printed its error in green and counted as validated. It now captures the output and the status separately. Verified against a deliberately broken file: before the fix, "All 78 files validated successfully" and exit 0.
make test and make test-all were never affected. They shell out to test-unit and test-integration without a pipeline.
|
| |
|
|
|
|
|
|
| |
The child sizes its agenda span from chime-modeline-lookahead-minutes and chime-tooltip-lookahead-hours, but neither was in chime-default-environment-regex. The child requires chime, so both were bound there at their defaults. Customizing either did nothing past the default eight days, with no error to explain why. The tooltip docstring invites 8760 hours to see distant events, and that setting was inert.
I added both names to the injected set.
The test that matters walks the child form and fails on any chime defcustom it reads that the regex doesn't cover, so a variable added to the child later can't reintroduce this quietly. Its companions pin the regex's anchoring and the additional-regexes extension point.
|
| |
|
|
|
|
|
|
| |
chime--days-until-event maps over every timestamp on an event and keeps a day count only for the all-day ones. A timed timestamp maps to nil. An event carrying both kinds handed -min a list with a nil in it, which signals wrong-type-argument. An event with no all-day timestamp handed it an empty list, which signals too.
Both crash on every tick once chime-day-wide-advance-notice is on. The recurring failure walks the async counter up to the persistent-failure warning.
The map now runs through -non-nil, and an empty result returns nil, which the docstring already promised. I added a nil branch to chime--day-wide-notification-text so a nil count can't reach the = comparisons. The advance-notice window should already exclude an event with no all-day timestamp, so that branch is a guard rather than a live path.
|
| |
|
|
|
|
| |
The tests cover a lone all-day timestamp, the soonest of several, an event mixing timed and all-day timestamps, an event with no all-day timestamp, and an empty timestamp list. A sixth covers the notification text when the day count comes back nil.
I built the timestamps against the real current time, not testutil-time's base, which sits 30 days ahead. chime--days-until-event measures against current-time, so the shared helper would have made every expected day count wrong.
|
| |
|
|
|
|
| |
CONFIGURATION.org covers how the player is chosen and when chime-sound-device applies.
TROUBLESHOOTING.org names the "No usable sound device driver found" error, how to confirm it outside Emacs with aplay, and the two ways out: name a device that resolves, or repair the system so default routes to PipeWire.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Emacs' play-sound-file opens an ALSA device directly, so it fails with "No usable sound device driver found" wherever ALSA's default PCM doesn't resolve. That's every PipeWire system where nothing points pcm.!default at the sound server. It also blocks Emacs until the clip finishes, and on macOS it isn't compiled in at all.
chime now hands the file to the first of pw-play, paplay, afplay or aplay found on exec-path, spawned asynchronously. I put aplay last because it speaks raw ALSA and shares play-sound-file's failure mode, so falling back to it from a broken default PCM would buy nothing. With no external player installed, play-sound-file still runs, now taking chime-sound-device as its DEVICE argument.
Two new defcustoms: chime-sound-player (auto, emacs, or a command name) and chime-sound-device. Both default to today's behavior for anyone whose sound already works.
A player that spawns and then exits non-zero is reported through a process sentinel rather than failing silently. The sentinel doesn't fall back to play-sound-file, which would reintroduce the blocking ALSA call this change avoids.
I pinned the notify tests to emacs so they mock one function and never spawn a real player.
|
| |
|
|
|
|
| |
The tests cover player selection, asynchronous spawn, the fallback to play-sound-file, DEVICE passthrough, the exit-status sentinel, and both players failing.
I mock play-sound-file everywhere, so no test opens an audio device. Two tests spawn true or false as a stand-in player, because neither the returned process nor the sentinel's exit status survives a mock.
|
| |
|
|
|
|
|
|
|
|
| |
Every test hand-wrote the same scaffolding: call a per-file setup, wrap the body in unwind-protect, call a per-file teardown. That's 419 unwind-protect blocks and 68 near-identical setup/teardown defuns, with no shared fixture because ERT has no native per-test setup.
I added a chime-deftest macro in test-bootstrap that creates the test base directory before the body and deletes it afterward, even on failure. I converted the 10 files whose fixture was exactly that base-dir create/delete and whose every test followed the standard shape, dropping their setup/teardown defuns. Files with custom fixtures (modeline state restore, validation reset) keep explicit setup/teardown, since the macro would silently drop their extra logic. I left the 4 tag-bearing integration files alone.
I also moved the near-universal testutil-general and testutil-time requires into test-bootstrap, so individual files no longer repeat them.
Behavior is unchanged: still 800 tests, full suite green. Converted bodies are copied verbatim, so the change only removes scaffolding, never an assertion.
|
| |
|
|
| |
The function matched parens, brackets, and braces with three near-identical cond branches plus a parallel opener-to-closer mapping. That's one balancing algorithm written three times. I replaced it with a single ((open . close) ...) alist driven by assq/rassq. Adding or changing a delimiter pair is now a one-line edit, and there's one code path to reason about. Behavior is unchanged. The existing 30 characterization tests stay green.
|
| |
|
|
|
|
| |
test-chime-apply-blacklist and test-chime-whitelist-blacklist-conflicts reset the filter defcustoms with setq in their teardowns. The filters aren't buffer-local, so setq clobbers the global default and leaves it nil for every later test in the same Emacs. test-chime-declined-events-predicate-on-default-exclude-filters reads default-value, so it failed in a combined full-suite run while passing per-file.
The teardowns now call custom-reevaluate-setting to restore each filter to its standard value. Every test already let-binds its own filter values, so the global only needs returning to its default.
|
| |
|
|
|
|
| |
The modeline event text inherited the mode-line face with no way to style it. I added four faces (chime-modeline-face, chime-modeline-soon-face, chime-modeline-urgent-face, chime-modeline-no-events-face) that color the display by how soon the next event is.
Two thresholds, chime-modeline-soon-threshold-minutes (30) and chime-modeline-urgent-threshold-minutes (5), drive the urgency mapping. The default and idle faces inherit mode-line, so the modeline looks unchanged until you theme it. Soon inherits warning and urgent inherits error, so imminent events stand out across themes without configuration.
|
| | |
|
| |
|
|
| |
The startup language-bundle sync repaired drift in these rulesets-owned files. Committing them at canonical so future syncs stay no-ops.
|
| |
|
|
| |
MELPA and list-packages index the summary line, and the old backronym carried no words anyone would search. I reworded it to plain terms and led the Keywords header with a standard finder keyword so package-lint stays clean. I synced the Eask file to match.
|
| |
|
|
| |
GitHub's org renderer drops #+begin_verse blocks, so the Fitzgerald lines vanished on the rendered page. Blank-line separation stacks them on both GitHub and Emacs export. Also reword the opening sentence, which had leaned on the removed Douglas Adams "whoosh" line.
|
| |
|
|
| |
Swap the Douglas Adams deadlines quote for Edward Fitzgerald's sundial verse, which names the chime directly.
|
| | |
|
| | |
|
| |
|
|
| |
A child that never returns produces no error sexp, so no failure path ever sees it. The failure counter stays at zero and the overlap guard blocks every subsequent check. The watchdog interrupts a fetch older than chime-async-timeout (default 120s, nil disables), records it through chime--record-async-failure, and lets the same check spawn a fresh child.
|
| |
|
|
| |
A vanished org-agenda-files entry hit org's interactive "[R]emove from list or [A]bort?" recovery prompt inside the -batch child, which blocked forever. The overlap guard then turned every subsequent check into a silent no-op and the modeline froze on a stale countdown. org-agenda-skip-unavailable-files makes org skip the missing file with a message instead.
|
| | |
|
| |
|
|
| |
The file was added to Eask's (files ...) list in 675fb68 but kept its standalone Version, Package-Requires, and Keywords headers. package-lint flags those as "Package-Requires outside the main file have no effect" and the lint step fails CI. Match the sibling auxiliary-file pattern (chime-debug.el, chime-org-contacts.el): keep Author + URL, add the convention note, drop the redundant metadata.
|
| |
|
|
| |
The PostToolUse hook tried to byte-compile every .el file edited, including ones outside the chime project. Out-of-project files typically require packages that aren't on the hook's -L paths, so validation failed for unrelated reasons. Exit 0 when the edited file isn't under $PROJECT_ROOT.
|
| |
|
|
| |
The unit tests call chime--gather-info on a marker built in the test. I added a :slow test that goes one layer up. It puts a SCHEDULED heading with :CHIME_NOTIFY_BEFORE: 30 in a temp .org file, runs org-agenda-list, and gathers events the way chime--retrieve-events does in the async child (split the agenda buffer into lines, pull the org-marker from each, drop nils, map chime--gather-info). The resulting event carries ((30 . medium)). That confirms the override survives a marker that came out of org-agenda-list, not just one built in the test.
|
| |
|
|
| |
The README's migration note said the per-event reminder property had been removed. That's stale now that the property is back. I rewrote the line to point at :CHIME_NOTIFY_BEFORE: and the deprecated :WILD_NOTIFIER_NOTIFY_BEFORE: alias. I also added a "Per-Event Override" section to docs/CONFIGURATION.org covering the syntax, the no-inheritance behavior, malformed-value handling, and the alias's deprecation path.
|
| |
|
|
|
|
|
|
|
|
| |
Chime's migration from org-wild-notifier dropped the per-event notify-before property, so a heading could no longer override the global chime-alert-intervals. I brought it back.
A heading's :CHIME_NOTIFY_BEFORE: N (a non-negative integer) means "notify once, N minutes before, severity medium" and replaces chime-alert-intervals for that event. The org-wild-notifier name :WILD_NOTIFIER_NOTIFY_BEFORE: still works, as a deprecated alias. When it supplies the value, chime emits one per-session warning pointing at the new name. :CHIME_NOTIFY_BEFORE: wins when both are set. A malformed value logs a message naming the heading and falls back to the global.
chime--gather-info reads the property in the async child. The deprecation flag rides on the event alist so the parent emits the warning without changing the async return contract.
I also touched two test files. test-chime-async-helpers.el's fixtures fed bare symbols where chime--handle-async-success now expects event alists, so they build real events via chime--make-event. And the new test file's message-capture macro had a lexical-scoping bug (the captured list was invisible after the macro returned), so the two affected tests inline the capture and the macro is gone.
|
| |
|
|
|
|
| |
I added fifteen tests ahead of the implementation, covering the per-event :CHIME_NOTIFY_BEFORE: property and the deprecated :WILD_NOTIFIER_NOTIFY_BEFORE: alias: value-string parsing (chime--parse-notify-before-value), property lookup with global fallback (chime--intervals-for-marker), the gather path (chime--gather-info honoring the property and flagging the alias on the event), and the once-per-session deprecation warning (chime--maybe-warn-deprecated-properties).
They all fail with void-function for the not-yet-defined helpers, plus a length mismatch on the gather-info tests since chime--gather-info still uses the global chime-alert-intervals.
|
| |
|
|
|
|
| |
TESTING.org carried a frozen "645 tests as of 2026-04-04" inventory plus a top-files table that goes stale every time a test file is added. I replaced it with a one-line pointer to `make count`, which is the live source.
README's History section said Artem Khramov "deprecated org-wild-notifier in favor of org-alert." He didn't. He archived the repo and stopped responding to issues. The README's "Alternatives" section listed org-alert and org-notify neutrally, never as recommended successors. And the project has since been picked back up under the Emacs Orphanage. I rewrote the paragraph to describe what happened and added a line about the orphanage fork.
|
| |
|
|
|
|
| |
Five new test files cover branches the per-function suites missed: the day-wide notification pipeline, the jump-to-event navigation path (including the org-show-entry fallback for Org < 9.6), chime--stop's process-interrupt branch, chime--start's debug log, and the two async error branches in chime--fetch-and-process. The edge-coverage file mops up scattered one-line fallbacks: the day-wide-notification "today" path, the tooltip placeholder pass-through, timestamp-parse's no-context error message, log-silently's mid-line newline insert, the validation :error count, and record-async-failure's chime-debug hook.
Line coverage on chime.el goes from 97.1% to 99.88%, 823 of 824 coverable lines. The one remaining line is a pcase _ fallback the preceding regex can't reach.
|
| |
|
|
|
|
| |
The validation banner, retry-attempt message, async-failure tooltip, and the initial loading tooltip were hardcoded English literals. I added defcustoms for them so the wording can be localized or adjusted. The format-string ones keep their %d/%s directives, with the order spelled out in each docstring.
I also dropped the dead :info severity from chime-validate-configuration's docstring. The function only ever emitted :ok, :warning, and :error.
|
| | |
|
| | |
|
| | |
|
| | |
|
| |
|
|
|
|
| |
The .claude/rules/ files (commits.md, testing.md, verification.md) were local copies that drifted from the canonical at ~/code/rulesets. I replaced them with absolute symlinks pointing at the canonical files. This repo now picks up rule updates automatically.
The symlinks are absolute. Cloning this repo on a new machine needs ~/code/rulesets at the same path. Otherwise the symlinks dangle.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Six per-axis filter variables (keyword/tags/predicate × whitelist/blacklist)
were carrying parallel structure for what's really one decision: include
events that look like X, exclude events that look like Y. I merged them
into two alists, `chime-include-filters' and `chime-exclude-filters', each
keyed by axis (`keywords', `tags', `predicates').
Default for `chime-exclude-filters' keeps the same out-of-the-box
behavior — done items and declined Google Calendar invites stay
filtered:
((predicates . (chime-done-keywords-predicate
chime-declined-events-predicate)))
Implementation: one shared `chime--filter-predicates' helper that walks
the alist and emits a marker-taking predicate per non-empty axis. The
public callers — now `chime--apply-include-filters' and
`chime--apply-exclude-filters' — wrap that helper with
`-orfn'-then-`-filter' and `-orfn'-then-`-remove' respectively. The
async-environment regex injection list shrank from six names to two,
and the debug config dump in chime-debug.el follows.
The terminology shift (whitelist/blacklist → include/exclude) drops
loaded language for descriptive intent. The internal helpers and the
public function names all moved together.
Tests: 700-ish lines across five test files (test-chime-apply-whitelist,
test-chime-apply-blacklist, test-chime-whitelist-blacklist-conflicts,
test-chime-environment-regex, test-chime-declined-events-predicate)
were rewritten to bind the new alists. The dedup-conflict tests still
exercise the same precedence rule (exclude wins on overlap). README's
filtering section was rewritten end-to-end with new examples.
Migration:
(setq chime-keyword-whitelist '("TODO"))
;; ->
(setq chime-include-filters '((keywords . ("TODO"))))
(setq chime-predicate-blacklist '(my-pred))
;; ->
(setq chime-exclude-filters '((predicates . (my-pred))))
This brings the consolidation pass to 37 -> 29 defcustoms, the target
from .ai/settings-consolidation.org.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
I merged `chime-time-left-format-at-event', `chime-time-left-format-short',
and `chime-time-left-format-long' into a single alist `chime-time-left-formats'
keyed by `at-event' / `short' / `long'. Three knobs for one feature
(countdown display) was unnecessary surface area; one alist is the same
flexibility with a third the namespace.
`chime--time-left' switched from a pcase-on-variable to a pcase-on-regime
that picks an alist key, then `alist-get's the format string. Behavior
is identical for default settings.
Test setup in the four affected files now builds the alist with `(list
(cons 'KEY VAL) ...)' instead of `'(...)'. The literal-quote form
returns the SAME cons-cell structure on every evaluation, so a previous
test mutating it via `setf' on `alist-get' poisoned later tests. `list'
+ `cons' produces fresh structure per call, which is what the tests
actually need.
Migration: `(setq chime-time-left-format-short "in %mm")' becomes
`(setf (alist-get 'short chime-time-left-formats) "in %mm")', or a
full `(setq chime-time-left-formats '((at-event . ...) (short . ...)
(long . ...)))' replacement.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This is an internal startup-timing parameter, not a knob real users have
reason to tune through `M-x customize'. I demoted it from defcustom to
defvar and renamed it to `chime--validation-max-retries' to make the
private status explicit. Anyone who was overriding it can keep doing so
with `setq' (the variable still exists, just under the new name).
The three customize-time validation tests went away with the defcustom —
nothing left to validate at customize-time once it stops being a
customize-target. The setter helper still applies to the other five
numeric defcustoms.
Test files that referenced the variable (`test-chime-validation-retry.el',
`test-integration-chime-mode.el') were renamed mechanically along with
the source.
Breaking change: `(setq chime-validation-max-retries N)' becomes
`(setq chime--validation-max-retries N)' if you actually had it. Most
users won't have touched it.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`chime-play-sound' was redundant with `chime-sound-file' — both had to be
truthy for sound to play, and `chime-sound-file' already documented "set
to nil to disable sound completely". Two knobs for one decision is a
trap.
I removed the defcustom and the `(when chime-play-sound ...)' guard in
`chime--notify' (it now just checks `chime-sound-file'). Tests, README
quick-start, and the troubleshooting checklist all reference the new
single-knob form. One redundant test (sound-disabled-via-the-toggle)
got dropped because it now duplicates the sound-file-nil case.
Breaking change: users with `(setq chime-play-sound nil)' in their
config will get a void-variable warning at customize time and lose
their disable. Migration: `(setq chime-sound-file nil)'.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Six numeric settings are declared as integers but were read straight into
arithmetic and timer math. A bad value (string, negative number, nil where
nil isn't supported) used to slip past the defcustom and surface as a
timer error or `arith-error' deep in a callback, instead of as a
configuration problem at the moment the user set it.
I added `chime--validate-integer-setting' as a small shared helper and
wired a `:set' on each of the affected defcustoms:
- `chime-modeline-lookahead-minutes' — integer >= 0 (0 disables)
- `chime-tooltip-lookahead-hours' — integer >= 1
- `chime-modeline-tooltip-max-events' — integer >= 1 or nil (show all)
- `chime-day-wide-advance-notice' — integer >= 0 or nil (same-day only)
- `chime-max-consecutive-failures' — integer >= 0 (0 disables warnings)
- `chime-validation-max-retries' — integer >= 0 (0 = fail immediately)
The constraints follow each docstring's stated intent. The helper signals
`user-error', so `customize-set-variable' surfaces it as a config problem
rather than a generic error trace.
Tests: 22 cases in `tests/test-chime-numeric-defcustom-setters.el' —
five direct on the helper plus each defcustom's accept/reject paths
through `customize-set-variable'.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`chime--deduplicate-events-by-title' was keying off the user-facing
title alone, so two real "1:1" entries on different days collapsed to
one and the second one disappeared from the tooltip.
I changed the key to `(marker-file . marker-pos)' — the source heading
identity — so distinct headings keep both copies and recurring expansions
of one heading still fold to the soonest. When marker info is missing
(synthesized test events, edge cases) the key falls back to title, which
preserves the older fixtures the function was originally tested against.
Three new tests cover the regression paths: two distinct headings sharing
a title both survive, one heading expanded into multiple instances still
collapses to the soonest, and a mix of sourced and unsourced events
behaves correctly under both code paths.
|