;;; compat-tests.el --- Tests for Compat -*- lexical-binding: t; no-byte-compile: t; -*- ;; Copyright (C) 2021-2025 Free Software Foundation, Inc. ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; Tests for compatibility functions from compat.el. ;; ;; Note that all functions are covered by tests. When new functions are ;; added to Compat, they must come with test coverage! ;; Functions are marked with a link to the test suite. The link type ;; `compat-tests' must be registered first by evaluating the following ;; code. If you intend to work on the test suite you can add this code to ;; your init.el. ;; ;; (require 'ol) ;; (org-link-set-parameters ;; "compat-tests" ;; :follow ;; (lambda (link _) ;; (org-link-open-from-string ;; (format "[[file:compat-tests.el::ert-deftest compat-%s ()]]" link)))) ;; ;; You can then jump to the links with the command ;; `org-open-at-point-global', ideally bound to a convenient key. ;; The tests are written in a simple, explicit style. Please inspect the ;; tests in order to find out the supported calling conventions. In ;; particular, note the use of `compat-call' to call functions, where the ;; calling convention or behavior changed between Emacs versions. ;; The functions tested here are guaranteed to work on the Emacs versions ;; tested by continuous integration. This includes 24.4, 24.5, 25.1, 25.2, ;; 25.3, 26.1, 26.2, 26.3, 27.1, 27.2, 28.1, 28.2, 29.1 and the current ;; Emacs master branch. ;;; Code: (require 'compat) (require 'ert-x) (require 'subr-x) (require 'time-date) (require 'image) (require 'text-property-search nil t) (require 'color) ;; Setup tramp mock (require 'tramp) (add-to-list 'tramp-methods '("mock" (tramp-login-program "sh") (tramp-login-args (("-i"))) (tramp-direct-async ("-c")) (tramp-remote-shell "/bin/sh") (tramp-remote-shell-args ("-c")) (tramp-connection-timeout 10))) (add-to-list 'tramp-default-host-alist `("\\`mock\\'" nil ,(system-name))) (defmacro should-equal (a b) `(should (equal ,a ,b))) (ert-deftest compat-loaded-features () (let ((version 0)) (while (< version 31) (should-equal (> version emacs-major-version) (featurep (intern (format "compat-%s" version)))) (setq version (1+ version))))) (ert-deftest compat-function () (let ((sym (compat-function plist-put)) list) (should sym) (should (symbolp sym)) (setq list (funcall sym list "first" 1 #'string=)) (should-equal (compat-call plist-get list "first" #'string=) 1))) (defconst compat-tests--version (package-get-version)) (ert-deftest compat-package-get-version () (should (stringp compat-tests--version)) (should-equal 30 (car (version-to-list compat-tests--version)))) (ert-deftest compat-buffer-match-p () (let ((b "*compat-test-buffer*") (child-mode (make-symbol "child")) (parent-mode (make-symbol "parent"))) (put child-mode 'derived-mode-parent parent-mode) (with-current-buffer (get-buffer-create b) (setq major-mode child-mode)) (should (buffer-match-p t b)) (should-not (buffer-match-p nil b)) (should (buffer-match-p "compat" b)) (should (buffer-match-p #'always b)) (should-not (buffer-match-p #'ignore b)) (should (buffer-match-p `(derived-mode . ,parent-mode) b)) (should-not (buffer-match-p '(derived-mode . text-mode) b)) (should (buffer-match-p `(major-mode . ,child-mode) b)) (should-not (buffer-match-p '(major-mode . prog-mode) b)) (should (buffer-match-p '(not (major-mode . prog-mode)) b)) (should (buffer-match-p `(and (major-mode . ,child-mode) "compat" t) b)) (should (buffer-match-p `(or (major-mode . prog-mode) "foo" t) b)))) (ert-deftest compat-match-buffers () (let ((b1 (get-buffer-create "*compat-buffer1*")) (b2 (get-buffer-create "*compat-buffer2*")) (b3 (get-buffer-create "*compat-buffer3*")) (m1 (make-symbol "mode1")) (m2 (make-symbol "mode2")) (m3 (make-symbol "mode3"))) (with-current-buffer b1 (setq major-mode m1)) (with-current-buffer b2 (setq major-mode m2)) (with-current-buffer b3 (setq major-mode m3)) (should-equal (list b2 b1) (match-buffers `(or (major-mode . ,m1) (major-mode . ,m2)) (list b1 b2 b3))))) (ert-deftest compat-thing-at-mouse () (save-window-excursion (with-temp-buffer (let ((event `(mouse-1 (,(selected-window) 1 (0 . 0) 0)))) (set-window-buffer nil (current-buffer)) (insert "https://emacs.org/") (goto-char (point-min)) (should-equal "https://emacs.org/" (thing-at-mouse event 'url)) (should-equal '(1 . 19) (bounds-of-thing-at-mouse event 'url)) (should-not (region-active-p)) (mark-thing-at-mouse event 'url) (should-equal (point) 19) (should-equal '((1 . 19)) (region-bounds)) (let ((mouse-select-region-move-to-beginning t)) (mark-thing-at-mouse event 'url)) (should-equal (point) 1) (should-equal '((1 . 19)) (region-bounds)))))) (ert-deftest compat-dolist-with-progress-reporter () (let (y) (should-equal (dolist-with-progress-reporter (x '(1 2 3) y) "Reporter" (push x y)) '(3 2 1))) (let (y) (should-equal (dolist-with-progress-reporter (x '(1 2 3) y) (make-progress-reporter "Reporter") (push x y)) '(3 2 1)))) (ert-deftest compat-minibuffer-history-value () (let ((minibuffer-history-variable 'file-name-history) (file-name-history '("a" "b" "c"))) (should-equal (minibuffer-history-value) '("a" "b" "c"))) (let ((file-name-history '("x" "y" "z"))) (should-equal (catch 'compat-tests--exit (minibuffer-with-setup-hook (lambda () (message "%S" minibuffer-history-variable) (throw 'compat-tests--exit (minibuffer-history-value))) (let ((executing-kbd-macro t)) (completing-read "Prompt: " #'completion-file-name-table nil nil nil 'file-name-history)))) '("x" "y" "z")))) (ert-deftest compat-with-minibuffer-selected-window () (let (ran) (should-not (minibuffer-selected-window)) (should-not (with-minibuffer-selected-window (setq ran t))) (should-not ran) (unwind-protect (progn (advice-add #'minibuffer-selected-window :override #'selected-window) (should-equal 'result (with-minibuffer-selected-window (setq ran t) 'result)) (should ran)) (advice-remove #'minibuffer-selected-window #'selected-window)))) (ert-deftest compat-fixnump () (should (fixnump 0)) (should (fixnump most-negative-fixnum)) (should (fixnump most-positive-fixnum))) (ert-deftest compat-bignump () (should-not (bignump 0)) (should-not (bignump most-negative-fixnum)) (should-not (bignump most-positive-fixnum)) (should-equal (bignump (1+ most-positive-fixnum)) (> emacs-major-version 26)) (should-equal (bignump (1- most-negative-fixnum)) (> emacs-major-version 26))) (ert-deftest compat-buttonize () (let ((b (buttonize "button" 'c 'd 'h))) (should-equal b "button") (should-equal 'c (get-text-property 0 'action b)) (should-equal 'c (get-text-property 5 'action b)) (should-equal 'd (get-text-property 0 'button-data b)) (should-equal 'd (get-text-property 5 'button-data b)) (should-equal 'h (get-text-property 0 'help-echo b)) (should-equal 'h (get-text-property 5 'help-echo b)))) (ert-deftest compat-obsolete-button-buttonize () (let ((b (with-no-warnings (button-buttonize "button" 'c 'd)))) (should-equal b "button") (should-equal 'c (get-text-property 0 'action b)) (should-equal 'c (get-text-property 5 'action b)) (should-equal 'd (get-text-property 0 'button-data b)) (should-equal 'd (get-text-property 5 'button-data b)))) (ert-deftest compat-buttonize-region () (with-temp-buffer (insert "