;;; testcover-tests.el --- Testcover test suite -*- lexical-binding:t -*- ;; Copyright (C) 2017-2021 Free Software Foundation, Inc. ;; Author: Gemini Lasswell ;; This file is part of GNU Emacs. ;; GNU Emacs 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. ;; GNU Emacs 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 GNU Emacs. If not, see . ;;; Commentary: ;; Testcover test suite. ;; * All the test cases are in testcover-resources/testcover-cases.el. ;; See that file for an explanation of the test case format. ;; * `testcover-tests-define-tests', which is run when this file is ;; loaded, reads testcover-resources/testcover-cases.el and defines ;; ERT tests for each test case. ;;; Code: (require 'ert) (require 'ert-x) (require 'testcover) (require 'skeleton) ;; Convert Testcover's overlays to plain text. (eval-and-compile (defun testcover-tests-markup-region (beg end &rest optargs) "Mark up test code within region between BEG and END. Convert Testcover's tan and red splotches to %%% and !!! for testcases.el. This can be used to create test cases if Testcover is working correctly on a code sample. OPTARGS are optional arguments for `testcover-start'." (interactive "r") (let ((tempfile (make-temp-file "testcover-tests-" nil ".el")) (find-file-suppress-same-file-warnings t) (code (buffer-substring beg end)) (marked-up-code)) (unwind-protect (progn (with-temp-file tempfile (insert code)) (save-current-buffer (let ((buf (find-file-noselect tempfile))) (set-buffer buf) (apply 'testcover-start (cons tempfile optargs)) (testcover-mark-all buf) (dolist (overlay (overlays-in (point-min) (point-max))) (let ((ov-face (overlay-get overlay 'face))) (goto-char (overlay-end overlay)) (cond ((eq ov-face 'testcover-nohits) (insert "!!!")) ((eq ov-face 'testcover-1value) (insert "%%%")) (t nil)))) (setq marked-up-code (buffer-string))) (set-buffer-modified-p nil))) (ignore-errors (kill-buffer (find-file-noselect tempfile))) (ignore-errors (delete-file tempfile))) ;; Now replace the original code with the marked up code. (delete-region beg end) (insert marked-up-code)))) (eval-and-compile (defun testcover-tests-unmarkup-region (beg end) "Remove the markup used in testcases.el between BEG and END." (interactive "r") (save-excursion (save-restriction (narrow-to-region beg end) (goto-char (point-min)) (while (re-search-forward "!!!\\|%%%" nil t) (replace-match "")))))) (define-skeleton testcover-tests-skeleton "Write a testcase for testcover-tests.el." "Enter name of test: " ";; ==== " str " ====\n" "\"docstring\"\n" ";; Directives for ERT should go here, if any.\n" ";; ====\n" ";; Replace this line with annotated test code.\n") ;; Check a test case. (eval-and-compile (defun testcover-tests-run-test-case (marked-up-code) "Test the operation of Testcover on the string MARKED-UP-CODE." (let ((tempfile (make-temp-file "testcover-tests-" nil ".el")) (find-file-suppress-same-file-warnings t)) (unwind-protect (progn (with-temp-file tempfile (insert marked-up-code)) ;; Remove the marks and mark the code up again. The original ;; and recreated versions should match. (save-current-buffer (set-buffer (find-file-noselect tempfile)) ;; Fail the test if the debugger tries to become active, ;; which can happen if Testcover fails to attach itself ;; correctly. Note that this will prevent debugging ;; these tests using Edebug. (cl-letf (((symbol-function #'edebug-default-enter) (lambda (&rest _args) (ert-fail "Debugger invoked during test run")))) (dolist (byte-compile '(t nil)) (testcover-tests-unmarkup-region (point-min) (point-max)) (unwind-protect (testcover-tests-markup-region (point-min) (point-max) byte-compile) (set-buffer-modified-p nil)) (should (string= marked-up-code (buffer-string))))))) (ignore-errors (kill-buffer (find-file-noselect tempfile))) (ignore-errors (delete-file tempfile)))))) ;; Convert test case file to ert-defmethod. (eval-and-compile (defun testcover-tests-build-test-cases () "Parse the test case file and return a list of ERT test definitions. Construct and return a list of `ert-deftest' forms. See testcases.el for documentation of the test definition format." (let (results) (with-temp-buffer (insert-file-contents (ert-resource-file "testcases.el")) (goto-char (point-min)) (while (re-search-forward (concat "^;; ==== \\([^ ]+?\\) ====\n" "\\(\\(?:.*\n\\)*?\\)" ";; ====\n" "\\(\\(?:.*\n\\)*?\\)" "\\(\\'\\|;; ====\\)") nil t) (let ((name (match-string 1)) (splice (car (read-from-string (format "(%s)" (match-string 2))))) (code (match-string 3))) (push `(ert-deftest ,(intern (concat "testcover-tests-" name)) () ,@splice (testcover-tests-run-test-case ,code)) results)) (beginning-of-line))) results))) ;; Define all the tests. (defmacro testcover-tests-define-tests () "Construct and define ERT test methods using the test case file." (let* ((test-cases (testcover-tests-build-test-cases))) `(progn ,@test-cases))) (testcover-tests-define-tests) (provide 'testcover-tests) ;;; testcover-tests.el ends here