This commit is contained in:
Kinneyzhang 2025-12-12 11:17:52 +08:00
parent 3e7d2881a9
commit 4d914ebd29
3 changed files with 683 additions and 829 deletions

View File

@ -1,697 +0,0 @@
;;; tp-ert-tests.el --- ERT tests for tp.el -*- lexical-binding: t -*-
;; Copyright (C) 2024
;;; Commentary:
;; Comprehensive test suite for tp.el using ERT (Emacs Lisp Regression Testing).
;; Run with: emacs --batch -l tp.el -l tp-ert-tests.el -f ert-run-tests-batch-and-exit
;;; Code:
(require 'ert)
(require 'cl-lib)
;; Load tp.el from the same directory
(let ((tp-dir (file-name-directory (or load-file-name buffer-file-name))))
(add-to-list 'load-path tp-dir)
(require 'tp))
;;; ============================================================
;;; Test Utilities
;;; ============================================================
(defmacro tp-test-with-temp-buffer (&rest body)
"Execute BODY in a temporary buffer with tp.el loaded."
(declare (indent 0))
`(with-temp-buffer
(setq tp-layer-alist nil)
(setq tp-layer-groups nil)
,@body))
;;; ============================================================
;;; Basic Text Property Functions Tests
;;; ============================================================
(ert-deftest tp-test-put-and-get ()
"Test tp-put and tp-get basic functionality."
(tp-test-with-temp-buffer
(insert "Hello World")
;; Set a single property
(tp-put 1 6 'face 'bold)
(should (eq (tp-get 1 'face) 'bold))
(should (eq (tp-get 3 'face) 'bold))
(should (null (tp-get 7 'face)))
;; Set multiple properties
(tp-put 7 12 'face 'italic 'help-echo "test")
(should (eq (tp-get 7 'face) 'italic))
(should (equal (tp-get 7 'help-echo) "test"))))
(ert-deftest tp-test-put-with-list ()
"Test tp-put accepts properties as a list."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-put 1 6 '(face bold help-echo "greeting"))
(should (eq (tp-get 1 'face) 'bold))
(should (equal (tp-get 1 'help-echo) "greeting"))))
(ert-deftest tp-test-put-returns-region ()
"Test tp-put returns the modified region."
(tp-test-with-temp-buffer
(insert "Hello")
(let ((result (tp-put 1 6 'face 'bold)))
(should (equal result '(1 . 6))))))
(ert-deftest tp-test-remove ()
"Test tp-remove removes a specific property."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-put 1 6 'face 'bold 'help-echo "test")
(should (eq (tp-get 1 'face) 'bold))
(tp-remove 1 6 'face)
(should (null (tp-get 1 'face)))
(should (equal (tp-get 1 'help-echo) "test"))))
(ert-deftest tp-test-remove-list ()
"Test tp-remove-list removes multiple properties."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-put 1 6 'face 'bold 'help-echo "test" 'mouse-face 'highlight)
(tp-remove-list 1 6 '(face help-echo))
(should (null (tp-get 1 'face)))
(should (null (tp-get 1 'help-echo)))
(should (eq (tp-get 1 'mouse-face) 'highlight))))
(ert-deftest tp-test-clear ()
"Test tp-clear removes all properties."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 6 'face 'bold)
(tp-put 7 12 'face 'italic)
(tp-clear 1 12)
(should (null (tp-get 1 'face)))
(should (null (tp-get 7 'face)))))
(ert-deftest tp-test-clear-defaults-to-buffer ()
"Test tp-clear defaults to entire buffer."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 12 'face 'bold)
(tp-clear)
(should (null (tp-get 1 'face)))
(should (null (tp-get 7 'face)))))
(ert-deftest tp-test-at ()
"Test tp-at returns all properties at point."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-put 1 6 'face 'bold 'help-echo "test")
(let ((props (tp-at 1)))
(should (eq (plist-get props 'face) 'bold))
(should (equal (plist-get props 'help-echo) "test")))))
(ert-deftest tp-test-at-defaults-to-point ()
"Test tp-at defaults to current point."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-put 1 6 'face 'bold)
(goto-char 3)
(should (eq (plist-get (tp-at) 'face) 'bold))))
(ert-deftest tp-test-plist ()
"Test tp-plist merges properties from region."
(tp-test-with-temp-buffer
(insert "Hello World")
;; Put both properties on the same overlapping region for proper merging
(tp-put 1 12 'face 'bold)
(tp-put 1 12 'help-echo "test")
(let ((props (tp-plist 1 12)))
(should (eq (plist-get props 'face) 'bold))
(should (equal (plist-get props 'help-echo) "test")))))
;;; ============================================================
;;; Text Property Interval Tests
;;; ============================================================
(ert-deftest tp-test-empty-p ()
"Test tp-empty-p detects empty properties."
(should (tp-empty-p "plain string"))
(should-not (tp-empty-p (propertize "styled" 'face 'bold))))
(ert-deftest tp-test-intervals ()
"Test tp-intervals returns property intervals."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 6 'face 'bold)
(tp-put 7 12 'face 'italic)
(let ((intervals (tp-intervals 1 12)))
(should (>= (length intervals) 2)))))
;;; ============================================================
;;; Layer Definition Tests
;;; ============================================================
(ert-deftest tp-test-layer-define ()
"Test tp-layer-define creates a layer."
(tp-test-with-temp-buffer
(tp-layer-define test-layer '(face bold help-echo "test"))
(should (assoc 'test-layer tp-layer-alist))
(should (equal (cdr (assoc 'test-layer tp-layer-alist))
'(face bold help-echo "test")))))
(ert-deftest tp-test-layer-define-updates-existing ()
"Test tp-layer-define updates existing layer."
(tp-test-with-temp-buffer
(tp-layer-define test-layer '(face bold))
(tp-layer-define test-layer '(face italic))
(should (equal (cdr (assoc 'test-layer tp-layer-alist))
'(face italic)))))
(ert-deftest tp-test-layer-props ()
"Test tp-layer-props returns properties with tp-name."
(tp-test-with-temp-buffer
(tp-layer-define my-layer '(face bold))
(let ((props (tp-layer-props 'my-layer)))
(should (eq (plist-get props 'face) 'bold))
(should (eq (plist-get props 'tp-name) 'my-layer)))))
(ert-deftest tp-test-layer-props-returns-nil-for-undefined ()
"Test tp-layer-props returns nil for undefined layer."
(tp-test-with-temp-buffer
(should (null (tp-layer-props 'undefined-layer)))))
(ert-deftest tp-test-layer-undefine ()
"Test tp-layer-undefine removes layer definition."
(tp-test-with-temp-buffer
(tp-layer-define test-layer '(face bold))
(should (assoc 'test-layer tp-layer-alist))
(tp-layer-undefine 'test-layer)
(should-not (assoc 'test-layer tp-layer-alist))))
;;; ============================================================
;;; Layer Group Tests
;;; ============================================================
(ert-deftest tp-test-group-define ()
"Test tp-group-define creates a layer group."
(tp-test-with-temp-buffer
(tp-group-define my-group
layer1 '(face bold)
layer2 '(face italic)
layer3 '(face underline))
(should (assoc 'my-group tp-layer-groups))
(should (assoc 'layer1 tp-layer-alist))
(should (assoc 'layer2 tp-layer-alist))
(should (assoc 'layer3 tp-layer-alist))
;; Check all layers are present in the group
(let ((layers (cdr (assoc 'my-group tp-layer-groups))))
(should (= (length layers) 3))
(should (memq 'layer1 layers))
(should (memq 'layer2 layers))
(should (memq 'layer3 layers)))))
(ert-deftest tp-test-group-props ()
"Test tp-group-props returns all layer properties."
(tp-test-with-temp-buffer
(tp-group-define my-group
layer1 '(face bold)
layer2 '(face italic))
(let ((props-list (tp-group-props 'my-group)))
(should (= (length props-list) 2))
;; Check that both layers are present (order may vary)
(let ((faces (mapcar (lambda (p) (plist-get p 'face)) props-list)))
(should (or (memq 'bold faces) (memq 'italic faces)))))))
(ert-deftest tp-test-group-undefine ()
"Test tp-group-undefine removes group definition."
(tp-test-with-temp-buffer
(tp-group-define my-group
layer1 '(face bold))
(should (assoc 'my-group tp-layer-groups))
(tp-group-undefine 'my-group)
(should-not (assoc 'my-group tp-layer-groups))))
(ert-deftest tp-test-layer-reset ()
"Test tp-layer-reset clears all definitions."
(tp-test-with-temp-buffer
(tp-layer-define layer1 '(face bold))
(tp-group-define group1 layer2 '(face italic))
(should tp-layer-alist)
(should tp-layer-groups)
(tp-layer-reset)
(should-not tp-layer-alist)
(should-not tp-layer-groups)))
;;; ============================================================
;;; Layer Stack Operations Tests
;;; ============================================================
(ert-deftest tp-test-layer-push ()
"Test tp-layer-push adds layer to stack."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-push 1 6 'layer1)
(should (eq (tp-get 1 'face) 'bold))
(should (eq (tp-get 1 'tp-name) 'layer1))))
(ert-deftest tp-test-layer-push-multiple ()
"Test pushing multiple layers."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
;; layer2 should be on top (visible)
(should (eq (tp-get 1 'face) 'italic))
(should (eq (tp-get 1 'tp-name) 'layer2))
;; layer1 should be in the stack below
(should (tp-get 1 'tp-layers))))
(ert-deftest tp-test-layer-push-error-on-duplicate ()
"Test tp-layer-push errors on duplicate layer."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-push 1 6 'layer1)
(should-error (tp-layer-push 1 6 'layer1))))
(ert-deftest tp-test-layer-delete ()
"Test tp-layer-delete removes layer from stack."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
;; Delete top layer
(tp-layer-delete 1 6 'layer2)
;; layer1 should now be visible
(should (eq (tp-get 1 'face) 'bold))
(should (eq (tp-get 1 'tp-name) 'layer1))))
(ert-deftest tp-test-layer-delete-from-middle ()
"Test deleting layer from middle of stack."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-define layer3 '(face underline))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
(tp-layer-push 1 6 'layer3)
;; Delete middle layer
(tp-layer-delete 1 6 'layer2)
;; Top layer should still be visible
(should (eq (tp-get 1 'tp-name) 'layer3))
;; layer2 should not exist anymore
(should-not (tp-layer-exists-p 1 6 'layer2))))
(ert-deftest tp-test-layer-rotate ()
"Test tp-layer-rotate cycles layers."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-define layer3 '(face underline))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
(tp-layer-push 1 6 'layer3)
;; layer3 is on top
(should (eq (tp-layer-top 1 6) 'layer3))
;; Rotate once - layer2 should be on top
(tp-layer-rotate 1 6)
(should (eq (tp-layer-top 1 6) 'layer2))
;; Rotate again - layer1 should be on top
(tp-layer-rotate 1 6)
(should (eq (tp-layer-top 1 6) 'layer1))
;; Rotate again - layer3 should be on top (cycled back)
(tp-layer-rotate 1 6)
(should (eq (tp-layer-top 1 6) 'layer3))))
(ert-deftest tp-test-layer-pin ()
"Test tp-layer-pin brings layer to top."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-define layer3 '(face underline))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
(tp-layer-push 1 6 'layer3)
;; Pin layer1 to top
(tp-layer-pin 1 6 'layer1)
(should (eq (tp-layer-top 1 6) 'layer1))))
(ert-deftest tp-test-layer-pin-error-on-nonexistent ()
"Test tp-layer-pin errors on nonexistent layer."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-push 1 6 'layer1)
(should-error (tp-layer-pin 1 6 'nonexistent))))
(ert-deftest tp-test-layer-hide ()
"Test tp-layer-hide moves layer to bottom."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
;; layer2 is on top
(should (eq (tp-layer-top 1 6) 'layer2))
;; Hide layer2
(tp-layer-hide 1 6 'layer2)
;; layer1 should now be on top
(should (eq (tp-layer-top 1 6) 'layer1))))
(ert-deftest tp-test-layer-show ()
"Test tp-layer-show brings layer to top."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
;; Hide layer2
(tp-layer-hide 1 6 'layer2)
(should (eq (tp-layer-top 1 6) 'layer1))
;; Show layer2 again
(tp-layer-show 1 6 'layer2)
(should (eq (tp-layer-top 1 6) 'layer2))))
;;; ============================================================
;;; Layer Query Tests
;;; ============================================================
(ert-deftest tp-test-layer-list ()
"Test tp-layer-list returns all layer names."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-define layer3 '(face underline))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
(tp-layer-push 1 6 'layer3)
(let ((layers (tp-layer-list 1 6)))
(should (= (length layers) 3))
(should (memq 'layer1 layers))
(should (memq 'layer2 layers))
(should (memq 'layer3 layers)))))
(ert-deftest tp-test-layer-count ()
"Test tp-layer-count returns correct count."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-push 1 6 'layer1)
(should (= (tp-layer-count 1 6) 1))
(tp-layer-push 1 6 'layer2)
(should (= (tp-layer-count 1 6) 2))))
(ert-deftest tp-test-layer-exists-p ()
"Test tp-layer-exists-p correctly detects layers."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-push 1 6 'layer1)
(should (tp-layer-exists-p 1 6 'layer1))
(should-not (tp-layer-exists-p 1 6 'layer2))))
(ert-deftest tp-test-layer-top ()
"Test tp-layer-top returns top layer name."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-push 1 6 'layer1)
(should (eq (tp-layer-top 1 6) 'layer1))
(tp-layer-push 1 6 'layer2)
(should (eq (tp-layer-top 1 6) 'layer2))))
;;; ============================================================
;;; Propertize String Tests
;;; ============================================================
(ert-deftest tp-test-propertize ()
"Test tp-propertize adds properties to string."
(let ((str (tp-propertize "Hello" 'face 'bold)))
(should (eq (get-text-property 0 'face str) 'bold))))
(ert-deftest tp-test-propertize-with-list ()
"Test tp-propertize accepts properties as list."
(let ((str (tp-propertize "Hello" '(face bold help-echo "test"))))
(should (eq (get-text-property 0 'face str) 'bold))
(should (equal (get-text-property 0 'help-echo str) "test"))))
(ert-deftest tp-test-layer-propertize ()
"Test tp-layer-propertize applies layer to string."
(tp-test-with-temp-buffer
(tp-layer-define my-layer '(face bold help-echo "greeting"))
(let ((str (tp-layer-propertize "Hello" 'my-layer)))
(should (eq (get-text-property 0 'face str) 'bold))
(should (equal (get-text-property 0 'help-echo str) "greeting")))))
(ert-deftest tp-test-layer-propertize-error-on-undefined ()
"Test tp-layer-propertize errors on undefined layer."
(tp-test-with-temp-buffer
(should-error (tp-layer-propertize "Hello" 'undefined-layer))))
(ert-deftest tp-test-group-propertize ()
"Test tp-group-propertize applies group to string."
(tp-test-with-temp-buffer
(tp-group-define my-group
layer1 '(face bold)
layer2 '(help-echo "test"))
(let ((str (tp-group-propertize "Hello" 'my-group)))
(should (stringp str))
(should (= (length str) 5)))))
(ert-deftest tp-test-group-propertize-error-on-undefined ()
"Test tp-group-propertize errors on undefined group."
(tp-test-with-temp-buffer
(should-error (tp-group-propertize "Hello" 'undefined-group))))
;;; ============================================================
;;; Match and Regexp Tests
;;; ============================================================
(ert-deftest tp-test-match ()
"Test tp-match sets properties on string matches."
(tp-test-with-temp-buffer
(insert "Hello World Hello")
(let ((regions (tp-match "Hello" 'face 'bold)))
(should (= (length regions) 2))
(should (eq (tp-get 1 'face) 'bold))
(should (eq (tp-get 13 'face) 'bold)))))
(ert-deftest tp-test-match-returns-regions ()
"Test tp-match returns correct region pairs."
(tp-test-with-temp-buffer
(insert "Hello World Hello")
(let ((regions (tp-match "Hello")))
(should (= (length regions) 2))
(should (equal (car regions) '(1 . 6)))
(should (equal (cadr regions) '(13 . 18))))))
(ert-deftest tp-test-regexp ()
"Test tp-regexp sets properties on regexp matches."
(tp-test-with-temp-buffer
(insert "abc 123 def 456")
(let ((regions (tp-regexp "[0-9]+" 'face 'bold)))
(should (= (length regions) 2))
(should (eq (tp-get 5 'face) 'bold))
(should (eq (tp-get 13 'face) 'bold)))))
(ert-deftest tp-test-regexp-returns-regions ()
"Test tp-regexp returns correct region pairs."
(tp-test-with-temp-buffer
(insert "abc 123 def 456")
(let ((regions (tp-regexp "[0-9]+")))
(should (= (length regions) 2)))))
;;; ============================================================
;;; Search and Navigation Tests
;;; ============================================================
(ert-deftest tp-test-forward ()
"Test tp-forward finds next property."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 7 12 'face 'bold)
(goto-char 1)
;; text-property-search-forward may not exist in all Emacs versions
(skip-unless (fboundp 'text-property-search-forward))
(let ((match (tp-forward 'face)))
(should match)
(should (= (prop-match-beginning match) 7)))))
(ert-deftest tp-test-backward ()
"Test tp-backward finds previous property."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 6 'face 'bold)
(goto-char 12)
;; text-property-search-backward may not exist in all Emacs versions
;; Skip test if function is not available
(skip-unless (fboundp 'text-property-search-backward))
(let ((match (tp-backward 'face)))
(should match)
(should (= (prop-match-beginning match) 1)))))
(ert-deftest tp-test-next ()
"Test tp-next returns next position with property."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 7 12 'face 'bold)
(let ((pos (tp-next 1 'face)))
(should (= pos 7)))))
(ert-deftest tp-test-prev ()
"Test tp-prev returns previous position with property."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 6 'face 'bold)
(let ((pos (tp-prev 12 'face)))
(should (= pos 1)))))
(ert-deftest tp-test-goto-next ()
"Test tp-goto-next moves point."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 7 12 'face 'bold)
(goto-char 1)
(tp-goto-next 'face)
(should (= (point) 7))))
(ert-deftest tp-test-goto-prev ()
"Test tp-goto-prev moves point."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 6 'face 'bold)
(goto-char 12)
(tp-goto-prev 'face)
(should (= (point) 1))))
;;; ============================================================
;;; Utility Function Tests
;;; ============================================================
(ert-deftest tp-test-in ()
"Test tp-in finds regions with property."
(tp-test-with-temp-buffer
(insert "Hello World Test")
(tp-put 1 6 'my-prop 'value1)
(tp-put 7 12 'my-prop 'value2)
(let ((regions (tp-in 'my-prop)))
(should (= (length regions) 2)))))
(ert-deftest tp-test-in-with-value ()
"Test tp-in filters by value."
(tp-test-with-temp-buffer
(insert "Hello World Test")
(tp-put 1 6 'my-prop 'value1)
(tp-put 7 12 'my-prop 'value2)
(let ((regions (tp-in 'my-prop 'value1)))
(should (= (length regions) 1))
(should (equal (car (car regions)) 1)))))
(ert-deftest tp-test-all ()
"Test tp-all returns all regions with properties."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 6 'face 'bold)
(tp-put 7 12 'face 'italic)
(let ((regions (tp-all)))
(should (>= (length regions) 2)))))
(ert-deftest tp-test-regions-map ()
"Test tp-regions-map applies function to regions."
(tp-test-with-temp-buffer
(insert "Hello World Hello")
(tp-put 1 6 'marker t)
(tp-put 13 18 'marker t)
(let ((result nil))
(tp-regions-map
(lambda (start end idx)
(push (list start end idx) result))
'marker)
(should (= (length result) 2)))))
(ert-deftest tp-test-strings-map ()
"Test tp-strings-map applies function to strings."
(tp-test-with-temp-buffer
(insert "Hello World Hello")
(tp-put 1 6 'marker t)
(tp-put 13 18 'marker t)
(let ((result nil))
(tp-strings-map
(lambda (str idx)
(push str result))
'marker)
(should (= (length result) 2))
(should (member "Hello" result)))))
;;; ============================================================
;;; Alias Tests
;;; ============================================================
(ert-deftest tp-test-aliases-exist ()
"Test that all aliases are properly defined."
(should (fboundp 'tp-set))
(should (fboundp 'tp-layer-properties))
(should (fboundp 'tp-layer-group-define))
(should (fboundp 'tp-layer-group-properties))
(should (fboundp 'tp-layer-group-propertize))
(should (fboundp 'tp-layer-group-undefine)))
(ert-deftest tp-test-aliases-work ()
"Test that aliases work correctly."
(tp-test-with-temp-buffer
;; Test tp-set alias
(insert "Hello")
(tp-set 1 6 'face 'bold)
(should (eq (tp-get 1 'face) 'bold))))
;;; ============================================================
;;; Edge Case Tests
;;; ============================================================
(ert-deftest tp-test-empty-region ()
"Test operations on empty buffer."
(tp-test-with-temp-buffer
(should (null (tp-at 1)))
(should (null (tp-all)))))
(ert-deftest tp-test-overlapping-regions ()
"Test overlapping property regions."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 8 'prop1 'val1)
(tp-put 5 12 'prop2 'val2)
(should (eq (tp-get 1 'prop1) 'val1))
(should (null (tp-get 1 'prop2)))
(should (eq (tp-get 6 'prop1) 'val1))
(should (eq (tp-get 6 'prop2) 'val2))
(should (null (tp-get 10 'prop1)))
(should (eq (tp-get 10 'prop2) 'val2))))
(ert-deftest tp-test-single-char-region ()
"Test operations on single character."
(tp-test-with-temp-buffer
(insert "H")
(tp-put 1 2 'face 'bold)
(should (eq (tp-get 1 'face) 'bold))))
(ert-deftest tp-test-layer-on-string ()
"Test layer operations on string object."
(let ((str (copy-sequence "Hello")))
(set-text-properties 0 5 nil str)
(should (tp-empty-p str))))
(provide 'tp-ert-tests)
;;; tp-ert-tests.el ends here

View File

@ -1,59 +0,0 @@
(defvar pop-buffer-insert-buffer
"*pop-buffer-insert*")
(defvar pop-buffer-win-conf nil)
(defun my-pop-to-buffer (buffer-or-name &optional action norecord)
(declare (indent defun))
(let ((buffer (pop-to-buffer buffer-or-name action norecord)))
(with-current-buffer buffer
(local-set-key "q" 'pop-buffer-quit))
buffer))
(defun remove-from-list (list-var element)
(set list-var (delete element (symbol-value list-var))))
(defun pop-buffer-quit ()
(interactive)
(let ((win-conf pop-buffer-win-conf))
(local-unset-key "q")
(setq-local pop-buffer-win-conf nil)
(remove-from-list 'margin-work-modes 'quick-buffer-mode)
(set-window-configuration win-conf)))
(defmacro with-pop-buffer (buffer-or-name height &rest body)
(declare (indent defun))
`(let* ((win-conf (current-window-configuration))
(height ,height)
(buffer (my-pop-to-buffer ,buffer-or-name
`(,@(if height
`(display-buffer-at-bottom
(cons window-height height))
`(display-buffer-full-frame))))))
(add-to-list 'margin-work-modes 'quick-buffer-mode)
(with-current-buffer buffer
(setq major-mode 'quick-buffer-mode)
(setq-local pop-buffer-win-conf win-conf)
(let ((inhibit-read-only t))
(erase-buffer)
,@body)
(read-only-mode 1))))
(defun pop-buffer-insert (height &rest body)
(declare (indent defun))
(if (member pop-buffer-insert-buffer
(mapcar #'buffer-name (window-buffers)))
(let ((inhibit-read-only t))
(select-window (get-buffer-window
pop-buffer-insert-buffer))
(erase-buffer)
(apply #'insert body))
(with-pop-buffer pop-buffer-insert-buffer height
(apply #'insert body))))
(defmacro pop-buffer-do (height &rest body)
(declare (indent defun))
`(with-pop-buffer ,pop-buffer-insert-buffer ,height
,@body))
(provide 'tp-tests-utils)

View File

@ -1,87 +1,697 @@
(require 'tp-tests-utils)
;;; tp-ert-tests.el --- ERT tests for tp.el -*- lexical-binding: t -*-
;; tp-layer-alist
;; tp-layer-groups
;; Copyright (C) 2024
(tp-layer-define test1
'(face link :foreground "orange"))
;;; Commentary:
(tp-layer-define test2
'(face link :foreground "cyan"))
;; Comprehensive test suite for tp.el using ERT (Emacs Lisp Regression Testing).
;; Run with: emacs --batch -l tp.el -l tp-ert-tests.el -f ert-run-tests-batch-and-exit
(tp-layer-group-define test-group
test1 '( display "this is top layer"
face (:background "red" :foreground "#000"))
test2 '( display "this is middle layer"
face (:background "green" :foreground "#000"))
test3 '( display "this is bottom layer"
face (:background "cyan" :foreground "#000")))
;;; Code:
(setq tp-layer-alist nil)
(setq tp-layer-groups nil)
(require 'ert)
(require 'cl-lib)
(tp-layer-propertize "emacs" 'test1)
(tp-layer-group-propertize "emacs" 'test-group)
;; Load tp.el from the same directory
(let ((tp-dir (file-name-directory (or load-file-name buffer-file-name))))
(add-to-list 'load-path tp-dir)
(require 'tp))
(defun tp-tests-layer-rotate (&optional btn)
(interactive)
(let ((inhibit-read-only t))
(save-excursion
(goto-char (point-min))
(tp-layer-rotate (line-beginning-position)
(line-end-position)))))
;;; ============================================================
;;; Test Utilities
;;; ============================================================
(pop-buffer-do nil
(insert (tp-layer-group-propertize "emacs" 'test-group)
"\n\n")
(insert-text-button
" eval (tp-tests-layer-rotate) "
'action 'tp-tests-layer-rotate
'face '(:box t)
'follow-link t))
(defmacro tp-test-with-temp-buffer (&rest body)
"Execute BODY in a temporary buffer with tp.el loaded."
(declare (indent 0))
`(with-temp-buffer
(setq tp-layer-alist nil)
(setq tp-layer-groups nil)
,@body))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; ============================================================
;;; Basic Text Property Functions Tests
;;; ============================================================
(pop-buffer-insert nil
(propertize
" "
'display ""
'face '(:underline (:position t :color "grey")))
" "
(propertize
"hacking"
'face '(:foreground "cyan"))
" "
(propertize
"emacs"
'face '(:slant italic :background "orange")))
(ert-deftest tp-test-put-and-get ()
"Test tp-put and tp-get basic functionality."
(tp-test-with-temp-buffer
(insert "Hello World")
;; Set a single property
(tp-put 1 6 'face 'bold)
(should (eq (tp-get 1 'face) 'bold))
(should (eq (tp-get 3 'face) 'bold))
(should (null (tp-get 7 'face)))
;; Set multiple properties
(tp-put 7 12 'face 'italic 'help-echo "test")
(should (eq (tp-get 7 'face) 'italic))
(should (equal (tp-get 7 'help-echo) "test"))))
(with-current-buffer pop-buffer-insert-buffer
(erase-buffer)
(tp-tests-run))
(ert-deftest tp-test-put-with-list ()
"Test tp-put accepts properties as a list."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-put 1 6 '(face bold help-echo "greeting"))
(should (eq (tp-get 1 'face) 'bold))
(should (equal (tp-get 1 'help-echo) "greeting"))))
(with-current-buffer pop-buffer-insert-buffer
(let ((inhibit-read-only 1))
;; (tp-all (point-min) (point-max))
(tp-layer-set 'default (point-min) (point-max))
(tp-layer-push 1
(point-min) (point-max)
'( face (:background "green" :foreground "grey")
display (height 1.2)))
(tp-layer-push 2
(point-min) (point-max)
'(face (:background "orange" :foreground "#000")))
(ert-deftest tp-test-put-returns-region ()
"Test tp-put returns the modified region."
(tp-test-with-temp-buffer
(insert "Hello")
(let ((result (tp-put 1 6 'face 'bold)))
(should (equal result '(1 . 6))))))
(tp-layer-push 3
(point-min) (point-max)
'(face (:weight bold :background "SlateBlue2")))))
(ert-deftest tp-test-remove ()
"Test tp-remove removes a specific property."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-put 1 6 'face 'bold 'help-echo "test")
(should (eq (tp-get 1 'face) 'bold))
(tp-remove 1 6 'face)
(should (null (tp-get 1 'face)))
(should (equal (tp-get 1 'help-echo) "test"))))
(with-current-buffer pop-buffer-insert-buffer
(let ((inhibit-read-only 1))
;; (tp-layer-demote (point-min) (point-max))
;; (tp-layer-rotate (point-min) (point-max))
;; (tp-layer-delete 2 (point-min) (point-max))
;; (tp-layer-pin 1 (point-min) (point-max))
;; (tp-layer-pin '3 1 16)
))
(ert-deftest tp-test-remove-list ()
"Test tp-remove-list removes multiple properties."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-put 1 6 'face 'bold 'help-echo "test" 'mouse-face 'highlight)
(tp-remove-list 1 6 '(face help-echo))
(should (null (tp-get 1 'face)))
(should (null (tp-get 1 'help-echo)))
(should (eq (tp-get 1 'mouse-face) 'highlight))))
(ert-deftest tp-test-clear ()
"Test tp-clear removes all properties."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 6 'face 'bold)
(tp-put 7 12 'face 'italic)
(tp-clear 1 12)
(should (null (tp-get 1 'face)))
(should (null (tp-get 7 'face)))))
(ert-deftest tp-test-clear-defaults-to-buffer ()
"Test tp-clear defaults to entire buffer."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 12 'face 'bold)
(tp-clear)
(should (null (tp-get 1 'face)))
(should (null (tp-get 7 'face)))))
(ert-deftest tp-test-at ()
"Test tp-at returns all properties at point."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-put 1 6 'face 'bold 'help-echo "test")
(let ((props (tp-at 1)))
(should (eq (plist-get props 'face) 'bold))
(should (equal (plist-get props 'help-echo) "test")))))
(ert-deftest tp-test-at-defaults-to-point ()
"Test tp-at defaults to current point."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-put 1 6 'face 'bold)
(goto-char 3)
(should (eq (plist-get (tp-at) 'face) 'bold))))
(ert-deftest tp-test-plist ()
"Test tp-plist merges properties from region."
(tp-test-with-temp-buffer
(insert "Hello World")
;; Put both properties on the same overlapping region for proper merging
(tp-put 1 12 'face 'bold)
(tp-put 1 12 'help-echo "test")
(let ((props (tp-plist 1 12)))
(should (eq (plist-get props 'face) 'bold))
(should (equal (plist-get props 'help-echo) "test")))))
;;; ============================================================
;;; Text Property Interval Tests
;;; ============================================================
(ert-deftest tp-test-empty-p ()
"Test tp-empty-p detects empty properties."
(should (tp-empty-p "plain string"))
(should-not (tp-empty-p (propertize "styled" 'face 'bold))))
(ert-deftest tp-test-intervals ()
"Test tp-intervals returns property intervals."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 6 'face 'bold)
(tp-put 7 12 'face 'italic)
(let ((intervals (tp-intervals 1 12)))
(should (>= (length intervals) 2)))))
;;; ============================================================
;;; Layer Definition Tests
;;; ============================================================
(ert-deftest tp-test-layer-define ()
"Test tp-layer-define creates a layer."
(tp-test-with-temp-buffer
(tp-layer-define test-layer '(face bold help-echo "test"))
(should (assoc 'test-layer tp-layer-alist))
(should (equal (cdr (assoc 'test-layer tp-layer-alist))
'(face bold help-echo "test")))))
(ert-deftest tp-test-layer-define-updates-existing ()
"Test tp-layer-define updates existing layer."
(tp-test-with-temp-buffer
(tp-layer-define test-layer '(face bold))
(tp-layer-define test-layer '(face italic))
(should (equal (cdr (assoc 'test-layer tp-layer-alist))
'(face italic)))))
(ert-deftest tp-test-layer-props ()
"Test tp-layer-props returns properties with tp-name."
(tp-test-with-temp-buffer
(tp-layer-define my-layer '(face bold))
(let ((props (tp-layer-props 'my-layer)))
(should (eq (plist-get props 'face) 'bold))
(should (eq (plist-get props 'tp-name) 'my-layer)))))
(ert-deftest tp-test-layer-props-returns-nil-for-undefined ()
"Test tp-layer-props returns nil for undefined layer."
(tp-test-with-temp-buffer
(should (null (tp-layer-props 'undefined-layer)))))
(ert-deftest tp-test-layer-undefine ()
"Test tp-layer-undefine removes layer definition."
(tp-test-with-temp-buffer
(tp-layer-define test-layer '(face bold))
(should (assoc 'test-layer tp-layer-alist))
(tp-layer-undefine 'test-layer)
(should-not (assoc 'test-layer tp-layer-alist))))
;;; ============================================================
;;; Layer Group Tests
;;; ============================================================
(ert-deftest tp-test-group-define ()
"Test tp-group-define creates a layer group."
(tp-test-with-temp-buffer
(tp-group-define my-group
layer1 '(face bold)
layer2 '(face italic)
layer3 '(face underline))
(should (assoc 'my-group tp-layer-groups))
(should (assoc 'layer1 tp-layer-alist))
(should (assoc 'layer2 tp-layer-alist))
(should (assoc 'layer3 tp-layer-alist))
;; Check all layers are present in the group
(let ((layers (cdr (assoc 'my-group tp-layer-groups))))
(should (= (length layers) 3))
(should (memq 'layer1 layers))
(should (memq 'layer2 layers))
(should (memq 'layer3 layers)))))
(ert-deftest tp-test-group-props ()
"Test tp-group-props returns all layer properties."
(tp-test-with-temp-buffer
(tp-group-define my-group
layer1 '(face bold)
layer2 '(face italic))
(let ((props-list (tp-group-props 'my-group)))
(should (= (length props-list) 2))
;; Check that both layers are present (order may vary)
(let ((faces (mapcar (lambda (p) (plist-get p 'face)) props-list)))
(should (or (memq 'bold faces) (memq 'italic faces)))))))
(ert-deftest tp-test-group-undefine ()
"Test tp-group-undefine removes group definition."
(tp-test-with-temp-buffer
(tp-group-define my-group
layer1 '(face bold))
(should (assoc 'my-group tp-layer-groups))
(tp-group-undefine 'my-group)
(should-not (assoc 'my-group tp-layer-groups))))
(ert-deftest tp-test-layer-reset ()
"Test tp-layer-reset clears all definitions."
(tp-test-with-temp-buffer
(tp-layer-define layer1 '(face bold))
(tp-group-define group1 layer2 '(face italic))
(should tp-layer-alist)
(should tp-layer-groups)
(tp-layer-reset)
(should-not tp-layer-alist)
(should-not tp-layer-groups)))
;;; ============================================================
;;; Layer Stack Operations Tests
;;; ============================================================
(ert-deftest tp-test-layer-push ()
"Test tp-layer-push adds layer to stack."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-push 1 6 'layer1)
(should (eq (tp-get 1 'face) 'bold))
(should (eq (tp-get 1 'tp-name) 'layer1))))
(ert-deftest tp-test-layer-push-multiple ()
"Test pushing multiple layers."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
;; layer2 should be on top (visible)
(should (eq (tp-get 1 'face) 'italic))
(should (eq (tp-get 1 'tp-name) 'layer2))
;; layer1 should be in the stack below
(should (tp-get 1 'tp-layers))))
(ert-deftest tp-test-layer-push-error-on-duplicate ()
"Test tp-layer-push errors on duplicate layer."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-push 1 6 'layer1)
(should-error (tp-layer-push 1 6 'layer1))))
(ert-deftest tp-test-layer-delete ()
"Test tp-layer-delete removes layer from stack."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
;; Delete top layer
(tp-layer-delete 1 6 'layer2)
;; layer1 should now be visible
(should (eq (tp-get 1 'face) 'bold))
(should (eq (tp-get 1 'tp-name) 'layer1))))
(ert-deftest tp-test-layer-delete-from-middle ()
"Test deleting layer from middle of stack."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-define layer3 '(face underline))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
(tp-layer-push 1 6 'layer3)
;; Delete middle layer
(tp-layer-delete 1 6 'layer2)
;; Top layer should still be visible
(should (eq (tp-get 1 'tp-name) 'layer3))
;; layer2 should not exist anymore
(should-not (tp-layer-exists-p 1 6 'layer2))))
(ert-deftest tp-test-layer-rotate ()
"Test tp-layer-rotate cycles layers."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-define layer3 '(face underline))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
(tp-layer-push 1 6 'layer3)
;; layer3 is on top
(should (eq (tp-layer-top 1 6) 'layer3))
;; Rotate once - layer2 should be on top
(tp-layer-rotate 1 6)
(should (eq (tp-layer-top 1 6) 'layer2))
;; Rotate again - layer1 should be on top
(tp-layer-rotate 1 6)
(should (eq (tp-layer-top 1 6) 'layer1))
;; Rotate again - layer3 should be on top (cycled back)
(tp-layer-rotate 1 6)
(should (eq (tp-layer-top 1 6) 'layer3))))
(ert-deftest tp-test-layer-pin ()
"Test tp-layer-pin brings layer to top."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-define layer3 '(face underline))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
(tp-layer-push 1 6 'layer3)
;; Pin layer1 to top
(tp-layer-pin 1 6 'layer1)
(should (eq (tp-layer-top 1 6) 'layer1))))
(ert-deftest tp-test-layer-pin-error-on-nonexistent ()
"Test tp-layer-pin errors on nonexistent layer."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-push 1 6 'layer1)
(should-error (tp-layer-pin 1 6 'nonexistent))))
(ert-deftest tp-test-layer-hide ()
"Test tp-layer-hide moves layer to bottom."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
;; layer2 is on top
(should (eq (tp-layer-top 1 6) 'layer2))
;; Hide layer2
(tp-layer-hide 1 6 'layer2)
;; layer1 should now be on top
(should (eq (tp-layer-top 1 6) 'layer1))))
(ert-deftest tp-test-layer-show ()
"Test tp-layer-show brings layer to top."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
;; Hide layer2
(tp-layer-hide 1 6 'layer2)
(should (eq (tp-layer-top 1 6) 'layer1))
;; Show layer2 again
(tp-layer-show 1 6 'layer2)
(should (eq (tp-layer-top 1 6) 'layer2))))
;;; ============================================================
;;; Layer Query Tests
;;; ============================================================
(ert-deftest tp-test-layer-list ()
"Test tp-layer-list returns all layer names."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-define layer3 '(face underline))
(tp-layer-push 1 6 'layer1)
(tp-layer-push 1 6 'layer2)
(tp-layer-push 1 6 'layer3)
(let ((layers (tp-layer-list 1 6)))
(should (= (length layers) 3))
(should (memq 'layer1 layers))
(should (memq 'layer2 layers))
(should (memq 'layer3 layers)))))
(ert-deftest tp-test-layer-count ()
"Test tp-layer-count returns correct count."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-push 1 6 'layer1)
(should (= (tp-layer-count 1 6) 1))
(tp-layer-push 1 6 'layer2)
(should (= (tp-layer-count 1 6) 2))))
(ert-deftest tp-test-layer-exists-p ()
"Test tp-layer-exists-p correctly detects layers."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-push 1 6 'layer1)
(should (tp-layer-exists-p 1 6 'layer1))
(should-not (tp-layer-exists-p 1 6 'layer2))))
(ert-deftest tp-test-layer-top ()
"Test tp-layer-top returns top layer name."
(tp-test-with-temp-buffer
(insert "Hello")
(tp-layer-define layer1 '(face bold))
(tp-layer-define layer2 '(face italic))
(tp-layer-push 1 6 'layer1)
(should (eq (tp-layer-top 1 6) 'layer1))
(tp-layer-push 1 6 'layer2)
(should (eq (tp-layer-top 1 6) 'layer2))))
;;; ============================================================
;;; Propertize String Tests
;;; ============================================================
(ert-deftest tp-test-propertize ()
"Test tp-propertize adds properties to string."
(let ((str (tp-propertize "Hello" 'face 'bold)))
(should (eq (get-text-property 0 'face str) 'bold))))
(ert-deftest tp-test-propertize-with-list ()
"Test tp-propertize accepts properties as list."
(let ((str (tp-propertize "Hello" '(face bold help-echo "test"))))
(should (eq (get-text-property 0 'face str) 'bold))
(should (equal (get-text-property 0 'help-echo str) "test"))))
(ert-deftest tp-test-layer-propertize ()
"Test tp-layer-propertize applies layer to string."
(tp-test-with-temp-buffer
(tp-layer-define my-layer '(face bold help-echo "greeting"))
(let ((str (tp-layer-propertize "Hello" 'my-layer)))
(should (eq (get-text-property 0 'face str) 'bold))
(should (equal (get-text-property 0 'help-echo str) "greeting")))))
(ert-deftest tp-test-layer-propertize-error-on-undefined ()
"Test tp-layer-propertize errors on undefined layer."
(tp-test-with-temp-buffer
(should-error (tp-layer-propertize "Hello" 'undefined-layer))))
(ert-deftest tp-test-group-propertize ()
"Test tp-group-propertize applies group to string."
(tp-test-with-temp-buffer
(tp-group-define my-group
layer1 '(face bold)
layer2 '(help-echo "test"))
(let ((str (tp-group-propertize "Hello" 'my-group)))
(should (stringp str))
(should (= (length str) 5)))))
(ert-deftest tp-test-group-propertize-error-on-undefined ()
"Test tp-group-propertize errors on undefined group."
(tp-test-with-temp-buffer
(should-error (tp-group-propertize "Hello" 'undefined-group))))
;;; ============================================================
;;; Match and Regexp Tests
;;; ============================================================
(ert-deftest tp-test-match ()
"Test tp-match sets properties on string matches."
(tp-test-with-temp-buffer
(insert "Hello World Hello")
(let ((regions (tp-match "Hello" 'face 'bold)))
(should (= (length regions) 2))
(should (eq (tp-get 1 'face) 'bold))
(should (eq (tp-get 13 'face) 'bold)))))
(ert-deftest tp-test-match-returns-regions ()
"Test tp-match returns correct region pairs."
(tp-test-with-temp-buffer
(insert "Hello World Hello")
(let ((regions (tp-match "Hello")))
(should (= (length regions) 2))
(should (equal (car regions) '(1 . 6)))
(should (equal (cadr regions) '(13 . 18))))))
(ert-deftest tp-test-regexp ()
"Test tp-regexp sets properties on regexp matches."
(tp-test-with-temp-buffer
(insert "abc 123 def 456")
(let ((regions (tp-regexp "[0-9]+" 'face 'bold)))
(should (= (length regions) 2))
(should (eq (tp-get 5 'face) 'bold))
(should (eq (tp-get 13 'face) 'bold)))))
(ert-deftest tp-test-regexp-returns-regions ()
"Test tp-regexp returns correct region pairs."
(tp-test-with-temp-buffer
(insert "abc 123 def 456")
(let ((regions (tp-regexp "[0-9]+")))
(should (= (length regions) 2)))))
;;; ============================================================
;;; Search and Navigation Tests
;;; ============================================================
(ert-deftest tp-test-forward ()
"Test tp-forward finds next property."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 7 12 'face 'bold)
(goto-char 1)
;; text-property-search-forward may not exist in all Emacs versions
(skip-unless (fboundp 'text-property-search-forward))
(let ((match (tp-forward 'face)))
(should match)
(should (= (prop-match-beginning match) 7)))))
(ert-deftest tp-test-backward ()
"Test tp-backward finds previous property."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 6 'face 'bold)
(goto-char 12)
;; text-property-search-backward may not exist in all Emacs versions
;; Skip test if function is not available
(skip-unless (fboundp 'text-property-search-backward))
(let ((match (tp-backward 'face)))
(should match)
(should (= (prop-match-beginning match) 1)))))
(ert-deftest tp-test-next ()
"Test tp-next returns next position with property."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 7 12 'face 'bold)
(let ((pos (tp-next 1 'face)))
(should (= pos 7)))))
(ert-deftest tp-test-prev ()
"Test tp-prev returns previous position with property."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 6 'face 'bold)
(let ((pos (tp-prev 12 'face)))
(should (= pos 1)))))
(ert-deftest tp-test-goto-next ()
"Test tp-goto-next moves point."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 7 12 'face 'bold)
(goto-char 1)
(tp-goto-next 'face)
(should (= (point) 7))))
(ert-deftest tp-test-goto-prev ()
"Test tp-goto-prev moves point."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 6 'face 'bold)
(goto-char 12)
(tp-goto-prev 'face)
(should (= (point) 1))))
;;; ============================================================
;;; Utility Function Tests
;;; ============================================================
(ert-deftest tp-test-in ()
"Test tp-in finds regions with property."
(tp-test-with-temp-buffer
(insert "Hello World Test")
(tp-put 1 6 'my-prop 'value1)
(tp-put 7 12 'my-prop 'value2)
(let ((regions (tp-in 'my-prop)))
(should (= (length regions) 2)))))
(ert-deftest tp-test-in-with-value ()
"Test tp-in filters by value."
(tp-test-with-temp-buffer
(insert "Hello World Test")
(tp-put 1 6 'my-prop 'value1)
(tp-put 7 12 'my-prop 'value2)
(let ((regions (tp-in 'my-prop 'value1)))
(should (= (length regions) 1))
(should (equal (car (car regions)) 1)))))
(ert-deftest tp-test-all ()
"Test tp-all returns all regions with properties."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 6 'face 'bold)
(tp-put 7 12 'face 'italic)
(let ((regions (tp-all)))
(should (>= (length regions) 2)))))
(ert-deftest tp-test-regions-map ()
"Test tp-regions-map applies function to regions."
(tp-test-with-temp-buffer
(insert "Hello World Hello")
(tp-put 1 6 'marker t)
(tp-put 13 18 'marker t)
(let ((result nil))
(tp-regions-map
(lambda (start end idx)
(push (list start end idx) result))
'marker)
(should (= (length result) 2)))))
(ert-deftest tp-test-strings-map ()
"Test tp-strings-map applies function to strings."
(tp-test-with-temp-buffer
(insert "Hello World Hello")
(tp-put 1 6 'marker t)
(tp-put 13 18 'marker t)
(let ((result nil))
(tp-strings-map
(lambda (str idx)
(push str result))
'marker)
(should (= (length result) 2))
(should (member "Hello" result)))))
;;; ============================================================
;;; Alias Tests
;;; ============================================================
(ert-deftest tp-test-aliases-exist ()
"Test that all aliases are properly defined."
(should (fboundp 'tp-set))
(should (fboundp 'tp-layer-properties))
(should (fboundp 'tp-layer-group-define))
(should (fboundp 'tp-layer-group-properties))
(should (fboundp 'tp-layer-group-propertize))
(should (fboundp 'tp-layer-group-undefine)))
(ert-deftest tp-test-aliases-work ()
"Test that aliases work correctly."
(tp-test-with-temp-buffer
;; Test tp-set alias
(insert "Hello")
(tp-set 1 6 'face 'bold)
(should (eq (tp-get 1 'face) 'bold))))
;;; ============================================================
;;; Edge Case Tests
;;; ============================================================
(ert-deftest tp-test-empty-region ()
"Test operations on empty buffer."
(tp-test-with-temp-buffer
(should (null (tp-at 1)))
(should (null (tp-all)))))
(ert-deftest tp-test-overlapping-regions ()
"Test overlapping property regions."
(tp-test-with-temp-buffer
(insert "Hello World")
(tp-put 1 8 'prop1 'val1)
(tp-put 5 12 'prop2 'val2)
(should (eq (tp-get 1 'prop1) 'val1))
(should (null (tp-get 1 'prop2)))
(should (eq (tp-get 6 'prop1) 'val1))
(should (eq (tp-get 6 'prop2) 'val2))
(should (null (tp-get 10 'prop1)))
(should (eq (tp-get 10 'prop2) 'val2))))
(ert-deftest tp-test-single-char-region ()
"Test operations on single character."
(tp-test-with-temp-buffer
(insert "H")
(tp-put 1 2 'face 'bold)
(should (eq (tp-get 1 'face) 'bold))))
(ert-deftest tp-test-layer-on-string ()
"Test layer operations on string object."
(let ((str (copy-sequence "Hello")))
(set-text-properties 0 5 nil str)
(should (tp-empty-p str))))
(provide 'tp-ert-tests)
;;; tp-ert-tests.el ends here