diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-10 16:36:17 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-10 16:36:17 -0500 |
| commit | 93063d8a35060d5467e2d056bd4496e51f17ddc1 (patch) | |
| tree | 666a81fdb34e758ffb1a49ab8808d0efcdd1face | |
| parent | 08cf28b2b752a9fdad10201484763f1fbb5733b3 (diff) | |
| download | chime-93063d8a35060d5467e2d056bd4496e51f17ddc1.tar.gz chime-93063d8a35060d5467e2d056bd4496e51f17ddc1.zip | |
test: harden test dependency and temp-dir setup
| -rw-r--r-- | TESTING.org | 30 | ||||
| -rw-r--r-- | tests/Makefile | 11 | ||||
| -rw-r--r-- | tests/check-deps.el | 38 | ||||
| -rw-r--r-- | tests/test-testutil-general.el | 49 | ||||
| -rw-r--r-- | tests/testutil-general.el | 31 |
5 files changed, 143 insertions, 16 deletions
diff --git a/TESTING.org b/TESTING.org index 92e4117..b1a80d1 100644 --- a/TESTING.org +++ b/TESTING.org @@ -40,7 +40,7 @@ All test logic lives in =tests/Makefile=. The root Makefile delegates to it, so | =make clean= | Remove byte-compiled files and logs | | =make help= | Show all available commands | -Each test file runs in its own Emacs process for isolation. Integration tests are identified by the =test-integration-= prefix. Dependencies are auto-detected from =~/.emacs.d/elpa/=. Override with =EMACS= or =ELPA_DIR= environment variables. +Each test file runs in its own Emacs process for isolation. Integration tests are identified by the =test-integration-= prefix. Dependency checks run inside Emacs with =package-initialize= and =require=, so any setup that makes the required features available on =load-path= works. ** Examples @@ -236,11 +236,19 @@ Builds org events and event data structures for tests. ("Birthday" bday-time nil t)) org-file (setq org-agenda-files (list org-file))) + +;; Build tooltip text from org content without manual buffer setup +(with-chime-tooltip-from-content content tooltip + (should (string-match-p "Meeting" tooltip))) #+end_src ** testutil-general.el --- File system utilities -Manages test directories and temp files under =~/.temp-chime-tests/=. +Manages test directories and temp files under =chime-test-base-dir=. By default +this is a unique per-process directory under =temporary-file-directory=, so test +runs do not write into the user's home and parallel runs do not share one fixed +root. Set =CHIME_TEST_TMPDIR= to force a specific test root when debugging or +when CI needs a predictable artifact location. | Function | Purpose | |----------+---------| @@ -252,6 +260,8 @@ Manages test directories and temp files under =~/.temp-chime-tests/=. | =chime-create-directory-or-file-ensuring-parents= | Create dir (trailing /) or file | All paths are sandboxed under =chime-test-base-dir= -- attempts to escape are rejected with an error. +Tests should create files through these helpers and clean up with +=chime-delete-test-base-dir= or the higher-level setup macros. * Key patterns @@ -281,9 +291,17 @@ All paths are sandboxed under =chime-test-base-dir= -- attempts to escape are re (chime--process-notifications events)) #+end_src +** State isolation + +Prefer lexical =let=, =with-chime-config=, =with-test-setup=, and event helpers +over file-local setup/teardown functions that save globals into defvars. Dynamic +bindings restore state automatically on both pass and failure, and helpers like +=with-gathered-events= and =with-chime-tooltip-from-content= avoid repeating temp +file and buffer cleanup logic. + * Important notes -1. *Load path*: The Makefile automatically finds dependencies in =~/.emacs.d/elpa/= +1. *Load path*: =make check-deps= verifies dependencies by requiring them inside Emacs, rather than assuming a package install directory. 2. *Fuzzy matching*: =test-file= and =test-one= support partial names 3. *Test logs*: Output saved to =test-output.log=, =test-file-output.log=, etc. in =tests/= 4. *Mock warnings*: "Redefining 'file-exists-p' might break native compilation" is normal and expected @@ -292,13 +310,15 @@ All paths are sandboxed under =chime-test-base-dir= -- attempts to escape are re * Dependencies -Required packages (auto-detected by Makefile): +Required packages (verified by =make check-deps=): - =dash= (list manipulation) - =alert= (notifications) - =async= (async processes) - =org-agenda= (built-in, events source) -Use =make check-deps= (from =tests/=) to verify all dependencies are installed. +Use =make check-deps= (from the project root or =tests/=) to verify all +dependencies are loadable. If any are missing, run =make setup= from the project +root or otherwise make the missing features available on Emacs' =load-path=. * Test inventory diff --git a/tests/Makefile b/tests/Makefile index abf91bb..8fd2e0c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -55,7 +55,12 @@ check-deps: printf "$(YELLOW)Warning: .eask not found — run 'make setup' from project root$(NC)\n"; \ exit 1; \ fi - @printf "$(GREEN)✓ eask available, deps installed$(NC)\n" + @$(EMACS_BATCH) -l check-deps.el >$(PROJECT_ROOT)/tests/check-deps-output.log 2>&1 || { \ + printf "$(RED)Error: required Emacs Lisp test dependencies are missing$(NC)\n"; \ + cat $(PROJECT_ROOT)/tests/check-deps-output.log; \ + exit 1; \ + } + @printf "$(GREEN)✓ eask available, required Emacs Lisp deps loadable$(NC)\n" # Run all tests (excluding :slow) test: check-deps @@ -260,7 +265,7 @@ lint: check-deps clean: @printf "$(YELLOW)Cleaning byte-compiled files...$(NC)\n" @rm -f *.elc ../*.elc - @rm -f test-output.log test-file-output.log test-unit-output.log test-integration-output.log + @rm -f check-deps-output.log test-output.log test-file-output.log test-unit-output.log test-integration-output.log @printf "$(GREEN)✓ Cleaned$(NC)\n" # Show help @@ -280,7 +285,7 @@ help: @echo " make count - Count tests per file" @echo " make list - List all test names" @echo " make clean - Remove byte-compiled files and logs" - @echo " make check-deps - Verify eask + installed deps" + @echo " make check-deps - Verify eask + loadable Emacs Lisp deps" @echo " make help - Show this help message" @echo "" @echo "Project-root targets (run from project root):" diff --git a/tests/check-deps.el b/tests/check-deps.el new file mode 100644 index 0000000..0cfa99c --- /dev/null +++ b/tests/check-deps.el @@ -0,0 +1,38 @@ +;;; check-deps.el --- Verify test dependencies are loadable -*- lexical-binding: t; -*- + +;; Copyright (C) 2026 Craig Jennings + +;;; Commentary: + +;; Loaded by tests/Makefile's check-deps target after eask has prepared the +;; test environment. Keep dependency discovery inside Emacs so package.el, +;; package-vc, Eask, Nix, and pre-populated load-path setups all work the same +;; way: a dependency is available if Emacs can require it. + +;;; Code: + +(when noninteractive + (package-initialize)) + +(defconst chime-check-deps-required-features + '(dash alert async org-agenda) + "Features required by the Chime test suite.") + +(defun chime-check-deps--missing-features () + "Return required test features that cannot be loaded." + (let (missing) + (dolist (feature chime-check-deps-required-features (nreverse missing)) + (unless (require feature nil t) + (push feature missing))))) + +(let ((missing (chime-check-deps--missing-features))) + (if missing + (progn + (message "Missing Emacs Lisp test dependencies: %s" + (mapconcat #'symbol-name missing ", ")) + (message "Run `make setup' from the project root, or make these features available on load-path.") + (kill-emacs 1)) + (message "Required Emacs Lisp dependencies are loadable: %s" + (mapconcat #'symbol-name chime-check-deps-required-features ", ")))) + +;;; check-deps.el ends here diff --git a/tests/test-testutil-general.el b/tests/test-testutil-general.el new file mode 100644 index 0000000..7b4e445 --- /dev/null +++ b/tests/test-testutil-general.el @@ -0,0 +1,49 @@ +;;; test-testutil-general.el --- Tests for shared test file utilities -*- lexical-binding: t; -*- + +;; Copyright (C) 2026 Craig Jennings + +;;; Commentary: + +;; Tests for the shared filesystem helpers in testutil-general.el. + +;;; Code: + +(require 'test-bootstrap (expand-file-name "test-bootstrap.el")) +(require 'testutil-general (expand-file-name "testutil-general.el")) + +(ert-deftest test-chime-test-default-base-dir-uses-env-override () + "Normal: CHIME_TEST_TMPDIR selects an explicit test root." + (cl-letf (((symbol-function 'getenv) + (lambda (name) + (when (string= name "CHIME_TEST_TMPDIR") + "/tmp/chime-explicit-root")))) + (should (equal "/tmp/chime-explicit-root/" + (chime-test--default-base-dir))))) + +(ert-deftest test-chime-test-default-base-dir-uses-temporary-directory () + "Normal: absent override uses a unique temp-directory path." + (let ((temporary-file-directory "/tmp/chime-parent/")) + (cl-letf (((symbol-function 'getenv) (lambda (_name) nil))) + (should (string-prefix-p + "/tmp/chime-parent/chime-tests-" + (chime-test--default-base-dir)))))) + +(ert-deftest test-chime-test-base-dir-is-not-fixed-home-path () + "Regression: default test root is no longer ~/.temp-chime-tests/." + (skip-unless (not (getenv "CHIME_TEST_TMPDIR"))) + (should-not + (equal (expand-file-name "~/.temp-chime-tests/") + chime-test-base-dir))) + +(ert-deftest test-chime-test-base-dir-create-and-delete-roundtrip () + "Normal: create and delete work for the configured test root." + (unwind-protect + (progn + (should (equal (file-name-as-directory chime-test-base-dir) + (chime-create-test-base-dir))) + (should (file-directory-p chime-test-base-dir))) + (chime-delete-test-base-dir)) + (should-not (file-exists-p chime-test-base-dir))) + +(provide 'test-testutil-general) +;;; test-testutil-general.el ends here diff --git a/tests/testutil-general.el b/tests/testutil-general.el index 556e520..2fffbca 100644 --- a/tests/testutil-general.el +++ b/tests/testutil-general.el @@ -6,22 +6,37 @@ ;; This library provides general helper functions and constants for managing ;; test directories and files across test suites. ;; -;; It establishes a user-local hidden directory as the root for all test assets, -;; provides utilities to create this directory safely, create temporary files -;; and subdirectories within it, and clean up after tests. +;; It establishes a per-process temporary directory as the root for all test +;; assets, provides utilities to create this directory safely, create temporary +;; files and subdirectories within it, and clean up after tests. ;; ;; This library should be required by test suites to ensure consistent, ;; reliable, and isolated file-system resources. ;; ;;; Code: +(defun chime-test--default-base-dir () + "Return the default base directory for CHIME test files. +When CHIME_TEST_TMPDIR is set and non-empty, use it as an explicit +override. Otherwise use a unique process-local directory under +`temporary-file-directory' so test runs do not write into the user's home +or collide with other runs." + (let ((override (getenv "CHIME_TEST_TMPDIR"))) + (file-name-as-directory + (expand-file-name + (if (and override (> (length override) 0)) + override + (make-temp-name + (expand-file-name + (format "chime-tests-%s-" (emacs-pid)) + temporary-file-directory))))))) + (defconst chime-test-base-dir - (expand-file-name "~/.temp-chime-tests/") + (chime-test--default-base-dir) "Base directory for all CHIME test files and directories. -All test file-system artifacts should be created under this hidden -directory in the user's home. This avoids relying on ephemeral system -directories like /tmp and reduces flaky test failures caused by external -cleanup.") +All test file-system artifacts should be created under this directory. +Set CHIME_TEST_TMPDIR to force a specific root; otherwise a unique +process-local directory under `temporary-file-directory' is used.") (defun chime-create-test-base-dir () "Create the test base directory `chime-test-base-dir' if it does not exist. |
