aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/test-gloss-core--alphabetical-insert.el42
-rw-r--r--tests/test-gloss-core--corrupt-file-preserves-cache.el32
-rw-r--r--tests/test-gloss-core--find-buffer-position.el49
-rw-r--r--tests/test-gloss-core--first-call-creates-file.el56
-rw-r--r--tests/test-gloss-core--invalidate-on-mtime.el49
-rw-r--r--tests/test-gloss-core--list.el36
-rw-r--r--tests/test-gloss-core--lookup.el73
-rw-r--r--tests/test-gloss-core--save.el82
-rw-r--r--tests/testutil-gloss.el62
9 files changed, 481 insertions, 0 deletions
diff --git a/tests/test-gloss-core--alphabetical-insert.el b/tests/test-gloss-core--alphabetical-insert.el
new file mode 100644
index 0000000..69c6c95
--- /dev/null
+++ b/tests/test-gloss-core--alphabetical-insert.el
@@ -0,0 +1,42 @@
+;;; test-gloss-core--alphabetical-insert.el --- Tests for alphabetical insert -*- lexical-binding: t -*-
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;;; Commentary:
+;; Tests that `gloss-core-save' inserts new entries at the correct
+;; alphabetical position (case-insensitive ordering).
+
+;;; Code:
+
+(require 'ert)
+(require 'gloss-core)
+(require 'testutil-gloss)
+
+(ert-deftest test-gloss-core-alphabetical-insert-correct-position ()
+ "Normal: terms saved out of order land in alphabetical order."
+ (gloss-test--with-temp-glossary "#+TITLE: Glossary\n"
+ (gloss-core-save "Charlie" "Third." 'manual)
+ (gloss-core-save "Alpha" "First." 'manual)
+ (gloss-core-save "Bravo" "Second." 'manual)
+ (should (equal (gloss-core-list) '("Alpha" "Bravo" "Charlie")))))
+
+(ert-deftest test-gloss-core-alphabetical-insert-case-insensitive-ordering ()
+ "Boundary: ordering uses a case-insensitive compare."
+ (gloss-test--with-temp-glossary "#+TITLE: Glossary\n"
+ (gloss-core-save "banana" "Fruit." 'manual)
+ (gloss-core-save "Apple" "Also fruit." 'manual)
+ (should (equal (gloss-core-list) '("Apple" "banana")))))
+
+(ert-deftest test-gloss-core-alphabetical-insert-on-disk-matches-list ()
+ "Boundary: the on-disk file order matches the list order."
+ (gloss-test--with-temp-glossary "#+TITLE: Glossary\n"
+ (gloss-core-save "zebra" "Last." 'manual)
+ (gloss-core-save "alpha" "First." 'manual)
+ (let ((file-content (with-temp-buffer
+ (insert-file-contents gloss-file)
+ (buffer-string))))
+ (should (< (string-match "^\\* alpha" file-content)
+ (string-match "^\\* zebra" file-content))))))
+
+(provide 'test-gloss-core--alphabetical-insert)
+;;; test-gloss-core--alphabetical-insert.el ends here
diff --git a/tests/test-gloss-core--corrupt-file-preserves-cache.el b/tests/test-gloss-core--corrupt-file-preserves-cache.el
new file mode 100644
index 0000000..6cc6cfc
--- /dev/null
+++ b/tests/test-gloss-core--corrupt-file-preserves-cache.el
@@ -0,0 +1,32 @@
+;;; test-gloss-core--corrupt-file-preserves-cache.el --- Tests for corrupt-file resilience -*- lexical-binding: t -*-
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;;; Commentary:
+;; Tests that a parser failure during cache reload does not destroy the
+;; existing cache; previously-cached lookups still succeed and the user
+;; sees an informative message.
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'ert)
+(require 'gloss-core)
+(require 'testutil-gloss)
+
+(ert-deftest test-gloss-core-corrupt-file-preserves-existing-cache ()
+ "Error: parser failure during reload preserves the existing cache."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ ;; Prime cache.
+ (should (gloss-core-lookup "anaphora"))
+ ;; Force mtime change to trigger a reload attempt.
+ (set-file-times gloss-file (time-add (current-time) 5))
+ ;; Mock the parse helper to fail.
+ (cl-letf (((symbol-function 'gloss-core--parse-file-into-cache)
+ (lambda () (error "simulated parse failure"))))
+ (let ((inhibit-message t))
+ ;; Lookup must not propagate the error; cached entry remains findable.
+ (should (gloss-core-lookup "anaphora"))))))
+
+(provide 'test-gloss-core--corrupt-file-preserves-cache)
+;;; test-gloss-core--corrupt-file-preserves-cache.el ends here
diff --git a/tests/test-gloss-core--find-buffer-position.el b/tests/test-gloss-core--find-buffer-position.el
new file mode 100644
index 0000000..1e63309
--- /dev/null
+++ b/tests/test-gloss-core--find-buffer-position.el
@@ -0,0 +1,49 @@
+;;; test-gloss-core--find-buffer-position.el --- Tests for gloss-core-find-buffer-position -*- lexical-binding: t -*-
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;;; Commentary:
+;; Tests for `gloss-core-find-buffer-position' covering N/B/E cases.
+
+;;; Code:
+
+(require 'ert)
+(require 'gloss-core)
+(require 'testutil-gloss)
+
+(ert-deftest test-gloss-core-find-buffer-position-existing-term-returns-marker ()
+ "Normal: returns a marker pointing at the term's heading."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (let ((marker (gloss-core-find-buffer-position "anaphora")))
+ (should (markerp marker))
+ (with-current-buffer (marker-buffer marker)
+ (goto-char marker)
+ (should (looking-at-p "^\\* anaphora"))))))
+
+(ert-deftest test-gloss-core-find-buffer-position-second-term-returns-marker ()
+ "Normal: marker for second term points at its heading, not the first."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (let ((marker (gloss-core-find-buffer-position "SBIR")))
+ (should (markerp marker))
+ (with-current-buffer (marker-buffer marker)
+ (goto-char marker)
+ (should (looking-at-p "^\\* SBIR"))))))
+
+(ert-deftest test-gloss-core-find-buffer-position-missing-term-returns-nil ()
+ "Boundary: returns nil for a term not in the glossary."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (should-not (gloss-core-find-buffer-position "nonexistent"))))
+
+(ert-deftest test-gloss-core-find-buffer-position-missing-file-returns-nil ()
+ "Error: returns nil when the file does not exist."
+ (let ((gloss-file (concat temporary-file-directory "gloss-pos-nonexistent-"
+ (number-to-string (random 100000)) ".org")))
+ (unwind-protect
+ (progn
+ (gloss-core--cache-reset)
+ (should-not (gloss-core-find-buffer-position "any")))
+ (gloss-core--cache-reset)
+ (when (file-exists-p gloss-file) (delete-file gloss-file)))))
+
+(provide 'test-gloss-core--find-buffer-position)
+;;; test-gloss-core--find-buffer-position.el ends here
diff --git a/tests/test-gloss-core--first-call-creates-file.el b/tests/test-gloss-core--first-call-creates-file.el
new file mode 100644
index 0000000..e168435
--- /dev/null
+++ b/tests/test-gloss-core--first-call-creates-file.el
@@ -0,0 +1,56 @@
+;;; test-gloss-core--first-call-creates-file.el --- Tests for first-save creates file -*- lexical-binding: t -*-
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;;; Commentary:
+;; Tests that `gloss-core-save' creates `gloss-file' (and any missing
+;; parent directory) on first call.
+
+;;; Code:
+
+(require 'ert)
+(require 'gloss-core)
+(require 'testutil-gloss)
+
+(ert-deftest test-gloss-core-save-creates-file-when-missing ()
+ "Normal: first save creates the file with a TITLE header."
+ (let ((gloss-file (concat temporary-file-directory "gloss-create-"
+ (number-to-string (random 100000)) ".org")))
+ (unwind-protect
+ (progn
+ (gloss-core--cache-reset)
+ (should-not (file-exists-p gloss-file))
+ (gloss-core-save "anaphora" "Reference earlier." 'manual)
+ (should (file-exists-p gloss-file))
+ (let ((content (with-temp-buffer
+ (insert-file-contents gloss-file)
+ (buffer-string))))
+ (should (string-match-p "#\\+TITLE:" content))
+ (should (string-match-p "^\\* anaphora" content))))
+ (gloss-core--cache-reset)
+ (when-let ((buf (find-buffer-visiting gloss-file)))
+ (with-current-buffer buf (set-buffer-modified-p nil))
+ (kill-buffer buf))
+ (when (file-exists-p gloss-file) (delete-file gloss-file)))))
+
+(ert-deftest test-gloss-core-save-creates-parent-directory ()
+ "Boundary: first save creates missing parent directory."
+ (let* ((parent (concat temporary-file-directory "gloss-newdir-"
+ (number-to-string (random 100000))))
+ (gloss-file (concat parent "/gloss.org")))
+ (unwind-protect
+ (progn
+ (gloss-core--cache-reset)
+ (should-not (file-exists-p parent))
+ (gloss-core-save "anaphora" "Reference earlier." 'manual)
+ (should (file-exists-p parent))
+ (should (file-exists-p gloss-file)))
+ (gloss-core--cache-reset)
+ (when-let ((buf (find-buffer-visiting gloss-file)))
+ (with-current-buffer buf (set-buffer-modified-p nil))
+ (kill-buffer buf))
+ (when (file-exists-p gloss-file) (delete-file gloss-file))
+ (when (file-directory-p parent) (delete-directory parent)))))
+
+(provide 'test-gloss-core--first-call-creates-file)
+;;; test-gloss-core--first-call-creates-file.el ends here
diff --git a/tests/test-gloss-core--invalidate-on-mtime.el b/tests/test-gloss-core--invalidate-on-mtime.el
new file mode 100644
index 0000000..69500dd
--- /dev/null
+++ b/tests/test-gloss-core--invalidate-on-mtime.el
@@ -0,0 +1,49 @@
+;;; test-gloss-core--invalidate-on-mtime.el --- Tests for cache mtime invalidation -*- lexical-binding: t -*-
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;;; Commentary:
+;; Tests that the cache reloads when `gloss-file' is edited out-of-band.
+
+;;; Code:
+
+(require 'ert)
+(require 'gloss-core)
+(require 'testutil-gloss)
+
+(ert-deftest test-gloss-core-invalidate-on-mtime-new-content-detected ()
+ "Normal: out-of-band edit + later lookup sees the new content."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ ;; Prime cache.
+ (should (gloss-core-lookup "anaphora"))
+ ;; Append a new entry directly to disk.
+ (with-temp-buffer
+ (insert-file-contents gloss-file)
+ (goto-char (point-max))
+ (insert "\n* hapax\n:PROPERTIES:\n:SOURCE: manual\n:ADDED: 2026-04-28\n:END:\nA word used only once in a corpus.\n")
+ (write-region (point-min) (point-max) gloss-file))
+ ;; Force mtime change to be visible (1-second granularity on some FSes).
+ (set-file-times gloss-file (time-add (current-time) 5))
+ ;; Lookup detects the mtime change and reloads.
+ (let ((entry (gloss-core-lookup "hapax")))
+ (should entry)
+ (should (string-match-p "used only once" (plist-get entry :body))))))
+
+(ert-deftest test-gloss-core-invalidate-on-mtime-unchanged-uses-cache ()
+ "Boundary: unchanged mtime — two consecutive lookups both succeed."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (should (gloss-core-lookup "anaphora"))
+ (should (gloss-core-lookup "anaphora"))))
+
+(ert-deftest test-gloss-core-invalidate-on-mtime-deleted-file-clears-cache ()
+ "Error: file deleted out-of-band — subsequent lookup returns nil."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (should (gloss-core-lookup "anaphora"))
+ (when-let ((buf (find-buffer-visiting gloss-file)))
+ (with-current-buffer buf (set-buffer-modified-p nil))
+ (kill-buffer buf))
+ (delete-file gloss-file)
+ (should-not (gloss-core-lookup "anaphora"))))
+
+(provide 'test-gloss-core--invalidate-on-mtime)
+;;; test-gloss-core--invalidate-on-mtime.el ends here
diff --git a/tests/test-gloss-core--list.el b/tests/test-gloss-core--list.el
new file mode 100644
index 0000000..22c988e
--- /dev/null
+++ b/tests/test-gloss-core--list.el
@@ -0,0 +1,36 @@
+;;; test-gloss-core--list.el --- Tests for gloss-core-list -*- lexical-binding: t -*-
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;;; Commentary:
+;; Tests for `gloss-core-list' covering Normal/Boundary/Error cases.
+
+;;; Code:
+
+(require 'ert)
+(require 'gloss-core)
+(require 'testutil-gloss)
+
+(ert-deftest test-gloss-core-list-returns-all-terms-alphabetically ()
+ "Normal: list returns all terms in case-insensitive alphabetical order."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (should (equal (gloss-core-list) '("anaphora" "SBIR")))))
+
+(ert-deftest test-gloss-core-list-empty-glossary-returns-nil ()
+ "Boundary: list against an empty file returns nil."
+ (gloss-test--with-temp-glossary "#+TITLE: Glossary\n"
+ (should-not (gloss-core-list))))
+
+(ert-deftest test-gloss-core-list-missing-file-returns-nil ()
+ "Error: list before any save returns nil (file does not exist)."
+ (let ((gloss-file (concat temporary-file-directory "gloss-list-nonexistent-"
+ (number-to-string (random 100000)) ".org")))
+ (unwind-protect
+ (progn
+ (gloss-core--cache-reset)
+ (should-not (gloss-core-list)))
+ (gloss-core--cache-reset)
+ (when (file-exists-p gloss-file) (delete-file gloss-file)))))
+
+(provide 'test-gloss-core--list)
+;;; test-gloss-core--list.el ends here
diff --git a/tests/test-gloss-core--lookup.el b/tests/test-gloss-core--lookup.el
new file mode 100644
index 0000000..a1986a7
--- /dev/null
+++ b/tests/test-gloss-core--lookup.el
@@ -0,0 +1,73 @@
+;;; test-gloss-core--lookup.el --- Tests for gloss-core-lookup -*- lexical-binding: t -*-
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;;; Commentary:
+;; Tests for `gloss-core-lookup' covering Normal/Boundary/Error cases.
+
+;;; Code:
+
+(require 'ert)
+(require 'gloss-core)
+(require 'testutil-gloss)
+
+(ert-deftest test-gloss-core-lookup-existing-term-returns-entry ()
+ "Normal: lookup of saved term returns entry plist with all fields."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (let ((entry (gloss-core-lookup "anaphora")))
+ (should entry)
+ (should (equal (plist-get entry :term) "anaphora"))
+ (should (string-match-p "Reference to something earlier"
+ (plist-get entry :body)))
+ (should (eq (plist-get entry :source) 'wiktionary))
+ (should (equal (plist-get entry :added) "2026-04-28")))))
+
+(ert-deftest test-gloss-core-lookup-includes-marker ()
+ "Normal: lookup result includes a :marker field at the heading."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (let* ((entry (gloss-core-lookup "anaphora"))
+ (marker (plist-get entry :marker)))
+ (should (markerp marker))
+ (with-current-buffer (marker-buffer marker)
+ (goto-char marker)
+ (should (looking-at-p "^\\* anaphora"))))))
+
+(ert-deftest test-gloss-core-lookup-missing-term-returns-nil ()
+ "Normal: lookup of unsaved term returns nil."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (should-not (gloss-core-lookup "nonexistent-term"))))
+
+(ert-deftest test-gloss-core-lookup-empty-string-returns-nil ()
+ "Boundary: lookup of empty string returns nil, not an error."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (should-not (gloss-core-lookup ""))))
+
+(ert-deftest test-gloss-core-lookup-nil-returns-nil ()
+ "Boundary: lookup of nil returns nil, not an error."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (should-not (gloss-core-lookup nil))))
+
+(ert-deftest test-gloss-core-lookup-case-sensitive ()
+ "Boundary: lookup is case-sensitive — \"Anaphora\" misses \"anaphora\"."
+ (gloss-test--with-temp-glossary gloss-test--sample-content
+ (should-not (gloss-core-lookup "Anaphora"))))
+
+(ert-deftest test-gloss-core-lookup-empty-glossary-file-returns-nil ()
+ "Error: lookup against empty file returns nil."
+ (gloss-test--with-temp-glossary "#+TITLE: Glossary\n"
+ (should-not (gloss-core-lookup "anything"))))
+
+(ert-deftest test-gloss-core-lookup-missing-file-returns-nil ()
+ "Error: lookup before any save returns nil (file does not exist)."
+ (let ((gloss-file (concat temporary-file-directory "gloss-nonexistent-"
+ (number-to-string (random 100000)) ".org")))
+ (unwind-protect
+ (progn
+ (gloss-core--cache-reset)
+ (should-not (file-exists-p gloss-file))
+ (should-not (gloss-core-lookup "anything")))
+ (gloss-core--cache-reset)
+ (when (file-exists-p gloss-file) (delete-file gloss-file)))))
+
+(provide 'test-gloss-core--lookup)
+;;; test-gloss-core--lookup.el ends here
diff --git a/tests/test-gloss-core--save.el b/tests/test-gloss-core--save.el
new file mode 100644
index 0000000..c9e77d1
--- /dev/null
+++ b/tests/test-gloss-core--save.el
@@ -0,0 +1,82 @@
+;;; test-gloss-core--save.el --- Tests for gloss-core-save -*- lexical-binding: t -*-
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;;; Commentary:
+;; Tests for `gloss-core-save' covering Normal/Boundary/Error cases.
+
+;;; Code:
+
+(require 'ert)
+(require 'gloss-core)
+(require 'testutil-gloss)
+
+(ert-deftest test-gloss-core-save-new-term-returns-entry-plist ()
+ "Normal: save new term returns the entry plist with all fields."
+ (gloss-test--with-temp-glossary "#+TITLE: Glossary\n"
+ (let ((entry (gloss-core-save "anaphora" "Reference to..." 'wiktionary)))
+ (should entry)
+ (should (equal (plist-get entry :term) "anaphora"))
+ (should (equal (plist-get entry :body) "Reference to..."))
+ (should (eq (plist-get entry :source) 'wiktionary))
+ (should (stringp (plist-get entry :added))))))
+
+(ert-deftest test-gloss-core-save-then-lookup-roundtrip ()
+ "Normal: save then lookup returns the matching entry."
+ (gloss-test--with-temp-glossary "#+TITLE: Glossary\n"
+ (gloss-core-save "anaphora" "Reference to something earlier." 'wiktionary)
+ (let ((entry (gloss-core-lookup "anaphora")))
+ (should entry)
+ (should (equal (plist-get entry :body)
+ "Reference to something earlier.")))))
+
+(ert-deftest test-gloss-core-save-multi-line-body-preserved ()
+ "Boundary: save preserves a multi-line body."
+ (gloss-test--with-temp-glossary "#+TITLE: Glossary\n"
+ (let ((body "First paragraph.\n\nSecond paragraph."))
+ (gloss-core-save "term" body 'manual)
+ (let ((entry (gloss-core-lookup "term")))
+ (should (equal (plist-get entry :body) body))))))
+
+(ert-deftest test-gloss-core-save-empty-body-raises-error ()
+ "Error: empty body raises a user-error."
+ (gloss-test--with-temp-glossary "#+TITLE: Glossary\n"
+ (should-error (gloss-core-save "anaphora" "" 'wiktionary)
+ :type 'user-error)))
+
+(ert-deftest test-gloss-core-save-empty-term-raises-error ()
+ "Error: empty term raises a user-error."
+ (gloss-test--with-temp-glossary "#+TITLE: Glossary\n"
+ (should-error (gloss-core-save "" "Some body" 'wiktionary)
+ :type 'user-error)))
+
+(ert-deftest test-gloss-core-save-collision-replace-overwrites ()
+ "Error: collision with action \\='replace overwrites the body and source."
+ (gloss-test--with-temp-glossary "#+TITLE: Glossary\n"
+ (gloss-core-save "anaphora" "First definition." 'wiktionary)
+ (gloss-core-save "anaphora" "Second definition." 'manual 'replace)
+ (let ((entry (gloss-core-lookup "anaphora")))
+ (should (equal (plist-get entry :body) "Second definition."))
+ (should (eq (plist-get entry :source) 'manual)))))
+
+(ert-deftest test-gloss-core-save-collision-append-joins-bodies ()
+ "Error: collision with action \\='append concatenates the bodies."
+ (gloss-test--with-temp-glossary "#+TITLE: Glossary\n"
+ (gloss-core-save "anaphora" "First definition." 'wiktionary)
+ (gloss-core-save "anaphora" "Second definition." 'manual 'append)
+ (let ((entry (gloss-core-lookup "anaphora")))
+ (should (string-match-p "First definition" (plist-get entry :body)))
+ (should (string-match-p "Second definition" (plist-get entry :body))))))
+
+(ert-deftest test-gloss-core-save-collision-cancel-leaves-original ()
+ "Error: collision with action \\='cancel leaves the original entry."
+ (gloss-test--with-temp-glossary "#+TITLE: Glossary\n"
+ (gloss-core-save "anaphora" "First definition." 'wiktionary)
+ (let ((result (gloss-core-save "anaphora" "Second definition."
+ 'manual 'cancel)))
+ (should-not result))
+ (let ((entry (gloss-core-lookup "anaphora")))
+ (should (equal (plist-get entry :body) "First definition.")))))
+
+(provide 'test-gloss-core--save)
+;;; test-gloss-core--save.el ends here
diff --git a/tests/testutil-gloss.el b/tests/testutil-gloss.el
new file mode 100644
index 0000000..b70b0ea
--- /dev/null
+++ b/tests/testutil-gloss.el
@@ -0,0 +1,62 @@
+;;; testutil-gloss.el --- Shared test fixtures for gloss -*- lexical-binding: t -*-
+
+;; SPDX-License-Identifier: GPL-3.0-or-later
+
+;;; Commentary:
+
+;; Fixtures used across gloss test files. Provides:
+;; - `gloss-test--with-temp-glossary' macro (binds `gloss-file', cleans up).
+;; - `gloss-test--sample-content' two-entry org content.
+;; - `gloss-test--make-temp-glossary-file' for cases that need direct paths.
+
+;;; Code:
+
+(require 'gloss-core)
+
+(defconst gloss-test--sample-content
+ "#+TITLE: Glossary
+#+STARTUP: showall
+
+* anaphora
+:PROPERTIES:
+:SOURCE: wiktionary
+:ADDED: 2026-04-28
+:END:
+Reference to something earlier in the discourse.
+
+* SBIR
+:PROPERTIES:
+:SOURCE: wiktionary
+:ADDED: 2026-04-27
+:END:
+Initialism of Small Business Innovation Research.
+"
+ "Two-entry sample content for tests that need a populated glossary.")
+
+(defun gloss-test--make-temp-glossary-file (&optional initial-content)
+ "Create a temp file with INITIAL-CONTENT (or empty) and return its path.
+The caller is responsible for cleanup."
+ (let ((path (make-temp-file "gloss-test-" nil ".org")))
+ (when initial-content
+ (with-temp-file path (insert initial-content)))
+ path))
+
+(defmacro gloss-test--with-temp-glossary (initial-content &rest body)
+ "Bind `gloss-file' to a fresh temp file containing INITIAL-CONTENT.
+Reset the in-memory cache before BODY and after. Clean up file and any
+visiting buffer."
+ (declare (indent 1) (debug t))
+ `(let ((gloss-file (gloss-test--make-temp-glossary-file ,initial-content)))
+ (unwind-protect
+ (progn
+ (gloss-core--cache-reset)
+ ,@body)
+ (gloss-core--cache-reset)
+ (when-let ((buf (find-buffer-visiting gloss-file)))
+ (with-current-buffer buf (set-buffer-modified-p nil))
+ (kill-buffer buf))
+ (when (file-exists-p gloss-file)
+ (delete-file gloss-file)))))
+
+(provide 'testutil-gloss)
+;;; testutil-gloss.el ends here