diff options
| author | João Távora | 2017-09-30 17:32:53 +0100 |
|---|---|---|
| committer | João Távora | 2017-10-03 14:18:55 +0100 |
| commit | f6e909b41e927a6715bad5fc5386257f29e7c0bb (patch) | |
| tree | efc4ed256ed6f6ff96326285cc1abd6348330343 /test | |
| parent | 22a7372faba317a3589c49fef912e542f3197f0d (diff) | |
| download | emacs-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.el | 246 |
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 | ||