dotfiles/sbcl/.quicklisp/dists/quicklisp/software/slime-v2.24/contrib/test/slime-repl-tests.el

344 lines
12 KiB
EmacsLisp
Raw Normal View History

2020-01-20 14:13:08 -05:00
(require 'slime-repl)
(require 'slime-tests)
(require 'cl-lib)
(defmacro slime-repl-test-markers (expected-string-spec &rest marker-specs)
"For (MARKER SIG FORM) in MARKER-SPECS, produce suitable `should' assertions.
The assertions compare values in symbols `expected-MARKER' and
`observed-MARKER'. The former is obtained by searching EXPECTED-STRING-SPEC
for the string sig SIG, the latter by evaling FORM in the test buffer."
(declare (indent 1))
(cl-loop
for (marker signature observer-form) in marker-specs
for expected-sym = (make-symbol (format "expected-%s" marker))
for observed-sym = (make-symbol (format "observed-%s" marker))
collect `(,expected-sym
(progn (goto-char (point-min))
(when (search-forward ,signature nil t)
(replace-match "")
(point-marker))))
into expected-bindings
collect `(,observed-sym ,observer-form)
into observed-bindings
collect `(when (and ,observed-sym (not ,expected-sym))
(ert-fail
(format "Didn't expect to observe %s, but did and its %s"
',marker ,observed-sym)))
into assertions
collect `(when (and (not ,observed-sym) ,expected-sym)
(ert-fail
(format "Expected %s to be %s, bit didn't observe anything"
',marker ,expected-sym)))
into assertions
collect `(when (and ,observed-sym ,expected-sym)
(should (= ,observed-sym ,expected-sym)))
into assertions
finally
(return
`(progn
(let (,@observed-bindings
(observed-string (buffer-substring-no-properties (point-min)
(point-max))))
(with-current-buffer (get-buffer-create "*slime-repl test buffer*")
(erase-buffer)
(insert ,expected-string-spec)
(let (,@expected-bindings)
(should
(equal observed-string (buffer-string)))
,@assertions)))))))
(defun slime-check-buffer-contents (_msg expected-string-spec)
(slime-repl-test-markers expected-string-spec
(point "*" (point))
(output-start "{" (next-single-property-change
(point-min) 'slime-repl-output))
(output-end "}" (previous-single-property-change
(point-max) 'slime-repl-output))
(input-start "[" slime-repl-input-start-mark)
(point-max "]" (point-max))
(next-input-start "^" nil)))
(def-slime-test package-updating
(package-name nicknames)
"Test if slime-lisp-package is updated."
'(("COMMON-LISP" ("CL"))
("KEYWORD" ("" "KEYWORD" "||"))
("COMMON-LISP-USER" ("CL-USER")))
(with-current-buffer (slime-output-buffer)
(let ((p (slime-eval
`(swank-repl:listener-eval
,(format
"(cl:setq cl:*print-case* :upcase)
(cl:setq cl:*package* (cl:find-package %S))
(cl:package-name cl:*package*)" package-name))
(slime-lisp-package))))
(slime-check ("slime-lisp-package is %S." package-name)
(equal (slime-lisp-package) package-name))
(slime-check ("slime-lisp-package-prompt-string is in %S." nicknames)
(member (slime-lisp-package-prompt-string) nicknames)))))
(defmacro with-canonicalized-slime-repl-buffer (&rest body)
"Evaluate BODY within a fresh REPL buffer. The REPL prompt is
canonicalized to \"SWANK\"---we do actually switch to that
package, though."
(declare (debug (&rest form)) (indent 0))
`(let ((%old-prompt% (slime-lisp-package-prompt-string)))
(unwind-protect
(progn (with-current-buffer (slime-output-buffer)
(setf (slime-lisp-package-prompt-string) "SWANK"))
(kill-buffer (slime-output-buffer))
(with-current-buffer (slime-output-buffer)
,@body))
(setf (slime-lisp-package-prompt-string) %old-prompt%))))
(def-slime-test repl-test
(input result-contents)
"Test simple commands in the minibuffer."
'(("(+ 1 2)" "SWANK> (+ 1 2)
3
SWANK> *[]")
("(princ 10)" "SWANK> (princ 10)
{10
}10
SWANK> *[]")
("(princ 10)(princ 20)" "SWANK> (princ 10)(princ 20)
{1020
}20
SWANK> *[]")
("(dotimes (i 10 77) (princ i) (terpri))"
"SWANK> (dotimes (i 10 77) (princ i) (terpri))
{0
1
2
3
4
5
6
7
8
9
}77
SWANK> *[]")
("(abort)" "SWANK> (abort)
; Evaluation aborted on NIL.
SWANK> *[]")
("(progn (princ 10) (force-output) (abort))"
"SWANK> (progn (princ 10) (force-output) (abort))
{10}; Evaluation aborted on NIL.
SWANK> *[]")
("(progn (princ 10) (abort))"
;; output can be flushed after aborting
"SWANK> (progn (princ 10) (abort))
{10}; Evaluation aborted on NIL.
SWANK> *[]")
("(if (fresh-line) 1 0)"
"SWANK> (if (fresh-line) 1 0)
{
}1
SWANK> *[]")
("(values 1 2 3)" "SWANK> (values 1 2 3)
1
2
3
SWANK> *[]"))
(with-canonicalized-slime-repl-buffer
(insert input)
(slime-check-buffer-contents "Buffer contains input"
(concat "SWANK> [" input "*]"))
(call-interactively 'slime-repl-return)
(slime-sync-to-top-level 5)
(slime-check-buffer-contents "Buffer contains result" result-contents)))
(def-slime-test repl-test-2
(input result-contents)
"Test some more simple situations dealing with print-width and stuff"
'(("(with-standard-io-syntax
(write (make-list 15 :initial-element '(1 . 2)) :pretty t) 0)"
"SWANK> (with-standard-io-syntax
(write (make-list 15 :initial-element '(1 . 2)) :pretty t) 0)
{((1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2)
(1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2))
}0
SWANK> *[]")
;; Two times to test the effect of FRESH-LINE.
("(with-standard-io-syntax
(write (make-list 15 :initial-element '(1 . 2)) :pretty t) 0)"
"SWANK> (with-standard-io-syntax
(write (make-list 15 :initial-element '(1 . 2)) :pretty t) 0)
{((1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2)
(1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2) (1 . 2))
}0
SWANK> *[]"))
(slime-skip-test "Repl test is unstable without the slime-presentations contrib.")
(slime-test-repl-test input result-contents))
(def-slime-test repl-return
(before after result-contents)
"Test if slime-repl-return sends the correct protion to Lisp even
if point is not at the end of the line."
'(("(+ 1 2)" "" "SWANK> (+ 1 2)
3
SWANK> ")
("(+ 1 " "2)" "SWANK> (+ 1 2)
3
SWANK> ")
("(+ 1\n" "2)" "SWANK> (+ 1
2)
3
SWANK> "))
(with-canonicalized-slime-repl-buffer
(insert before)
(save-excursion (insert after))
(slime-test-expect "Buffer contains input"
(concat "SWANK> " before after)
(buffer-string))
(call-interactively 'slime-repl-return)
(slime-sync-to-top-level 5)
(slime-test-expect "Buffer contains result"
result-contents (buffer-string))))
(def-slime-test repl-read
(prompt input result-contents)
"Test simple commands in the minibuffer."
'(("(read-line)" "foo" "SWANK> (values (read-line))
foo
\"foo\"
SWANK> ")
("(read-char)" "1" "SWANK> (values (read-char))
1
#\\1
SWANK> ")
("(read)" "(+ 2 3
4)" "SWANK> (values (read))
\(+ 2 3
4)
\(+ 2 3 4)
SWANK> "))
(with-canonicalized-slime-repl-buffer
(insert (format "(values %s)" prompt))
(call-interactively 'slime-repl-return)
(slime-wait-condition "reading" #'slime-reading-p 5)
(insert input)
(call-interactively 'slime-repl-return)
(slime-sync-to-top-level 5)
(slime-test-expect "Buffer contains result"
result-contents (buffer-string))))
(def-slime-test repl-read-lines
(command inputs final-contents)
"Test reading multiple lines from the repl."
'(("(list (read-line) (read-line) (read-line))"
("a" "b" "c")
"SWANK> (list (read-line) (read-line) (read-line))
a
b
c
\(\"a\" \"b\" \"c\")
SWANK> "))
(with-canonicalized-slime-repl-buffer
(insert command)
(call-interactively 'slime-repl-return)
(dolist (input inputs)
(slime-wait-condition "reading" #'slime-reading-p 5)
(insert input)
(call-interactively 'slime-repl-return))
(slime-sync-to-top-level 5)
(slime-test-expect "Buffer contains result"
final-contents
(buffer-string)
#'equal)))
(def-slime-test repl-type-ahead
(command input final-contents)
"Ensure that user input is preserved correctly.
In particular, input inserted while waiting for a result."
'(("(sleep 0.1)" "foo*" "SWANK> (sleep 0.1)
NIL
SWANK> [foo*]")
("(sleep 0.1)" "*foo" "SWANK> (sleep 0.1)
NIL
SWANK> [*foo]")
("(progn (sleep 0.1) (abort))" "*foo" "SWANK> (progn (sleep 0.1) (abort))
; Evaluation aborted on NIL.
SWANK> [*foo]"))
(with-canonicalized-slime-repl-buffer
(insert command)
(call-interactively 'slime-repl-return)
(save-excursion (insert (cl-delete ?* input)))
(forward-char (cl-position ?* input))
(slime-sync-to-top-level 5)
(slime-check-buffer-contents "Buffer contains result" final-contents)))
(def-slime-test interrupt-in-blocking-read
()
"Let's see what happens if we interrupt a blocking read operation."
'(())
(slime-skip-test "TODO: skip for now, but analyse this failure!")
(slime-check-top-level)
(with-canonicalized-slime-repl-buffer
(insert "(read-char)")
(call-interactively 'slime-repl-return)
(slime-wait-condition "reading" #'slime-reading-p 5)
(slime-interrupt)
(slime-wait-condition "Debugger visible"
(lambda ()
(and (slime-sldb-level= 1)
(get-buffer-window
(sldb-get-default-buffer))))
5)
(with-current-buffer (sldb-get-default-buffer)
(sldb-continue))
(slime-wait-condition "reading" #'slime-reading-p 5)
(with-current-buffer (slime-output-buffer)
(insert "X")
(call-interactively 'slime-repl-return)
(slime-sync-to-top-level 5)
(slime-test-expect "Buffer contains result"
"SWANK> (read-char)
X
#\\X
SWANK> " (buffer-string)))))
(def-slime-test move-around-and-be-nasty
()
"Test moving around in repl, and watching attempts to destroy prompt fail"
'(())
(slime-skip-test "TODO: Test causes instability for other tests.")
(slime-check-top-level)
(with-canonicalized-slime-repl-buffer
(let ((start (point)))
(insert "foo")
(beginning-of-line)
(should (equal (buffer-substring-no-properties
(point-min)
(point-max)) "SWANK> foo"))
(should (equal (point) start))
(unwind-protect
(progn
(let ((inhibit-field-text-motion t))
(goto-char (line-beginning-position)))
(should-error (delete-char 1)))
(goto-char (line-end-position))))))
(def-slime-test mixed-output-and-results
(prompt eval-input result-contents)
"Test that output goes to the correct places."
'(("(princ 123)" (cl:loop repeat 2 do (cl:princ 456)) "SWANK> (princ 123)
123
123
456456
SWANK> "))
(with-canonicalized-slime-repl-buffer
(insert prompt)
(call-interactively 'slime-repl-return)
(slime-sync-to-top-level 5)
(slime-eval eval-input)
(slime-sync-to-top-level 5)
(slime-test-expect "Buffer contains result"
result-contents (buffer-string))))
(provide 'slime-repl-tests)