summaryrefslogtreecommitdiff
path: root/test/lisp/progmodes/flymake-tests.el
diff options
context:
space:
mode:
Diffstat (limited to 'test/lisp/progmodes/flymake-tests.el')
-rw-r--r--test/lisp/progmodes/flymake-tests.el361
1 files changed, 327 insertions, 34 deletions
diff --git a/test/lisp/progmodes/flymake-tests.el b/test/lisp/progmodes/flymake-tests.el
index 9bf6e7aa178..8eb180a5130 100644
--- a/test/lisp/progmodes/flymake-tests.el
+++ b/test/lisp/progmodes/flymake-tests.el
@@ -1,4 +1,4 @@
-;;; flymake-tests.el --- Test suite for flymake
+;;; flymake-tests.el --- Test suite for flymake -*- lexical-binding: t -*-
;; Copyright (C) 2011-2017 Free Software Foundation, Inc.
@@ -17,63 +17,356 @@
;; 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 <http://www.gnu.org/licenses/>.
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;;; Code:
(require 'ert)
(require 'flymake)
+(eval-when-compile (require 'subr-x)) ; string-trim
(defvar flymake-tests-data-directory
- (expand-file-name "lisp/progmodes/flymake-resources" (getenv "EMACS_TEST_DIRECTORY"))
+ (expand-file-name "lisp/progmodes/flymake-resources"
+ (or (getenv "EMACS_TEST_DIRECTORY")
+ (expand-file-name "../../.."
+ (or load-file-name
+ buffer-file-name))))
"Directory containing flymake test data.")
-;; Warning predicate
-(defun flymake-tests--current-face (file predicate)
- (let ((buffer (find-file-noselect
- (expand-file-name file flymake-tests-data-directory)))
- (process-environment (cons "LC_ALL=C" process-environment))
- (i 0))
+;;
+;;
+(defun flymake-tests--wait-for-backends ()
+ ;; Weirdness here... https://debbugs.gnu.org/17647#25
+ ;; ... meaning `sleep-for', and even
+ ;; `accept-process-output', won't suffice as ways to get
+ ;; process filters and sentinels to run, though they do work
+ ;; fine in a non-interactive batch session. The only thing
+ ;; that will indeed unblock pending process output is
+ ;; reading an input event, so, as a workaround, use a dummy
+ ;; `read-event' with a very short timeout.
+ (unless noninteractive (read-event "" nil 0.1))
+ (cl-loop repeat 5
+ for notdone = (cl-set-difference (flymake-running-backends)
+ (flymake-reporting-backends))
+ while notdone
+ unless noninteractive do (read-event "" nil 0.1)
+ do (sleep-for (+ 0.5 flymake-no-changes-timeout))
+ finally (when notdone (ert-fail
+ (format "Some backends not reporting yet %s"
+ notdone)))))
+
+(cl-defun flymake-tests--call-with-fixture (fn file
+ &key (severity-predicate
+ nil sev-pred-supplied-p))
+ "Call FN after flymake setup in FILE, using `flymake-proc`.
+SEVERITY-PREDICATE is used to setup
+`flymake-proc-diagnostic-type-pred'"
+ (let* ((file (expand-file-name file flymake-tests-data-directory))
+ (visiting (find-buffer-visiting file))
+ (buffer (or visiting (find-file-noselect file)))
+ (process-environment (cons "LC_ALL=C" process-environment))
+ (warning-minimum-log-level :error))
(unwind-protect
(with-current-buffer buffer
- (setq-local flymake-warning-predicate predicate)
- (goto-char (point-min))
- (flymake-mode 1)
- ;; Weirdness here... http://debbugs.gnu.org/17647#25
- (while (and flymake-is-running (< (setq i (1+ i)) 10))
- (sleep-for (+ 0.5 flymake-no-changes-timeout)))
- (flymake-goto-next-error)
- (face-at-point))
- (and buffer (let (kill-buffer-query-functions) (kill-buffer buffer))))))
+ (save-excursion
+ (when sev-pred-supplied-p
+ (setq-local flymake-proc-diagnostic-type-pred severity-predicate))
+ (goto-char (point-min))
+ (let ((flymake-start-on-flymake-mode nil))
+ (unless flymake-mode (flymake-mode 1)))
+ (flymake-start)
+ (flymake-tests--wait-for-backends)
+ (funcall fn)))
+ (and buffer
+ (not visiting)
+ (let (kill-buffer-query-functions) (kill-buffer buffer))))))
+
+(cl-defmacro flymake-tests--with-flymake ((file &rest args)
+ &body body)
+ (declare (indent 1)
+ (debug (sexp &rest form)))
+ `(flymake-tests--call-with-fixture (lambda () ,@body) ,file ,@args))
(ert-deftest warning-predicate-rx-gcc ()
"Test GCC warning via regexp predicate."
(skip-unless (and (executable-find "gcc") (executable-find "make")))
- (should (eq 'flymake-warnline
- (flymake-tests--current-face "test.c" "^[Ww]arning"))))
+ (flymake-tests--with-flymake
+ ("test.c" :severity-predicate "^[Ww]arning")
+ (flymake-goto-next-error)
+ (should (eq 'flymake-warning
+ (face-at-point)))))
(ert-deftest warning-predicate-function-gcc ()
"Test GCC warning via function predicate."
(skip-unless (and (executable-find "gcc") (executable-find "make")))
- (should (eq 'flymake-warnline
- (flymake-tests--current-face "test.c"
- (lambda (msg) (string-match "^[Ww]arning" msg))))))
+ (flymake-tests--with-flymake
+ ("test.c" :severity-predicate
+ (lambda (msg) (string-match "^[Ww]arning" msg)))
+ (flymake-goto-next-error)
+ (should (eq 'flymake-warning
+ (face-at-point)))))
-(ert-deftest warning-predicate-rx-perl ()
- "Test perl warning via regular expression predicate."
+(ert-deftest perl-backend ()
+ "Test the perl backend"
(skip-unless (executable-find "perl"))
- (should (eq 'flymake-warnline
- (flymake-tests--current-face "test.pl" "^Scalar value"))))
+ (flymake-tests--with-flymake ("test.pl")
+ (flymake-goto-next-error)
+ (should (eq 'flymake-warning (face-at-point)))
+ (goto-char (point-max))
+ (flymake-goto-prev-error)
+ (should (eq 'flymake-error (face-at-point)))))
+
+(ert-deftest ruby-backend ()
+ "Test the ruby backend"
+ (skip-unless (executable-find "ruby"))
+ ;; Some versions of ruby fail if HOME doesn't exist (bug#29187).
+ (let* ((tempdir (make-temp-file "flymake-tests-ruby" t))
+ (process-environment (cons (format "HOME=%s" tempdir)
+ process-environment))
+ ;; And see https://debbugs.gnu.org/cgi/bugreport.cgi?bug=19657#20
+ ;; for this particular yuckiness
+ (abbreviated-home-dir nil))
+ (unwind-protect
+ (flymake-tests--with-flymake ("test.rb")
+ (flymake-goto-next-error)
+ (should (eq 'flymake-warning (face-at-point)))
+ (flymake-goto-next-error)
+ (should (eq 'flymake-error (face-at-point))))
+ (delete-directory tempdir t))))
+
+(ert-deftest different-diagnostic-types ()
+ "Test GCC warning via function predicate."
+ (skip-unless (and (executable-find "gcc")
+ (version<=
+ "5" (string-trim
+ (shell-command-to-string "gcc -dumpversion")))
+ (executable-find "make")))
+ (let ((flymake-wrap-around nil))
+ (flymake-tests--with-flymake
+ ("errors-and-warnings.c")
+ (flymake-goto-next-error)
+ (should (eq 'flymake-error (face-at-point)))
+ (flymake-goto-next-error)
+ (should (eq 'flymake-note (face-at-point)))
+ (flymake-goto-next-error)
+ (should (eq 'flymake-warning (face-at-point)))
+ (flymake-goto-next-error)
+ (should (eq 'flymake-error (face-at-point)))
+ (flymake-goto-next-error)
+ (should (eq 'flymake-warning (face-at-point)))
+ (flymake-goto-next-error)
+ (should (eq 'flymake-warning (face-at-point)))
+ (should-error (flymake-goto-next-error nil nil t)))))
+
+(ert-deftest included-c-header-files ()
+ "Test inclusion of .h header files."
+ (skip-unless (and (executable-find "gcc") (executable-find "make")))
+ (let ((flymake-wrap-around nil))
+ (flymake-tests--with-flymake
+ ("some-problems.h")
+ (flymake-goto-next-error)
+ (should (eq 'flymake-warning (face-at-point)))
+ (flymake-goto-next-error)
+ (should (eq 'flymake-error (face-at-point)))
+ (should-error (flymake-goto-next-error nil nil t)))
+ (flymake-tests--with-flymake
+ ("no-problems.h")
+ (should-error (flymake-goto-next-error nil nil t)))))
+
+(defmacro flymake-tests--assert-set (set
+ should
+ should-not)
+ (declare (indent 1))
+ `(progn
+ ,@(cl-loop
+ for s in should
+ collect `(should (memq (quote ,s) ,set)))
+ ,@(cl-loop
+ for s in should-not
+ collect `(should-not (memq (quote ,s) ,set)))))
+
+(defun flymake-tests--diagnose-words
+ (report-fn type words)
+ "Helper. Call REPORT-FN with diagnostics for WORDS in buffer."
+ (funcall report-fn
+ (cl-loop
+ for word in words
+ append
+ (save-excursion
+ (goto-char (point-min))
+ (cl-loop while (word-search-forward word nil t)
+ collect (flymake-make-diagnostic
+ (current-buffer)
+ (match-beginning 0)
+ (match-end 0)
+ type
+ (concat word " is wrong")))))))
+
+(ert-deftest dummy-backends ()
+ "Test many different kinds of backends."
+ (with-temp-buffer
+ (cl-letf
+ (((symbol-function 'error-backend)
+ (lambda (report-fn)
+ (run-with-timer
+ 0.5 nil
+ #'flymake-tests--diagnose-words report-fn :error '("manha" "prognata"))))
+ ((symbol-function 'warning-backend)
+ (lambda (report-fn)
+ (run-with-timer
+ 0.5 nil
+ #'flymake-tests--diagnose-words report-fn :warning '("ut" "dolor"))))
+ ((symbol-function 'sync-backend)
+ (lambda (report-fn)
+ (flymake-tests--diagnose-words report-fn :note '("quis" "commodo"))))
+ ((symbol-function 'panicking-backend)
+ (lambda (report-fn)
+ (run-with-timer
+ 0.5 nil
+ report-fn :panic :explanation "The spanish inquisition!")))
+ ((symbol-function 'crashing-backend)
+ (lambda (_report-fn)
+ ;; HACK: Shoosh log during tests
+ (setq-local warning-minimum-log-level :emergency)
+ (error "crashed"))))
+ (insert "Lorem ipsum dolor sit amet, consectetur adipiscing
+ elit, sed do eiusmod tempor incididunt ut labore et dolore
+ manha aliqua. Ut enim ad minim veniam, quis nostrud
+ exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in
+ voluptate velit esse cillum dolore eu fugiat nulla
+ pariatur. Excepteur sint occaecat cupidatat non prognata
+ sunt in culpa qui officia deserunt mollit anim id est
+ laborum.")
+ (let ((flymake-diagnostic-functions
+ (list 'error-backend 'warning-backend 'sync-backend
+ 'panicking-backend
+ 'crashing-backend
+ ))
+ (flymake-wrap-around nil))
+ (let ((flymake-start-on-flymake-mode nil))
+ (flymake-mode))
+ (flymake-start)
+
+ (flymake-tests--assert-set (flymake-running-backends)
+ (error-backend warning-backend panicking-backend)
+ (crashing-backend))
+
+ (flymake-tests--assert-set (flymake-disabled-backends)
+ (crashing-backend)
+ (error-backend warning-backend sync-backend
+ panicking-backend))
+
+ (flymake-tests--wait-for-backends)
+
+ (flymake-tests--assert-set (flymake-disabled-backends)
+ (crashing-backend panicking-backend)
+ (error-backend warning-backend sync-backend))
+
+ (goto-char (point-min))
+ (flymake-goto-next-error)
+ (should (eq 'flymake-warning (face-at-point))) ; dolor
+ (flymake-goto-next-error)
+ (should (eq 'flymake-warning (face-at-point))) ; ut
+ (flymake-goto-next-error)
+ (should (eq 'flymake-error (face-at-point))) ; manha
+ (flymake-goto-next-error)
+ (should (eq 'flymake-warning (face-at-point))) ; Ut
+ (flymake-goto-next-error)
+ (should (eq 'flymake-note (face-at-point))) ; quis
+ (flymake-goto-next-error)
+ (should (eq 'flymake-warning (face-at-point))) ; ut
+ (flymake-goto-next-error)
+ (should (eq 'flymake-note (face-at-point))) ; commodo
+ (flymake-goto-next-error)
+ (should (eq 'flymake-warning (face-at-point))) ; dolor
+ (flymake-goto-next-error)
+ (should (eq 'flymake-error (face-at-point))) ; prognata
+ (should-error (flymake-goto-next-error nil nil t))))))
+
+(ert-deftest recurrent-backend ()
+ "Test a backend that calls REPORT-FN multiple times"
+ (with-temp-buffer
+ (let (tick)
+ (cl-letf
+ (((symbol-function 'eager-backend)
+ (lambda (report-fn)
+ (funcall report-fn nil :explanation "very eager but no diagnostics")
+ (display-buffer (current-buffer))
+ (run-with-timer
+ 0.5 nil
+ (lambda ()
+ (flymake-tests--diagnose-words report-fn :warning '("consectetur"))
+ (setq tick t)
+ (run-with-timer
+ 0.5 nil
+ (lambda ()
+ (flymake-tests--diagnose-words report-fn :error '("fugiat"))
+ (setq tick t))))))))
+ (insert "Lorem ipsum dolor sit amet, consectetur adipiscing
+ elit, sed do eiusmod tempor incididunt ut labore et dolore
+ manha aliqua. Ut enim ad minim veniam, quis nostrud
+ exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in
+ voluptate velit esse cillum dolore eu fugiat nulla
+ pariatur. Excepteur sint occaecat cupidatat non prognata
+ sunt in culpa qui officia deserunt mollit anim id est
+ laborum.")
+ (let ((flymake-diagnostic-functions
+ (list 'eager-backend))
+ (flymake-wrap-around nil))
+ (let ((flymake-start-on-flymake-mode nil))
+ (flymake-mode))
+ (flymake-start)
+ (flymake-tests--assert-set (flymake-running-backends)
+ (eager-backend) ())
+ (cl-loop until tick repeat 4 do (sleep-for 0.2))
+ (setq tick nil)
+ (goto-char (point-max))
+ (flymake-goto-prev-error)
+ (should (eq 'flymake-warning (face-at-point))) ; consectetur
+ (should-error (flymake-goto-prev-error nil nil t))
+ (cl-loop until tick repeat 4 do (sleep-for 0.2))
+ (flymake-goto-next-error)
+ (should (eq 'flymake-error (face-at-point))) ; fugiat
+ (flymake-goto-prev-error)
+ (should (eq 'flymake-warning (face-at-point))) ; back at consectetur
+ (should-error (flymake-goto-prev-error nil nil t))
+ )))))
+
+(ert-deftest eob-region-and-trailing-newline ()
+ "`flymake-diag-region' at eob with varying trailing newlines."
+ (cl-flet ((diag-region-substring
+ (line col)
+ (pcase-let
+ ((`(,a . ,b) (flymake-diag-region (current-buffer) line col)))
+ (buffer-substring a b))))
+ (with-temp-buffer
+ (insert "beg\nmmm\nend")
+ (should (equal
+ (diag-region-substring 3 3)
+ "d"))
+ (should (equal
+ (diag-region-substring 3 nil)
+ "end"))
+ (insert "\n")
+ (should (equal
+ (diag-region-substring 4 1)
+ "end"))
+ (should (equal
+ (diag-region-substring 4 nil)
+ "end"))
+ (insert "\n")
+ (should (equal
+ (diag-region-substring 5 1)
+ "\n"))
+ (should (equal
+ (diag-region-substring 5 nil)
+ "\n")))))
+
-(ert-deftest warning-predicate-function-perl ()
- "Test perl warning via function predicate."
- (skip-unless (executable-find "perl"))
- (should (eq 'flymake-warnline
- (flymake-tests--current-face
- "test.pl"
- (lambda (msg) (string-match "^Scalar value" msg))))))
(provide 'flymake-tests)