1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
|
;;; coq-tests.el --- integration tests -*- lexical-binding: t; -*-
;;; Commentary:
;;
;;; Eval this to run the tests interactively <C-x C-e>
;;
;; (progn (load-file "coq-tests.el") (call-interactively #'ert))
(unless (and (boundp 'coq-test-dir) coq-test-dir) ; if set by ./test.sh
(if buffer-file-name
(setq coq-test-dir (file-name-directory buffer-file-name))
(error "You should set 'coq-test-dir, or run coq-test.el from a file buffer.")))
(setq debug-on-error t) ; open the debugger on error -- may be commented-out
(setq ert-batch-backtrace-right-margin 79)
(require 'subr-x) ;; for (string-trim)
;;(require 'ert-async)
;;(setq ert-async-timeout 2)
;; Load Coq instance of Proof General now.
(proof-ready-for-assistant 'coq)
(require 'coq)
;;; Code:
; Exemple de code Lisp qui lance des commandes au prouveur en arrière-plan
; (defun has-error ()
; "True if error in Proof"
; (eq 'error proof-shell-last-output-kind))
;
; (defun test-process-invisible-split ()
; (proof-shell-invisible-command "split."
; 'waitforit
; #'proof-done-invisible
; 'no-error-display 'no-response-display 'no-goals-display))
;
; (defun test-process-invisible-tactics-then-reset-and-insert ()
; (interactive)
; (let ((reset-cmd ; store backtracking info before proof search
; (format "Backtrack %s %s %s . "
; (int-to-string coq-last-but-one-statenum)
; (int-to-string coq-last-but-one-proofnum)
; 0)))
; ;; Toy example of proof search
; (while (not (has-error))
; (message "Trying split.")
; (test-process-invisible-split))
; ;; Reset to the previous state
; (proof-shell-invisible-command reset-cmd 'waitforit #'proof-done-invisible)
; ;; Insert (and re-process) the found script
; (proof-insert-sendback-command "split.\nsplit.\nsplit.\nQed.")))
;
; (test-process-invisible-tactics-then-reset-and-insert)
(defun coq-test-full-path (basename)
"Return the absolute path of BASENAME (a filename such as ./foo.v)."
(concat coq-test-dir basename))
(defconst coq-test-file-prefix "coq_test_")
(defun coq-test-init ()
"Ensure `coq' is loaded."
(unless (featurep 'coq)
(add-to-list 'load-path
(locate-dominating-file coq-test-dir "proof-general.el"))
(load "proof-general")
(proofgeneral "coq")))
(defun coq-test-exit ()
"Exit the Coq process."
(proof-shell-exit t))
; (coq-test-on-file nil (message (buffer-file-name)) (message "OK") 42)
;; DEFINITION OF MOCKS, SEE `coq-mock' BELOW
;; Another solution would consist in using el-mock, mentioned in:
;; https://www.gnu.org/software/emacs/manual/html_mono/ert.html#Mocks-and-Stubs
(defun coq-mock-proof-display-three-b (&rest rest)
(message (concat "Skipping proof-display-three-b on input: "
(pp-to-string rest)))
; Result:
nil)
;; AVOID THE FOLLOWING ERROR:
;; Hit M-x proof-layout-windows to reset layout
;; Debugger entered--Lisp error: (error "Window #<window 6 on *goals*> too small for splitting")
;; signal(error ("Window #<window 6 on *goals*> too small for splitting"))
;; error("Window %s too small for splitting" #<window 6 on *goals*>)
;; split-window(nil nil)
;; split-window-vertically()
;; proof-safe-split-window-vertically()
;; proof-select-three-b(nil #<buffer *goals*> #<buffer *response*> smart)
;; proof-display-three-b(smart)
;; proof-layout-windows()
;; proof-multiple-frames-enable()
;; proof-shell-start()
;; proof-shell-ready-prover()
(defun coq-mock (f)
(require 'pg-response) ; load the feature defining proof-display-three-b first
(cl-letf (;((symbol-function 'foo) #'mock-foo)
((symbol-function 'proof-display-three-b) #'coq-mock-proof-display-three-b))
(funcall f)))
;; Run <C-x C-e> on:
;; (coq-mock #'main)
(defun coq-test-cmd (cmd)
;;(coq-test-on-file)
;;(coq-test-init)
(proof-shell-invisible-command
cmd
'waitforit
#'proof-done-invisible
'no-error-display 'no-response-display 'no-goals-display))
(defun coq-set-flags (val flags)
(when (member 'show-proof-stepwise flags) (setq coq-show-proof-stepwise val))
(when (member 'diffs-on flags) (if val (setq coq-diffs 'on) (setq coq-diffs 'off))))
(defun coq-fixture-on-file (file body &rest flags)
"Fixture to setup the test env: open FILE if non-nil, or a temp file
then evaluate the BODY function and finally tear-down (exit Coq)."
;; AVOID THE FOLLOWING ERROR:
;; Starting: -emacs
;; Debugger entered--Lisp error: (wrong-type-argument stringp nil)
;; file-name-directory(nil)
;; scomint-exec-1("coq" #<buffer *coq*> nil ("-emacs"))
;; scomint-exec(#<buffer *coq*> "coq" nil nil ("-emacs"))
;; scomint-make-in-buffer("coq" nil nil nil "-emacs")
;; apply(scomint-make-in-buffer "coq" nil nil nil "-emacs")
;; scomint-make("coq" nil nil "-emacs")
;; apply(scomint-make ("coq" nil nil "-emacs"))
;; proof-shell-start()
;; proof-shell-ready-prover()
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; For info on macros: https://mullikine.github.io/posts/macro-tutorial
;;; (pp (macroexpand '(macro args)))
(save-excursion
(let* (;; avoids bad width detection in batch mode
(coq-auto-adapt-printing-width nil)
(openfile (or file
(concat (make-temp-file coq-test-file-prefix) ".v")))
;; if FILE is nil, create a temporary Coq file, removed in the end
(rmfile (unless file openfile))
(buffer (find-file openfile)))
(message "Opening file %s ..." openfile)
(unwind-protect
(progn
(coq-test-init)
(with-current-buffer buffer
(setq proof-splash-enable nil)
(normal-mode) ;; or (coq-mode)
(coq-set-flags t flags)
(coq-mock body))))
(coq-test-exit)
(coq-set-flags nil flags)
(not-modified nil) ; Clear modification
(kill-buffer buffer)
(when rmfile (message "Removing file %s ..." rmfile))
(ignore-errors (delete-file rmfile)))))
(defun coq-test-goto-before (comment)
"Go just before COMMENT (a unique string in the .v file).
For example, COMMENT could be (*test-definition*)"
(goto-char (point-max))
(search-backward comment))
(defun coq-test-goto-after (comment)
"Go just before COMMENT (a unique string in the .v file)."
(goto-char (point-min))
(search-forward comment))
(defun coq-should-buffer-regexp (regexp &optional buffer-name)
"Check REGEXP matches in BUFFER-NAME (*response* if nil)."
(should (string-match-p
regexp
(string-trim
(with-current-buffer (or buffer-name "*response*")
(buffer-substring-no-properties (point-min) (point-max)))))))
(defun coq-should-buffer-string (str &optional buffer-name)
"Particular case of `coq-should-buffer-regexp'."
(coq-should-buffer-regexp (regexp-quote str) buffer-name))
;; TODO: Use https://github.com/rejeep/ert-async.el
;; and/or ERT https://www.gnu.org/software/emacs/manual/html_node/ert/index.html
(ert-deftest 010_coq-test-running ()
"Test that the coqtop process is started properly."
(coq-fixture-on-file nil
(lambda ()
(coq-test-cmd "Print 0.")
;; (should (process-list)) ; wouldn't be a strong enough assert.
(should (get-process "coq")))))
(ert-deftest 020_coq-test-definition ()
"Test *response* output after asserting a Definition."
(coq-fixture-on-file
(coq-test-full-path "test_stepwise.v")
(lambda ()
(coq-test-goto-before "(*test-definition*)")
(proof-goto-point)
(proof-shell-wait)
(coq-should-buffer-string "trois is defined"))))
(ert-deftest 021_coq-test-regression-goto-point ()
"Regression test for proof-goto-point after a comment, PR #490"
(coq-fixture-on-file
(coq-test-full-path "test_stepwise.v")
(lambda ()
(coq-test-goto-after "(*test-definition*)")
(proof-goto-point)
(proof-shell-wait)
t)))
(ert-deftest 030_coq-test-position ()
"Test locked region after Qed."
(coq-fixture-on-file
(coq-test-full-path "test_stepwise.v")
(lambda ()
(coq-test-goto-before " (*test-lemma*)")
(let ((proof-point (point)))
(proof-goto-point)
(proof-shell-wait)
(should (equal (proof-queue-or-locked-end) proof-point))))))
(ert-deftest 040_coq-test-insert ()
"Test retract on insert from Qed."
(coq-fixture-on-file
(coq-test-full-path "test_stepwise.v")
(lambda ()
(coq-test-goto-before " (*test-lemma*)")
(proof-goto-point)
(proof-shell-wait)
(let ((proof-point (point)))
(coq-test-goto-before "(*test-insert*)")
(move-beginning-of-line nil)
(insert "\n")
;; The locked end point should go up compared to before
(should (< (proof-queue-or-locked-end) proof-point))))))
(ert-deftest 050_coq-test-lemma-false ()
"Test retract on proof error."
(coq-fixture-on-file
(coq-test-full-path "test_stepwise.v")
(lambda ()
(coq-test-goto-before " (*test-lemma2*)")
(let ((proof-point (save-excursion (coq-test-goto-after "(*error*)"))))
(proof-goto-point)
(proof-shell-wait)
(coq-should-buffer-string "Error: Unable to unify \"false\" with \"true\".")
(should (equal (proof-queue-or-locked-end) proof-point))))))
(ert-deftest 060_coq-test-wholefile ()
"Test `proof-process-buffer'."
(coq-fixture-on-file
(coq-test-full-path "test_wholefile.v")
(lambda ()
(let ((proof-point (save-excursion
(coq-test-goto-before "Theorem")
(search-forward "Qed."))))
(proof-process-buffer)
(proof-shell-wait)
(should (equal (proof-queue-or-locked-end) proof-point))))))
(ert-deftest 070_coq-test-regression-wholefile-no-proof ()
"Regression test for no proof bug"
(coq-fixture-on-file
(coq-test-full-path "test_wholefile.v")
(lambda ()
(proof-process-buffer)
(proof-shell-wait)
(goto-char (point-min))
(insert "(*.*)")
(should (equal (proof-queue-or-locked-end) (point-min))))))
(ert-deftest 080_coq-test-regression-show-proof-stepwise()
"Regression test for the \"Show Proof\" option"
(coq-fixture-on-file
(coq-test-full-path "test_stepwise.v")
(lambda ()
(coq-test-goto-before " (*test-insert*)")
(proof-goto-point)
(proof-shell-wait)
(coq-should-buffer-string "(fun (A : Prop) (proof_of_A : A) => ?Goal)"))
'show-proof-stepwise))
(ert-deftest 081_coq-test-regression-show-proof-diffs()
"Test for Show Proof Diffs"
(coq-fixture-on-file
(coq-test-full-path "test_stepwise.v")
(lambda ()
(coq-test-goto-before " (*test-insert*)")
(proof-goto-point)
(proof-shell-wait)
;; If coq--post-v811, it should be "Show Proof Diffs." otherwise "Show Proof."
(if (coq--post-v811)
(coq-should-buffer-string "<diff.added.bg>(fun <diff.added>(</diff.added>A : Prop<diff.added>) (proof_of_A : A)</diff.added> => ?Goal)</diff.added.bg>" "*coq*")
(coq-should-buffer-string "(fun (A : Prop) (proof_of_A : A) => ?Goal)")))
'show-proof-stepwise 'diffs-on))
(ert-deftest 090_coq-test-regression-Fail()
"Test for Fail"
(coq-fixture-on-file
(coq-test-full-path "test_stepwise.v")
(lambda ()
(coq-test-goto-before "(*FailNoTrace*)")
(proof-goto-point)
(proof-shell-wait)
(proof-assert-next-command-interactive) ;; pas the comment
(proof-assert-next-command-interactive)
(proof-shell-wait)
(if (coq--version< (coq-version) "8.10.0")
(coq-should-buffer-string "The command has indeed failed with message:
In nested Ltac calls to \"now (tactic)\" and \"easy\", last call failed.
Tactic failure: Cannot solve this goal.")
(coq-should-buffer-string "The command has indeed failed with message:
Tactic failure: Cannot solve this goal." "*coq*")))))
;; (coq-should-buffer-regexp (regexp-quote "The command has indeed failed with message: Tactic failure: Cannot solve this goal.") "*response*")
(ert-deftest 091_coq-test-regression-Fail()
"Test for Fail"
(coq-fixture-on-file
(coq-test-full-path "test_stepwise.v")
(lambda ()
(coq-test-goto-before "(*FailTrace*)")
(proof-goto-point)
(proof-shell-wait)
(proof-assert-next-command-interactive) ;; pas the comment
(proof-assert-next-command-interactive)
(proof-shell-wait)
;; If coq--post-v811, it should be "Show Proof Diffs." otherwise "Show Proof."
(coq-should-buffer-string "The command has indeed failed with message:
In nested Ltac calls to \"now (tactic)\" and \"easy\", last call failed.
Tactic failure: Cannot solve this goal."))))
(ert-deftest 100_coq-test-proof-using-proof ()
"Test for insertion of Proof using annotations"
(describe-function 'should)
(coq-fixture-on-file
(coq-test-full-path "test_proof_using.v")
(lambda ()
(coq-test-goto-after "(*qed*)")
(proof-goto-point)
(proof-shell-wait)
(proof-assert-next-command-interactive)
(proof-shell-wait)
;; If coq--post-v811, it should be "Show Proof Diffs." otherwise "Show Proof."
;(coq-should-buffer-string "\"Proof using\" not set. M-x coq-insert-suggested-dependency or right click to add it. See also \‘coq-accept-proof-using-suggestion\’.")
(save-excursion
(coq-test-goto-before "(*proof*)")
(backward-char 3)
(should (span-at (point) 'proofusing))))))
(provide 'coq-tests)
;;; coq-tests.el ends here
|