aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorJoão Távora2017-09-30 17:32:53 +0100
committerJoão Távora2017-10-03 14:18:55 +0100
commitf6e909b41e927a6715bad5fc5386257f29e7c0bb (patch)
treeefc4ed256ed6f6ff96326285cc1abd6348330343 /test
parent22a7372faba317a3589c49fef912e542f3197f0d (diff)
downloademacs-f6e909b41e927a6715bad5fc5386257f29e7c0bb.tar.gz
emacs-f6e909b41e927a6715bad5fc5386257f29e7c0bb.zip
Flymake backends can report multiple times per check
Rewrote a significant part of the Flymake backend API. Flymake now ignores the return value of backend functions: a function can either returns or errors. If it doesn't error, a backend is no longer constrained to call REPORT-FN exactly once. It may do so any number of times, cumulatively reporting diagnostics. Flymake keeps track of outdated REPORT-FN instances and disconsiders obsolete reports. Backends should avoid reporting obsolete data by cancelling any ongoing processing at every renewed call to the backend function. Consolidated flymake.el internal data structures to require less buffer-local variables. Adjusted Flymake's mode-line indicator to the new semantics. Adapted and simplified the implementation of elisp and legacy backends, fixing potential race conditions when calling backends in rapid succession. Added a new test for a backend that calls REPORT-FN multiple times. Simplify test infrastructure. * lisp/progmodes/flymake-elisp.el (flymake-elisp-checkdoc) (flymake-elisp-byte-compile): Error instead of returning nil if not in emacs-lisp-mode. (flymake-elisp--byte-compile-process): New buffer-local variable. (flymake-elisp-byte-compile): Mark (and kill) previous process obsolete process before starting a new one. Don't report if obsolete process. * lisp/progmodes/flymake-proc.el (flymake-proc--current-process): New buffer-local variable. (flymake-proc--processes): Remove. (flymake-proc--process-filter): Don't bind flymake-proc--report-fn. (flymake-proc--process-sentinel): Rewrite. Don't report if obsolete process. (flymake-proc-legacy-flymake): Rewrite. Mark (and kill) previous process obsolete process before starting a new one. Integrate flymake-proc--start-syntax-check-process helper. (flymake-proc--start-syntax-check-process): Delete. (flymake-proc-stop-all-syntax-checks): Don't use flymake-proc--processes, iterate buffers. (flymake-proc-compile): * lisp/progmodes/flymake.el (subr-x): Require it explicitly. (flymake-diagnostic-functions): Reword docstring. (flymake--running-backends, flymake--disabled-backends) (flymake--diagnostics-table): Delete. (flymake--backend-state): New buffer-local variable and new defstruct. (flymake--with-backend-state, flymake--collect) (flymake-running-backends, flymake-disabled-backends) (flymake-reporting-backends): New helpers. (flymake-is-running): Use flymake-running-backends. (flymake--handle-report): Rewrite. (flymake-make-report-fn): Ensure REPORT-FN runs in the correct buffer or not at all. (flymake--disable-backend, flymake--run-backend): Rewrite. (flymake-start): Rewrite. (flymake-mode): Set flymake--backend-state. (flymake--mode-line-format): Rewrite. * test/lisp/progmodes/flymake-tests.el (flymake-tests--wait-for-backends): New helper. (flymake-tests--call-with-fixture): Use it. (included-c-header-files): Fix whitespace. (flymake-tests--diagnose-words): New helper. (dummy-backends): Rewrite for new semantics. Use cl-letf. (flymake-tests--assert-set): Use quote. (recurrent-backend): New test.
Diffstat (limited to 'test')
-rw-r--r--test/lisp/progmodes/flymake-tests.el246
1 files changed, 147 insertions, 99 deletions
diff --git a/test/lisp/progmodes/flymake-tests.el b/test/lisp/progmodes/flymake-tests.el
index 222c8f11848..5e042f2b082 100644
--- a/test/lisp/progmodes/flymake-tests.el
+++ b/test/lisp/progmodes/flymake-tests.el
@@ -36,6 +36,26 @@
36 36
37;; 37;;
38;; 38;;
39(defun flymake-tests--wait-for-backends ()
40 ;; Weirdness here... http://debbugs.gnu.org/17647#25
41 ;; ... meaning `sleep-for', and even
42 ;; `accept-process-output', won't suffice as ways to get
43 ;; process filters and sentinels to run, though they do work
44 ;; fine in a non-interactive batch session. The only thing
45 ;; that will indeed unblock pending process output is
46 ;; reading an input event, so, as a workaround, use a dummy
47 ;; `read-event' with a very short timeout.
48 (unless noninteractive (read-event "" nil 0.1))
49 (cl-loop repeat 5
50 for notdone = (cl-set-difference (flymake-running-backends)
51 (flymake-reporting-backends))
52 while notdone
53 unless noninteractive do (read-event "" nil 0.1)
54 do (sleep-for (+ 0.5 flymake-no-changes-timeout))
55 finally (when notdone (ert-fail
56 (format "Some backends not reporting yet %s"
57 notdone)))))
58
39(cl-defun flymake-tests--call-with-fixture (fn file 59(cl-defun flymake-tests--call-with-fixture (fn file
40 &key (severity-predicate 60 &key (severity-predicate
41 nil sev-pred-supplied-p)) 61 nil sev-pred-supplied-p))
@@ -46,7 +66,6 @@ SEVERITY-PREDICATE is used to setup
46 (visiting (find-buffer-visiting file)) 66 (visiting (find-buffer-visiting file))
47 (buffer (or visiting (find-file-noselect file))) 67 (buffer (or visiting (find-file-noselect file)))
48 (process-environment (cons "LC_ALL=C" process-environment)) 68 (process-environment (cons "LC_ALL=C" process-environment))
49 (i 0)
50 (warning-minimum-log-level :error)) 69 (warning-minimum-log-level :error))
51 (unwind-protect 70 (unwind-protect
52 (with-current-buffer buffer 71 (with-current-buffer buffer
@@ -55,18 +74,7 @@ SEVERITY-PREDICATE is used to setup
55 (setq-local flymake-proc-diagnostic-type-pred severity-predicate)) 74 (setq-local flymake-proc-diagnostic-type-pred severity-predicate))
56 (goto-char (point-min)) 75 (goto-char (point-min))
57 (unless flymake-mode (flymake-mode 1)) 76 (unless flymake-mode (flymake-mode 1))
58 ;; Weirdness here... http://debbugs.gnu.org/17647#25 77 (flymake-tests--wait-for-backends)
59 ;; ... meaning `sleep-for', and even
60 ;; `accept-process-output', won't suffice as ways to get
61 ;; process filters and sentinels to run, though they do work
62 ;; fine in a non-interactive batch session. The only thing
63 ;; that will indeed unblock pending process output is
64 ;; reading an input event, so, as a workaround, use a dummy
65 ;; `read-event' with a very short timeout.
66 (unless noninteractive (read-event "" nil 0.1))
67 (while (and (flymake-is-running) (< (setq i (1+ i)) 10))
68 (unless noninteractive (read-event "" nil 0.1))
69 (sleep-for (+ 0.5 flymake-no-changes-timeout)))
70 (funcall fn))) 78 (funcall fn)))
71 (and buffer 79 (and buffer
72 (not visiting) 80 (not visiting)
@@ -119,38 +127,37 @@ SEVERITY-PREDICATE is used to setup
119(ert-deftest different-diagnostic-types () 127(ert-deftest different-diagnostic-types ()
120 "Test GCC warning via function predicate." 128 "Test GCC warning via function predicate."
121 (skip-unless (and (executable-find "gcc") (executable-find "make"))) 129 (skip-unless (and (executable-find "gcc") (executable-find "make")))
122 (flymake-tests--with-flymake 130 (let ((flymake-wrap-around nil))
123 ("errors-and-warnings.c") 131 (flymake-tests--with-flymake
124 (flymake-goto-next-error) 132 ("errors-and-warnings.c")
125 (should (eq 'flymake-error (face-at-point))) 133 (flymake-goto-next-error)
126 (flymake-goto-next-error) 134 (should (eq 'flymake-error (face-at-point)))
127 (should (eq 'flymake-note (face-at-point))) 135 (flymake-goto-next-error)
128 (flymake-goto-next-error) 136 (should (eq 'flymake-note (face-at-point)))
129 (should (eq 'flymake-warning (face-at-point))) 137 (flymake-goto-next-error)
130 (flymake-goto-next-error) 138 (should (eq 'flymake-warning (face-at-point)))
131 (should (eq 'flymake-error (face-at-point))) 139 (flymake-goto-next-error)
132 (flymake-goto-next-error) 140 (should (eq 'flymake-error (face-at-point)))
133 (should (eq 'flymake-warning (face-at-point))) 141 (flymake-goto-next-error)
134 (flymake-goto-next-error) 142 (should (eq 'flymake-warning (face-at-point)))
135 (should (eq 'flymake-warning (face-at-point))) 143 (flymake-goto-next-error)
136 (let ((flymake-wrap-around nil)) 144 (should (eq 'flymake-warning (face-at-point)))
137 (should-error (flymake-goto-next-error nil nil t))) )) 145 (should-error (flymake-goto-next-error nil nil t)))))
138 146
139(ert-deftest included-c-header-files () 147(ert-deftest included-c-header-files ()
140 "Test inclusion of .h header files." 148 "Test inclusion of .h header files."
141 (skip-unless (and (executable-find "gcc") (executable-find "make"))) 149 (skip-unless (and (executable-find "gcc") (executable-find "make")))
142 (flymake-tests--with-flymake 150 (let ((flymake-wrap-around nil))
143 ("some-problems.h") 151 (flymake-tests--with-flymake
144 (flymake-goto-next-error) 152 ("some-problems.h")
145 (should (eq 'flymake-warning (face-at-point))) 153 (flymake-goto-next-error)
146 (flymake-goto-next-error) 154 (should (eq 'flymake-warning (face-at-point)))
147 (should (eq 'flymake-error (face-at-point))) 155 (flymake-goto-next-error)
148 (let ((flymake-wrap-around nil)) 156 (should (eq 'flymake-error (face-at-point)))
149 (should-error (flymake-goto-next-error nil nil t))) ) 157 (should-error (flymake-goto-next-error nil nil t)))
150 (flymake-tests--with-flymake 158 (flymake-tests--with-flymake
151 ("no-problems.h") 159 ("no-problems.h")
152 (let ((flymake-wrap-around nil)) 160 (should-error (flymake-goto-next-error nil nil t)))))
153 (should-error (flymake-goto-next-error nil nil t))) ))
154 161
155(defmacro flymake-tests--assert-set (set 162(defmacro flymake-tests--assert-set (set
156 should 163 should
@@ -159,19 +166,15 @@ SEVERITY-PREDICATE is used to setup
159 `(progn 166 `(progn
160 ,@(cl-loop 167 ,@(cl-loop
161 for s in should 168 for s in should
162 collect `(should (memq ,s ,set))) 169 collect `(should (memq (quote ,s) ,set)))
163 ,@(cl-loop 170 ,@(cl-loop
164 for s in should-not 171 for s in should-not
165 collect `(should-not (memq ,s ,set))))) 172 collect `(should-not (memq (quote ,s) ,set)))))
166 173
167(ert-deftest dummy-backends () 174(defun flymake-tests--diagnose-words
168 "Test GCC warning via function predicate." 175 (report-fn type words)
169 (with-temp-buffer 176 "Helper. Call REPORT-FN with diagnostics for WORDS in buffer."
170 (cl-labels 177 (funcall report-fn
171 ((diagnose
172 (report-fn type words)
173 (funcall
174 report-fn
175 (cl-loop 178 (cl-loop
176 for word in words 179 for word in words
177 append 180 append
@@ -184,32 +187,34 @@ SEVERITY-PREDICATE is used to setup
184 (match-end 0) 187 (match-end 0)
185 type 188 type
186 (concat word " is wrong"))))))) 189 (concat word " is wrong")))))))
187 (error-backend 190
188 (report-fn) 191(ert-deftest dummy-backends ()
189 (run-with-timer 192 "Test many different kinds of backends."
190 0.5 nil 193 (with-temp-buffer
191 #'diagnose report-fn :error '("manha" "prognata"))) 194 (cl-letf
192 (warning-backend 195 (((symbol-function 'error-backend)
193 (report-fn) 196 (lambda (report-fn)
194 (run-with-timer 197 (run-with-timer
195 0.5 nil 198 0.5 nil
196 #'diagnose report-fn :warning '("ut" "dolor"))) 199 #'flymake-tests--diagnose-words report-fn :error '("manha" "prognata"))))
197 (sync-backend 200 ((symbol-function 'warning-backend)
198 (report-fn) 201 (lambda (report-fn)
199 (diagnose report-fn :note '("quis" "commodo"))) 202 (run-with-timer
200 (refusing-backend 203 0.5 nil
201 (_report-fn) 204 #'flymake-tests--diagnose-words report-fn :warning '("ut" "dolor"))))
202 nil) 205 ((symbol-function 'sync-backend)
203 (panicking-backend 206 (lambda (report-fn)
204 (report-fn) 207 (flymake-tests--diagnose-words report-fn :note '("quis" "commodo"))))
205 (run-with-timer 208 ((symbol-function 'panicking-backend)
206 0.5 nil 209 (lambda (report-fn)
207 report-fn :panic :explanation "The spanish inquisition!")) 210 (run-with-timer
208 (crashing-backend 211 0.5 nil
209 (_report-fn) 212 report-fn :panic :explanation "The spanish inquisition!")))
210 ;; HACK: Shoosh log during tests 213 ((symbol-function 'crashing-backend)
211 (setq-local warning-minimum-log-level :emergency) 214 (lambda (_report-fn)
212 (error "crashed"))) 215 ;; HACK: Shoosh log during tests
216 (setq-local warning-minimum-log-level :emergency)
217 (error "crashed"))))
213 (insert "Lorem ipsum dolor sit amet, consectetur adipiscing 218 (insert "Lorem ipsum dolor sit amet, consectetur adipiscing
214 elit, sed do eiusmod tempor incididunt ut labore et dolore 219 elit, sed do eiusmod tempor incididunt ut labore et dolore
215 manha aliqua. Ut enim ad minim veniam, quis nostrud 220 manha aliqua. Ut enim ad minim veniam, quis nostrud
@@ -220,31 +225,27 @@ SEVERITY-PREDICATE is used to setup
220 sunt in culpa qui officia deserunt mollit anim id est 225 sunt in culpa qui officia deserunt mollit anim id est
221 laborum.") 226 laborum.")
222 (let ((flymake-diagnostic-functions 227 (let ((flymake-diagnostic-functions
223 (list #'error-backend #'warning-backend #'sync-backend 228 (list 'error-backend 'warning-backend 'sync-backend
224 #'refusing-backend #'panicking-backend 229 'panicking-backend
225 #'crashing-backend 230 'crashing-backend
226 ))) 231 ))
232 (flymake-wrap-around nil))
227 (flymake-mode) 233 (flymake-mode)
228 ;; FIXME: accessing some flymake-ui's internals here...
229 (flymake-tests--assert-set flymake--running-backends
230 (#'error-backend #'warning-backend #'panicking-backend)
231 (#'sync-backend #'crashing-backend #'refusing-backend))
232 234
233 (flymake-tests--assert-set flymake--disabled-backends 235 (flymake-tests--assert-set (flymake-running-backends)
234 (#'crashing-backend) 236 (error-backend warning-backend panicking-backend)
235 (#'error-backend #'warning-backend #'sync-backend 237 (crashing-backend))
236 #'panicking-backend #'refusing-backend))
237 238
238 (cl-loop repeat 10 while (flymake-is-running) 239 (flymake-tests--assert-set (flymake-disabled-backends)
239 unless noninteractive do (read-event "" nil 0.1) 240 (crashing-backend)
240 do (sleep-for (+ 0.5 flymake-no-changes-timeout))) 241 (error-backend warning-backend sync-backend
242 panicking-backend))
241 243
242 (should (eq flymake--running-backends '())) 244 (flymake-tests--wait-for-backends)
243 245
244 (flymake-tests--assert-set flymake--disabled-backends 246 (flymake-tests--assert-set (flymake-disabled-backends)
245 (#'crashing-backend #'panicking-backend) 247 (crashing-backend panicking-backend)
246 (#'error-backend #'warning-backend #'sync-backend 248 (error-backend warning-backend sync-backend))
247 #'refusing-backend))
248 249
249 (goto-char (point-min)) 250 (goto-char (point-min))
250 (flymake-goto-next-error) 251 (flymake-goto-next-error)
@@ -265,8 +266,55 @@ SEVERITY-PREDICATE is used to setup
265 (should (eq 'flymake-warning (face-at-point))) ; dolor 266 (should (eq 'flymake-warning (face-at-point))) ; dolor
266 (flymake-goto-next-error) 267 (flymake-goto-next-error)
267 (should (eq 'flymake-error (face-at-point))) ; prognata 268 (should (eq 'flymake-error (face-at-point))) ; prognata
268 (let ((flymake-wrap-around nil)) 269 (should-error (flymake-goto-next-error nil nil t))))))
269 (should-error (flymake-goto-next-error nil nil t))))))) 270
271(ert-deftest recurrent-backend ()
272 "Test a backend that calls REPORT-FN multiple times"
273 (with-temp-buffer
274 (let (tick)
275 (cl-letf
276 (((symbol-function 'eager-backend)
277 (lambda (report-fn)
278 (funcall report-fn nil :explanation "very eager but no diagnostics")
279 (display-buffer (current-buffer))
280 (run-with-timer
281 0.5 nil
282 (lambda ()
283 (flymake-tests--diagnose-words report-fn :warning '("consectetur"))
284 (setq tick t)
285 (run-with-timer
286 0.5 nil
287 (lambda ()
288 (flymake-tests--diagnose-words report-fn :error '("fugiat"))
289 (setq tick t))))))))
290 (insert "Lorem ipsum dolor sit amet, consectetur adipiscing
291 elit, sed do eiusmod tempor incididunt ut labore et dolore
292 manha aliqua. Ut enim ad minim veniam, quis nostrud
293 exercitation ullamco laboris nisi ut aliquip ex ea commodo
294 consequat. Duis aute irure dolor in reprehenderit in
295 voluptate velit esse cillum dolore eu fugiat nulla
296 pariatur. Excepteur sint occaecat cupidatat non prognata
297 sunt in culpa qui officia deserunt mollit anim id est
298 laborum.")
299 (let ((flymake-diagnostic-functions
300 (list 'eager-backend))
301 (flymake-wrap-around nil))
302 (flymake-mode)
303 (flymake-tests--assert-set (flymake-running-backends)
304 (eager-backend) ())
305 (cl-loop until tick repeat 4 do (sleep-for 0.2))
306 (setq tick nil)
307 (goto-char (point-max))
308 (flymake-goto-prev-error)
309 (should (eq 'flymake-warning (face-at-point))) ; consectetur
310 (should-error (flymake-goto-prev-error nil nil t))
311 (cl-loop until tick repeat 4 do (sleep-for 0.2))
312 (flymake-goto-next-error)
313 (should (eq 'flymake-error (face-at-point))) ; fugiat
314 (flymake-goto-prev-error)
315 (should (eq 'flymake-warning (face-at-point))) ; back at consectetur
316 (should-error (flymake-goto-prev-error nil nil t))
317 )))))
270 318
271(provide 'flymake-tests) 319(provide 'flymake-tests)
272 320