sly-tests.el (59557B)
1 ;;; sly-tests.el --- Automated tests for sly.el -*- lexical-binding: t; -*- 2 ;; 3 ;;;; License 4 ;; Copyright (C) 2003 Eric Marsden, Luke Gorrie, Helmut Eller 5 ;; Copyright (C) 2004,2005,2006 Luke Gorrie, Helmut Eller 6 ;; Copyright (C) 2007,2008,2009 Helmut Eller, Tobias C. Rittweiler 7 ;; Copyright (C) 2013 8 ;; 9 ;; For a detailed list of contributors, see the manual. 10 ;; 11 ;; This program is free software; you can redistribute it and/or 12 ;; modify it under the terms of the GNU General Public License as 13 ;; published by the Free Software Foundation; either version 2 of 14 ;; the License, or (at your option) any later version. 15 ;; 16 ;; This program is distributed in the hope that it will be useful, 17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 ;; GNU General Public License for more details. 20 ;; 21 ;; You should have received a copy of the GNU General Public 22 ;; License along with this program; if not, write to the Free 23 ;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, 24 ;; MA 02111-1307, USA. 25 26 27 ;;;; Tests 28 (require 'sly) 29 (require 'ert nil t) 30 (require 'ert "lib/ert" t) ;; look for bundled version for Emacs 23 31 (require 'cl-lib) 32 (require 'bytecomp) ; byte-compile-current-file 33 34 (defun sly-shuffle-list (list) 35 (let* ((len (length list)) 36 (taken (make-vector len nil)) 37 (result (make-vector len nil))) 38 (dolist (e list) 39 (while (let ((i (random len))) 40 (cond ((aref taken i)) 41 (t (aset taken i t) 42 (aset result i e) 43 nil))))) 44 (append result '()))) 45 46 (defun sly-batch-test (&optional test-name randomize) 47 "Run the test suite in batch-mode. 48 Exits Emacs when finished. The exit code is the number of failed tests." 49 (interactive) 50 (let ((ert-debug-on-error nil) 51 (timeout 30)) 52 (sly) 53 ;; Block until we are up and running. 54 (let (timed-out) 55 (run-with-timer timeout nil 56 (lambda () (setq timed-out t))) 57 (while (not (sly-connected-p)) 58 (sit-for 1) 59 (when timed-out 60 (when noninteractive 61 (kill-emacs 252))))) 62 (sly-sync-to-top-level 5) 63 (let* ((selector (if randomize 64 `(member ,@(sly-shuffle-list 65 (ert-select-tests (or test-name t) t))) 66 (or test-name t))) 67 (ert-fun (if noninteractive 68 'ert-run-tests-batch 69 'ert))) 70 (let ((stats (funcall ert-fun selector))) 71 (if noninteractive 72 (kill-emacs (ert-stats-completed-unexpected stats))))))) 73 74 (defun sly-skip-test (message) 75 ;; ERT for Emacs 23 and earlier doesn't have `ert-skip' 76 (if (fboundp 'ert-skip) 77 (ert-skip message) 78 (message (concat "SKIPPING: " message)) 79 (ert-pass))) 80 81 (defun sly-tests--undefine-all () 82 (dolist (test (ert-select-tests t t)) 83 (let ((sym (ert-test-name test))) 84 (cl-assert (eq (get sym 'ert--test) test)) 85 (cl-remprop sym 'ert--test)))) 86 87 (sly-tests--undefine-all) 88 89 (eval-and-compile 90 (defun sly-tests-auto-tags () 91 (append '(sly) 92 (let ((file-name (or load-file-name 93 byte-compile-current-file))) 94 (if (and (stringp file-name) 95 (string-match "test/sly-\\(.*\\)\.elc?$" file-name)) 96 (list 'contrib (intern (match-string 1 file-name))) 97 '(core))))) 98 99 (defmacro define-sly-ert-test (name &rest args) 100 "Like `ert-deftest', but set tags automatically. 101 Also don't error if `ert.el' is missing." 102 (declare (debug (&define name sexp sexp &rest def-form))) 103 (let* ((docstring (and (stringp (cl-second args)) 104 (cl-second args))) 105 (args (if docstring 106 (cddr args) 107 (cdr args))) 108 (tags (sly-tests-auto-tags))) 109 `(ert-deftest ,name () ,(or docstring "No docstring for this test.") 110 :tags ',tags 111 ,@args))) 112 113 (defun sly-test-ert-test-for (name input i doc _body fails-for style fname) 114 `(define-sly-ert-test 115 ,(intern (format "%s-%d" name i)) () 116 ,(format "For input %s, %s" (truncate-string-to-width 117 (format "%s" input) 118 15 nil nil 'ellipsis) 119 (replace-regexp-in-string "^.??\\(\\w+\\)" 120 (lambda (s) (downcase s)) 121 doc 122 t)) 123 ,@(if fails-for 124 `(:expected-result 125 '(satisfies 126 (lambda (result) 127 (ert-test-result-type-p 128 result 129 (if (cl-find-if 130 (lambda (impl) 131 (unless (listp impl) 132 (setq impl (list impl #'(lambda (&rest _ign) t)))) 133 (and (equal (car impl) (sly-lisp-implementation-name)) 134 (funcall 135 (cadr impl) 136 ;; Appease `version-to-list' for 137 ;; SBCL. `version-regexp-alist' 138 ;; doesn't work here. 139 (replace-regexp-in-string 140 "[-._+ ]?[[:alnum:]]\\{7,9\\}$" 141 "-snapshot" 142 (sly-lisp-implementation-version)) 143 (caddr impl)))) 144 ',fails-for) 145 :failed 146 :passed)))))) 147 148 ,@(when style 149 `((let ((style (sly-communication-style))) 150 (when (not (member style ',style)) 151 (sly-skip-test (format "test not applicable for style %s" 152 style)))))) 153 (apply #',fname ',input)))) 154 155 (defmacro def-sly-test (name args doc inputs &rest body) 156 "Define a test case. 157 NAME ::= SYMBOL | (SYMBOL OPTION*) is a symbol naming the test. 158 OPTION ::= (:fails-for IMPLEMENTATION*) | (:style COMMUNICATION-STYLE*) 159 ARGS is a lambda-list. 160 DOC is a docstring. 161 INPUTS is a list of argument lists, each tested separately. 162 BODY is the test case. The body can use `sly-check' to test 163 conditions (assertions)." 164 (declare (debug (&define name sexp sexp sexp &rest def-form)) 165 (indent 4)) 166 (if (not (featurep 'ert)) 167 (warn "No ert.el found: not defining test %s" 168 name) 169 `(progn 170 ,@(cl-destructuring-bind (name &rest options) 171 (if (listp name) name (list name)) 172 (let ((fname (intern (format "sly-test-%s" name)))) 173 (cons `(defun ,fname ,args 174 (sly-sync-to-top-level 0.3) 175 ,@body 176 (sly-sync-to-top-level 0.3)) 177 (cl-loop for input in (eval inputs) 178 for i from 1 179 with fails-for = (cdr (assoc :fails-for options)) 180 with style = (cdr (assoc :style options)) 181 collect (sly-test-ert-test-for name 182 input 183 i 184 doc 185 body 186 fails-for 187 style 188 fname)))))))) 189 190 (defmacro sly-check (check &rest body) 191 (declare (indent defun)) 192 `(unless (progn ,@body) 193 (ert-fail ,(cl-etypecase check 194 (cons `(concat "Ooops, " ,(cons 'format check))) 195 (string `(concat "Check failed: " ,check)) 196 (symbol `(concat "Check failed: " ,(symbol-name check))))))) 197 198 199 ;;;;; Test case definitions 200 (defun sly-check-top-level () ;(&optional _test-name) 201 (accept-process-output nil 0.001) 202 (sly-check "At the top level (no debugging or pending RPCs)" 203 (sly-at-top-level-p))) 204 205 (defun sly-at-top-level-p () 206 (and (not (sly-db-get-default-buffer)) 207 (null (sly-rex-continuations)))) 208 209 (defun sly-wait-condition (name predicate timeout &optional cleanup) 210 (let ((end (time-add (current-time) (seconds-to-time timeout)))) 211 (while (not (funcall predicate)) 212 (sly-message "waiting for condition: %s [%s]" name 213 (format-time-string "%H:%M:%S.%6N")) 214 (cond ((time-less-p end (current-time)) 215 (unwind-protect 216 (error "Timeout waiting for condition: %S" name) 217 (funcall cleanup))) 218 (t 219 ;; XXX if a process-filter enters a recursive-edit, we 220 ;; hang forever 221 (accept-process-output nil 0.1)))))) 222 223 (defun sly-sync-to-top-level (timeout) 224 (sly-wait-condition "top-level" #'sly-at-top-level-p timeout 225 (lambda () 226 (let ((sly-db-buffer 227 (sly-db-get-default-buffer))) 228 (when (bufferp sly-db-buffer) 229 (with-current-buffer sly-db-buffer 230 (sly-db-quit))))))) 231 232 ;; XXX: unused function 233 (defun sly-check-sly-db-level (expected) 234 (let ((sly-db-level (let ((sly-db (sly-db-get-default-buffer))) 235 (if sly-db 236 (with-current-buffer sly-db 237 sly-db-level))))) 238 (sly-check ("SLY-DB level (%S) is %S" expected sly-db-level) 239 (equal expected sly-db-level)))) 240 241 (defun sly-test-expect (_name expected actual &optional test) 242 (when (stringp expected) (setq expected (substring-no-properties expected))) 243 (when (stringp actual) (setq actual (substring-no-properties actual))) 244 (if test 245 (should (funcall test expected actual)) 246 (should (equal expected actual)))) 247 248 (defun sly-db-level () 249 (let ((sly-db (sly-db-get-default-buffer))) 250 (if sly-db 251 (with-current-buffer sly-db 252 sly-db-level)))) 253 254 (defun sly-sly-db-level= (level) 255 (equal level (sly-db-level))) 256 257 (eval-when-compile 258 (defvar sly-test-symbols 259 '(("foobar") ("foo@bar") ("@foobar") ("foobar@") ("\\@foobar") 260 ("|asdf||foo||bar|") 261 ("\\#<Foo@Bar>") 262 ("\\(setf\\ car\\)")))) 263 264 (defun sly-check-symbol-at-point (prefix symbol suffix) 265 ;; We test that `sly-symbol-at-point' works at every 266 ;; character of the symbol name. 267 (with-temp-buffer 268 (lisp-mode) 269 (insert prefix) 270 (let ((start (point))) 271 (insert symbol suffix) 272 (dotimes (i (length symbol)) 273 (goto-char (+ start i)) 274 (sly-test-expect (format "Check `%s' (at %d)..." 275 (buffer-string) (point)) 276 symbol 277 (sly-symbol-at-point) 278 #'equal))))) 279 280 281 282 (def-sly-test symbol-at-point.2 (sym) 283 "fancy symbol-name _not_ at BOB/EOB" 284 sly-test-symbols 285 (sly-check-symbol-at-point "(foo " sym " bar)")) 286 287 (def-sly-test symbol-at-point.3 (sym) 288 "fancy symbol-name with leading ," 289 (cl-remove-if (lambda (s) (eq (aref (car s) 0) ?@)) sly-test-symbols) 290 (sly-check-symbol-at-point "," sym "")) 291 292 (def-sly-test symbol-at-point.4 (sym) 293 "fancy symbol-name with leading ,@" 294 sly-test-symbols 295 (sly-check-symbol-at-point ",@" sym "")) 296 297 (def-sly-test symbol-at-point.5 (sym) 298 "fancy symbol-name with leading `" 299 sly-test-symbols 300 (sly-check-symbol-at-point "`" sym "")) 301 302 (def-sly-test symbol-at-point.6 (sym) 303 "fancy symbol-name wrapped in ()" 304 sly-test-symbols 305 (sly-check-symbol-at-point "(" sym ")")) 306 307 (def-sly-test symbol-at-point.7 (sym) 308 "fancy symbol-name wrapped in #< {DEADBEEF}>" 309 sly-test-symbols 310 (sly-check-symbol-at-point "#<" sym " {DEADBEEF}>")) 311 312 ;;(def-sly-test symbol-at-point.8 (sym) 313 ;; "fancy symbol-name wrapped in #<>" 314 ;; sly-test-symbols 315 ;; (sly-check-symbol-at-point "#<" sym ">")) 316 317 (def-sly-test symbol-at-point.9 (sym) 318 "fancy symbol-name wrapped in #| ... |#" 319 sly-test-symbols 320 (sly-check-symbol-at-point "#|\n" sym "\n|#")) 321 322 (def-sly-test symbol-at-point.10 (sym) 323 "fancy symbol-name after #| )))(( |# (1)" 324 sly-test-symbols 325 (sly-check-symbol-at-point "#| )))(( #|\n" sym "")) 326 327 (def-sly-test symbol-at-point.11 (sym) 328 "fancy symbol-name after #| )))(( |# (2)" 329 sly-test-symbols 330 (sly-check-symbol-at-point "#| )))(( #|" sym "")) 331 332 (def-sly-test symbol-at-point.12 (sym) 333 "fancy symbol-name wrapped in \"...\"" 334 sly-test-symbols 335 (sly-check-symbol-at-point "\"\n" sym "\"\n")) 336 337 (def-sly-test symbol-at-point.13 (sym) 338 "fancy symbol-name wrapped in \" )))(( \" (1)" 339 sly-test-symbols 340 (sly-check-symbol-at-point "\" )))(( \"\n" sym "")) 341 342 (def-sly-test symbol-at-point.14 (sym) 343 "fancy symbol-name wrapped in \" )))(( \" (1)" 344 sly-test-symbols 345 (sly-check-symbol-at-point "\" )))(( \"" sym "")) 346 347 (def-sly-test symbol-at-point.15 (sym) 348 "symbol-at-point after #." 349 sly-test-symbols 350 (sly-check-symbol-at-point "#." sym "")) 351 352 (def-sly-test symbol-at-point.16 (sym) 353 "symbol-at-point after #+" 354 sly-test-symbols 355 (sly-check-symbol-at-point "#+" sym "")) 356 357 358 (def-sly-test sexp-at-point.1 (string) 359 "symbol-at-point after #'" 360 '(("foo") 361 ("#:foo") 362 ("#'foo") 363 ("#'(lambda (x) x)") 364 ("()")) 365 (with-temp-buffer 366 (lisp-mode) 367 (insert string) 368 (goto-char (point-min)) 369 (sly-test-expect (format "Check sexp `%s' (at %d)..." 370 (buffer-string) (point)) 371 string 372 (sly-sexp-at-point) 373 #'equal))) 374 375 (def-sly-test narrowing () 376 "Check that narrowing is properly sustained." 377 '() 378 (sly-check-top-level) 379 (let ((random-buffer-name (symbol-name (cl-gensym))) 380 (defun-pos) (tmpbuffer)) 381 (with-temp-buffer 382 (dotimes (i 100) (insert (format ";;; %d. line\n" i))) 383 (setq tmpbuffer (current-buffer)) 384 (setq defun-pos (point)) 385 (insert (concat "(defun __foo__ (x y)" "\n" 386 " 'nothing)" "\n")) 387 (dotimes (i 100) (insert (format ";;; %d. line\n" (+ 100 i)))) 388 (sly-check "Checking that newly created buffer is not narrowed." 389 (not (buffer-narrowed-p))) 390 391 (goto-char defun-pos) 392 (narrow-to-defun) 393 (sly-check "Checking that narrowing succeeded." 394 (buffer-narrowed-p)) 395 396 (sly-with-popup-buffer (random-buffer-name) 397 (sly-check ("Checking that we're in Sly's temp buffer `%s'" 398 random-buffer-name) 399 (equal (buffer-name (current-buffer)) random-buffer-name))) 400 (with-current-buffer random-buffer-name 401 ;; Notice that we cannot quit the buffer within the extent 402 ;; of sly-with-output-to-temp-buffer. 403 (quit-window t)) 404 (sly-check ("Checking that we've got back from `%s'" 405 random-buffer-name) 406 (and (eq (current-buffer) tmpbuffer) 407 (= (point) defun-pos))) 408 409 (sly-check "Checking that narrowing sustained \ 410 after quitting Sly's temp buffer." 411 (buffer-narrowed-p)) 412 413 (let ((sly-buffer-package "SLYNK") 414 (symbol '*buffer-package*)) 415 (sly-edit-definition (symbol-name symbol)) 416 (sly-check ("Checking that we've got M-. into slynk.lisp. %S" symbol) 417 (string= (file-name-nondirectory (buffer-file-name)) 418 "slynk.lisp")) 419 (sly-pop-find-definition-stack) 420 (sly-check ("Checking that we've got back.") 421 (and (eq (current-buffer) tmpbuffer) 422 (= (point) defun-pos))) 423 424 (sly-check "Checking that narrowing sustained after M-," 425 (buffer-narrowed-p))) 426 )) 427 (sly-check-top-level)) 428 429 (defun sly-test--pos-at-line (line) 430 (save-excursion 431 (goto-char (point-min)) 432 (forward-line (1- line)) 433 (line-beginning-position))) 434 435 (def-sly-test recenter 436 (pos-line target expected-window-start) 437 "Test `sly-recenter'." 438 ;; numbers are actually lines numbers 439 '(;; region visible, point in region 440 (2 4 1) 441 ;; end not visible 442 (2 (+ wh 2) 2) 443 ;; end and start not visible 444 ((+ wh 2) (+ wh 500) (+ wh 2))) 445 (when noninteractive 446 (sly-skip-test "Can't test sly-recenter in batch mode")) 447 (with-temp-buffer 448 (cl-loop for i from 1 upto 1000 449 do (insert (format "%09d\n" i))) 450 (let* ((win (display-buffer (current-buffer)))) 451 (cl-flet ((eval-with-wh (form) 452 (eval `(let ((wh ,(window-text-height win))) 453 ,form)))) 454 (with-selected-window win 455 (goto-char (sly-test--pos-at-line (eval-with-wh pos-line))) 456 (sly-recenter (sly-test--pos-at-line (eval-with-wh target))) 457 (redisplay) 458 (should (= (eval-with-wh expected-window-start) 459 (line-number-at-pos (window-start))))))))) 460 461 (def-sly-test find-definition 462 (name buffer-package snippet) 463 "Find the definition of a function or macro in slynk.lisp." 464 '(("start-server" "SLYNK" "(defun start-server ") 465 ("slynk::start-server" "CL-USER" "(defun start-server ") 466 ("slynk:start-server" "CL-USER" "(defun start-server ") 467 ("slynk::connection" "CL-USER" "(defstruct (connection") 468 ("slynk::*emacs-connection*" "CL-USER" "(defvar \\*emacs-connection\\*") 469 ) 470 (switch-to-buffer "*scratch*") ; not buffer of definition 471 (sly-check-top-level) 472 (let ((orig-buffer (current-buffer)) 473 (orig-pos (point)) 474 (enable-local-variables nil) ; don't get stuck on -*- eval: -*- 475 (sly-buffer-package buffer-package)) 476 (sly-edit-definition name) 477 ;; Postconditions 478 (sly-check ("Definition of `%S' is in slynk.lisp." name) 479 (string= (file-name-nondirectory (buffer-file-name)) "slynk.lisp")) 480 (sly-check ("Looking at '%s'." snippet) (looking-at snippet)) 481 (sly-pop-find-definition-stack) 482 (sly-check "Returning from definition restores original buffer/position." 483 (and (eq orig-buffer (current-buffer)) 484 (= orig-pos (point))))) 485 (sly-check-top-level)) 486 487 (def-sly-test (find-definition.2 (:fails-for "allegro" "lispworks")) 488 (buffer-content buffer-package snippet) 489 "Check that we're able to find definitions even when 490 confronted with nasty #.-fu." 491 '(("#.(prog1 nil (defvar *foobar* 42)) 492 493 (defun .foo. (x) 494 (+ x #.*foobar*)) 495 496 #.(prog1 nil (makunbound '*foobar*)) 497 " 498 "SLYNK" 499 "[ \t]*(defun .foo. " 500 ) 501 ("#.(prog1 nil (defvar *foobar* 42)) 502 503 ;; some comment 504 (defun .foo. (x) 505 (+ x #.*foobar*)) 506 507 #.(prog1 nil (makunbound '*foobar*)) 508 " 509 "SLYNK" 510 "[ \t]*(defun .foo. " 511 ) 512 ("(in-package slynk) 513 (eval-when (:compile-toplevel) (defparameter *bar* 456)) 514 (eval-when (:load-toplevel :execute) (makunbound '*bar*)) 515 (defun bar () #.*bar*) 516 (defun .foo. () 123)" 517 "SLYNK" 518 "[ \t]*(defun .foo. () 123)")) 519 (let ((sly-buffer-package buffer-package)) 520 (with-temp-buffer 521 (insert buffer-content) 522 (sly-check-top-level) 523 (sly-eval 524 `(slynk:compile-string-for-emacs 525 ,buffer-content 526 ,(buffer-name) 527 '((:position 0) (:line 1 1)) 528 ,nil 529 ,nil)) 530 (let ((bufname (buffer-name))) 531 (sly-edit-definition ".foo.") 532 (sly-check ("Definition of `.foo.' is in buffer `%s'." bufname) 533 (string= (buffer-name) bufname)) 534 (sly-check "Definition now at point." (looking-at snippet))) 535 ))) 536 537 (def-sly-test (find-definition.3 538 (:fails-for "abcl" "allegro" "clisp" "lispworks" 539 ("sbcl" version< "1.3.0") 540 "ecl")) 541 (name source regexp) 542 "Extra tests for defstruct." 543 '(("slynk::foo-struct" 544 "(progn 545 (defun foo-fun ()) 546 (defstruct (foo-struct (:constructor nil) (:predicate nil))) 547 )" 548 "(defstruct (foo-struct")) 549 (switch-to-buffer "*scratch*") 550 (with-temp-buffer 551 (insert source) 552 (let ((sly-buffer-package "SLYNK")) 553 (sly-eval 554 `(slynk:compile-string-for-emacs 555 ,source 556 ,(buffer-name) 557 '((:position 0) (:line 1 1)) 558 ,nil 559 ,nil))) 560 (let ((temp-buffer (current-buffer))) 561 (with-current-buffer "*scratch*" 562 (sly-edit-definition name) 563 (sly-check ("Definition of %S is in buffer `%s'." 564 name temp-buffer) 565 (eq (current-buffer) temp-buffer)) 566 (sly-check "Definition now at point." (looking-at regexp))) 567 ))) 568 569 (def-sly-test complete-symbol 570 (prefix expected-completions) 571 "Find the completions of a symbol-name prefix." 572 '(("cl:compile" (("cl:compile" "cl:compile-file" "cl:compile-file-pathname" 573 "cl:compiled-function" "cl:compiled-function-p" 574 "cl:compiler-macro" "cl:compiler-macro-function") 575 "cl:compile")) 576 ("cl:foobar" (nil "")) 577 ("slynk::compile-file" (("slynk::compile-file" 578 "slynk::compile-file-for-emacs" 579 "slynk::compile-file-if-needed" 580 "slynk::compile-file-output" 581 "slynk::compile-file-pathname") 582 "slynk::compile-file")) 583 ("cl:m-v-l" (nil ""))) 584 (let ((completions (sly-simple-completions prefix))) 585 (sly-test-expect "Completion set" expected-completions completions))) 586 587 (def-sly-test flex-complete-symbol 588 (prefix expectations) 589 "Find the flex completions of a symbol-name prefix." 590 '(("m-v-b" (("multiple-value-bind" 1))) 591 ("mvbind" (("multiple-value-bind" 1))) 592 ("mvcall" (("multiple-value-call" 1))) 593 ("mvlist" (("multiple-value-list" 3))) 594 ("echonumberlist" (("slynk:*echo-number-alist*" 1)))) 595 (let* ((sly-buffer-package "COMMON-LISP") 596 (completions (car (sly-flex-completions prefix)))) 597 (cl-loop for (completion before-or-at) in expectations 598 for pos = (cl-position completion completions :test #'string=) 599 unless pos 600 do (ert-fail (format "Didn't find %s in the completions for %s" completion prefix)) 601 unless (< pos before-or-at) 602 do (ert-fail (format "Expected to find %s in the first %s completions for %s, but it came in %s 603 => %s" 604 completion before-or-at prefix (1+ pos) 605 (cl-subseq completions 0 (1+ pos))))))) 606 607 (def-sly-test basic-completion 608 (input-keys expected-result) 609 "Test `sly-read-from-minibuffer' with INPUT-KEYS as events." 610 '(("( r e v e TAB TAB SPC ' ( 1 SPC 2 SPC 3 ) ) RET" 611 "(reverse '(1 2 3))") 612 ("( c l : c o n TAB s t a n t l TAB TAB SPC 4 2 ) RET" 613 "(cl:constantly 42)")) 614 (when noninteractive 615 (sly-skip-test "Can't use unread-command-events in batch mode")) 616 (setq unread-command-events (listify-key-sequence (kbd input-keys))) 617 (let ((actual-result (sly-read-from-minibuffer "Test: "))) 618 (sly-test-expect "Completed string" expected-result actual-result))) 619 620 (def-sly-test arglist 621 ;; N.B. Allegro apparently doesn't return the default values of 622 ;; optional parameters. Thus the regexp in the start-server 623 ;; expected value. In a perfect world we'd find a way to smooth 624 ;; over this difference between implementations--perhaps by 625 ;; convincing Franz to provide a function that does what we want. 626 (function-name expected-arglist) 627 "Lookup the argument list for FUNCTION-NAME. 628 Confirm that EXPECTED-ARGLIST is displayed." 629 '(("slynk::operator-arglist" "(slynk::operator-arglist name package)") 630 ("slynk::compute-backtrace" "(slynk::compute-backtrace start end)") 631 ("slynk::emacs-connected" "(slynk::emacs-connected)") 632 ("slynk::compile-string-for-emacs" 633 "(slynk::compile-string-for-emacs \ 634 string buffer position filename policy)") 635 ("slynk::connection-socket-io" 636 "(slynk::connection-socket-io \ 637 \\(struct\\(ure\\)?\\|object\\|instance\\|x\\|connection\\))") 638 ("cl:lisp-implementation-type" "(cl:lisp-implementation-type)") 639 ("cl:class-name" 640 "(cl:class-name \\(class\\|object\\|instance\\|structure\\))")) 641 (let ((arglist (sly-eval `(slynk:operator-arglist ,function-name 642 "slynk")))) 643 (sly-test-expect "Argument list is as expected" 644 expected-arglist (and arglist (downcase arglist)) 645 (lambda (pattern arglist) 646 (and arglist (string-match pattern arglist)))))) 647 648 (defun sly-test--compile-defun (program subform) 649 (sly-check-top-level) 650 (with-temp-buffer 651 (lisp-mode) 652 (insert program) 653 (let ((font-lock-verbose nil)) 654 (setq sly-buffer-package ":slynk") 655 (sly-compile-string (buffer-string) 1) 656 (setq sly-buffer-package ":cl-user") 657 (sly-sync-to-top-level 5) 658 (goto-char (point-max)) 659 (call-interactively 'sly-previous-note) 660 (sly-check error-location-correct 661 (equal (read (current-buffer)) subform)))) 662 (sly-check-top-level)) 663 664 (def-sly-test (compile-defun (:fails-for "allegro" "lispworks" "clisp")) 665 (program subform) 666 "Compile PROGRAM containing errors. 667 Confirm that the EXPECTED subform is correctly located." 668 '(("(defun cl-user::foo () (cl-user::bar))" (cl-user::bar)) 669 ("(defun cl-user::foo () 670 #\\space 671 ;;Sdf 672 (cl-user::bar))" 673 (cl-user::bar)) 674 ("(defun cl-user::foo () 675 #+(or)skipped 676 #| #||# 677 #||# |# 678 (cl-user::bar))" 679 (cl-user::bar)) 680 ("(defun cl-user::foo () 681 \"\\\" bla bla \\\"\" 682 (cl-user::bar))" 683 (cl-user::bar)) 684 ("(defun cl-user::foo () 685 #.*log-events* 686 (cl-user::bar))" 687 (cl-user::bar)) 688 ("#.'(defun x () (/ 1 0)) 689 (defun foo () 690 (cl-user::bar)) 691 692 " 693 (cl-user::bar))) 694 (sly-test--compile-defun program subform)) 695 696 ;; This test ideally would be collapsed into the previous 697 ;; compile-defun test, but only 1 case fails for ccl--and that's here 698 (def-sly-test (compile-defun-with-reader-conditionals 699 (:fails-for "allegro" "lispworks" "clisp" "ccl")) 700 (program expected) 701 "Compile PROGRAM containing errors. 702 Confirm that the EXPECTED subform is correctly located." 703 '(("(defun foo () 704 #+#.'(:and) (/ 1 0))" 705 (/ 1 0))) 706 (sly-test--compile-defun program expected)) 707 708 ;; SBCL used to pass this one but since 1.2.2 the backquote/unquote 709 ;; reader was changed. See 710 ;; https://bugs.launchpad.net/sbcl/+bug/1361502 711 (def-sly-test (compile-defun-with-backquote 712 (:fails-for "sbcl" "allegro" "lispworks" "clisp")) 713 (program subform) 714 "Compile PROGRAM containing errors. 715 Confirm that SUBFORM is correctly located." 716 '(("(defun cl-user::foo () 717 (list `(1 ,(random 10) 2 ,@(make-list (random 10)) 3 718 ,(cl-user::bar))))" 719 (cl-user::bar))) 720 (sly-test--compile-defun program subform)) 721 722 (def-sly-test (compile-file (:fails-for "allegro" "lispworks" "clisp")) 723 (string) 724 "Insert STRING in a file, and compile it." 725 `((,(pp-to-string '(defun foo () nil)))) 726 (let ((filename "/tmp/sly-tmp-file.lisp")) 727 (with-temp-file filename 728 (insert string)) 729 (let ((cell (cons nil nil))) 730 (sly-eval-async 731 `(slynk:compile-file-for-emacs ,filename nil) 732 (sly-rcurry (lambda (result cell) 733 (setcar cell t) 734 (setcdr cell result)) 735 cell)) 736 (sly-wait-condition "Compilation finished" (lambda () (car cell)) 737 0.5) 738 (let ((result (cdr cell))) 739 (sly-check "Compilation successfull" 740 (eq (sly-compilation-result.successp result) t)))))) 741 742 (def-sly-test utf-8-source 743 (input output) 744 "Source code containing utf-8 should work" 745 (list (let* ((bytes "\343\201\212\343\201\257\343\202\210\343\201\206") 746 ;;(encode-coding-string (string #x304a #x306f #x3088 #x3046) 747 ;; 'utf-8) 748 (string (decode-coding-string bytes 'utf-8-unix))) 749 (cl-assert (equal bytes (encode-coding-string string 'utf-8-unix))) 750 (list (concat "(defun cl-user::foo () \"" string "\")") 751 string))) 752 (sly-eval `(cl:eval (cl:read-from-string ,input))) 753 (sly-test-expect "Eval result correct" 754 output (sly-eval '(cl-user::foo))) 755 (let ((cell (cons nil nil))) 756 (let ((hook (sly-curry (lambda (cell &rest _) (setcar cell t)) cell))) 757 (add-hook 'sly-compilation-finished-hook hook) 758 (unwind-protect 759 (progn 760 (sly-compile-string input 0) 761 (sly-wait-condition "Compilation finished" 762 (lambda () (car cell)) 763 0.5) 764 (sly-test-expect "Compile-string result correct" 765 output (sly-eval '(cl-user::foo)))) 766 (remove-hook 'sly-compilation-finished-hook hook)) 767 (let ((filename "/tmp/sly-tmp-file.lisp")) 768 (setcar cell nil) 769 (add-hook 'sly-compilation-finished-hook hook) 770 (unwind-protect 771 (with-temp-buffer 772 (when (fboundp 'set-buffer-multibyte) 773 (set-buffer-multibyte t)) 774 (setq buffer-file-coding-system 'utf-8-unix) 775 (setq buffer-file-name filename) 776 (insert ";; -*- coding: utf-8-unix -*- \n") 777 (insert input) 778 (let ((coding-system-for-write 'utf-8-unix)) 779 (write-region nil nil filename nil t)) 780 (let ((sly-load-failed-fasl 'always)) 781 (sly-compile-and-load-file) 782 (sly-wait-condition "Compilation finished" 783 (lambda () (car cell)) 784 0.5)) 785 (sly-test-expect "Compile-file result correct" 786 output (sly-eval '(cl-user::foo)))) 787 (remove-hook 'sly-compilation-finished-hook hook) 788 (ignore-errors (delete-file filename))))))) 789 790 (def-sly-test async-eval-debugging (depth) 791 "Test recursive debugging of asynchronous evaluation requests." 792 '((1) (2) (3)) 793 (let ((depth depth) 794 (debug-hook-max-depth 0)) 795 (let ((debug-hook 796 (lambda () 797 (with-current-buffer (sly-db-get-default-buffer) 798 (when (> sly-db-level debug-hook-max-depth) 799 (setq debug-hook-max-depth sly-db-level) 800 (if (= sly-db-level depth) 801 ;; We're at maximum recursion - time to unwind 802 (sly-db-quit) 803 ;; Going down - enter another recursive debug 804 ;; Recursively debug. 805 (sly-eval-async '(error)))))))) 806 (let ((sly-db-hook (cons debug-hook sly-db-hook))) 807 (sly-eval-async '(error)) 808 (sly-sync-to-top-level 5) 809 (sly-check ("Maximum depth reached (%S) is %S." 810 debug-hook-max-depth depth) 811 (= debug-hook-max-depth depth)))))) 812 813 (def-sly-test unwind-to-previous-sly-db-level (level2 level1) 814 "Test recursive debugging and returning to lower SLY-DB levels." 815 '((2 1) (4 2)) 816 (sly-check-top-level) 817 (let ((level2 level2) 818 (level1 level1) 819 (state 'enter) 820 (max-depth 0)) 821 (let ((debug-hook 822 (lambda () 823 (with-current-buffer (sly-db-get-default-buffer) 824 (setq max-depth (max sly-db-level max-depth)) 825 (cl-ecase state 826 (enter 827 (cond ((= sly-db-level level2) 828 (setq state 'leave) 829 (sly-db-invoke-restart (sly-db-first-abort-restart))) 830 (t 831 (sly-eval-async `(cl:aref cl:nil ,sly-db-level))))) 832 (leave 833 (cond ((= sly-db-level level1) 834 (setq state 'ok) 835 (sly-db-quit)) 836 (t 837 (sly-db-invoke-restart (sly-db-first-abort-restart)) 838 )))))))) 839 (let ((sly-db-hook (cons debug-hook sly-db-hook))) 840 (sly-eval-async `(cl:aref cl:nil 0)) 841 (sly-sync-to-top-level 15) 842 (sly-check-top-level) 843 (sly-check ("Maximum depth reached (%S) is %S." max-depth level2) 844 (= max-depth level2)) 845 (sly-check ("Final state reached.") 846 (eq state 'ok)))))) 847 848 (defun sly-db-first-abort-restart () 849 (let ((case-fold-search t)) 850 (cl-position-if (lambda (x) (string-match "abort" (car x))) sly-db-restarts))) 851 852 (def-sly-test loop-interrupt-quit 853 () 854 "Test interrupting a loop." 855 '(()) 856 (sly-check-top-level) 857 (sly-eval-async '(cl:loop) (lambda (_) ) "CL-USER") 858 (accept-process-output nil 1) 859 (sly-check "In eval state." (sly-busy-p)) 860 (sly-interrupt) 861 (sly-wait-condition "First interrupt" (lambda () (sly-sly-db-level= 1)) 5) 862 (with-current-buffer (sly-db-get-default-buffer) 863 (sly-db-quit)) 864 (sly-sync-to-top-level 5) 865 (sly-check-top-level)) 866 867 (def-sly-test loop-interrupt-continue-interrupt-quit 868 () 869 "Test interrupting a previously interrupted but continued loop." 870 '(()) 871 (sly-check-top-level) 872 (sly-eval-async '(cl:loop) (lambda (_) ) "CL-USER") 873 (sleep-for 1) 874 (sly-wait-condition "running" #'sly-busy-p 5) 875 (sly-interrupt) 876 (sly-wait-condition "First interrupt" (lambda () (sly-sly-db-level= 1)) 5) 877 (with-current-buffer (sly-db-get-default-buffer) 878 (sly-db-continue)) 879 (sly-wait-condition "running" (lambda () 880 (and (sly-busy-p) 881 (not (sly-db-get-default-buffer)))) 5) 882 (sly-interrupt) 883 (sly-wait-condition "Second interrupt" (lambda () (sly-sly-db-level= 1)) 5) 884 (with-current-buffer (sly-db-get-default-buffer) 885 (sly-db-quit)) 886 (sly-sync-to-top-level 5) 887 (sly-check-top-level)) 888 889 (def-sly-test interactive-eval 890 () 891 "Test interactive eval and continuing from the debugger." 892 '(()) 893 (sly-check-top-level) 894 (let ((sly-db-hook (lambda () 895 (sly-db-continue)))) 896 (sly-interactive-eval 897 "(progn\ 898 (cerror \"foo\" \"restart\")\ 899 (cerror \"bar\" \"restart\")\ 900 (+ 1 2))") 901 (sly-sync-to-top-level 5) 902 (current-message)) 903 (unless noninteractive 904 (should (equal "=> 3 (2 bits, #x3, #o3, #b11)" 905 (current-message))))) 906 907 (def-sly-test report-condition-with-circular-list 908 (format-control format-argument) 909 "Test conditions involving circular lists." 910 '(("~a" "(let ((x (cons nil nil))) (setf (cdr x) x))") 911 ("~a" "(let ((x (cons nil nil))) (setf (car x) x))") 912 ("~a" "(let ((x (cons (make-string 100000 :initial-element #\\X) nil)))\ 913 (setf (cdr x) x))")) 914 (sly-check-top-level) 915 (let ((done nil)) 916 (let ((sly-db-hook (lambda () (sly-db-continue) (setq done t)))) 917 (sly-interactive-eval 918 (format "(with-standard-io-syntax (cerror \"foo\" \"%s\" %s) (+ 1 2))" 919 format-control format-argument)) 920 (while (not done) (accept-process-output)) 921 (sly-sync-to-top-level 5) 922 (sly-check-top-level) 923 (unless noninteractive 924 (let ((message (current-message))) 925 (sly-check "Minibuffer contains: \"3\"" 926 (equal "=> 3 (2 bits, #x3, #o3, #b11)" message))))))) 927 928 (def-sly-test interrupt-bubbling-idiot 929 () 930 "Test interrupting a loop that sends a lot of output to Emacs." 931 '(()) 932 (accept-process-output nil 1) 933 (sly-check-top-level) 934 (sly-eval-async '(cl:loop :for i :from 0 :do (cl:progn (cl:print i) 935 (cl:finish-output))) 936 (lambda (_) ) 937 "CL-USER") 938 (sleep-for 1) 939 (sly-interrupt) 940 (sly-wait-condition "Debugger visible" 941 (lambda () 942 (and (sly-sly-db-level= 1) 943 (get-buffer-window (sly-db-get-default-buffer)))) 944 30) 945 (with-current-buffer (sly-db-get-default-buffer) 946 (sly-db-quit)) 947 (sly-sync-to-top-level 5)) 948 949 (def-sly-test (interrupt-encode-message (:style :sigio)) 950 () 951 "Test interrupt processing during slynk::encode-message" 952 '(()) 953 (sly-eval-async '(cl:loop :for i :from 0 954 :do (slynk::background-message "foo ~d" i))) 955 (sleep-for 1) 956 (sly-eval-async '(cl:/ 1 0)) 957 (sly-wait-condition "Debugger visible" 958 (lambda () 959 (and (sly-sly-db-level= 1) 960 (get-buffer-window (sly-db-get-default-buffer)))) 961 30) 962 (with-current-buffer (sly-db-get-default-buffer) 963 (sly-db-quit)) 964 (sly-sync-to-top-level 5)) 965 966 (def-sly-test inspector 967 (exp) 968 "Test basic inspector workingness." 969 '(((let ((h (make-hash-table))) 970 (loop for i below 10 do (setf (gethash i h) i)) 971 h)) 972 ((make-array 10)) 973 ((make-list 10)) 974 ('cons) 975 (#'cons)) 976 (sly-inspect (prin1-to-string exp)) 977 (cl-assert (not (sly-inspector-visible-p))) 978 (sly-wait-condition "Inspector visible" #'sly-inspector-visible-p 5) 979 (with-current-buffer (window-buffer (selected-window)) 980 (sly-inspector-quit)) 981 (sly-wait-condition "Inspector closed" 982 (lambda () (not (sly-inspector-visible-p))) 983 5) 984 (sly-sync-to-top-level 1)) 985 986 (defun sly-buffer-visible-p (name) 987 (let ((buffer (window-buffer (selected-window)))) 988 (string-match name (buffer-name buffer)))) 989 990 (defun sly-inspector-visible-p () 991 (sly-buffer-visible-p (sly-buffer-name :inspector :connection t))) 992 993 (defun sly-execute-as-command (name) 994 "Execute `name' as if it was done by the user through the 995 Command Loop. Similiar to `call-interactively' but also pushes on 996 the buffer's undo-list." 997 (undo-boundary) 998 (call-interactively name)) 999 1000 (def-sly-test macroexpand 1001 (macro-defs bufcontent expansion1 search-str expansion2) 1002 "foo" 1003 '((("(defmacro qwertz (&body body) `(list :qwertz ',body))" 1004 "(defmacro yxcv (&body body) `(list :yxcv (qwertz ,@body)))") 1005 "(yxcv :A :B :C)" 1006 "(list :yxcv (qwertz :a :b :c))" 1007 "(qwertz" 1008 "(list :yxcv (list :qwertz '(:a :b :c)))")) 1009 (sly-check-top-level) 1010 (setq sly-buffer-package ":slynk") 1011 (with-temp-buffer 1012 (lisp-mode) 1013 (dolist (def macro-defs) 1014 (sly-compile-string def 0) 1015 (sly-sync-to-top-level 5)) 1016 (insert bufcontent) 1017 (goto-char (point-min)) 1018 (sly-execute-as-command 'sly-macroexpand-1) 1019 (sly-wait-condition "Macroexpansion buffer visible" 1020 (lambda () 1021 (sly-buffer-visible-p 1022 (sly-buffer-name :macroexpansion))) 1023 5) 1024 (with-current-buffer (get-buffer (sly-buffer-name :macroexpansion)) 1025 (sly-test-expect "Initial macroexpansion is correct" 1026 expansion1 1027 (downcase (buffer-string)) 1028 #'sly-test-macroexpansion=) 1029 (search-forward search-str) 1030 (backward-up-list) 1031 (sly-execute-as-command 'sly-macroexpand-1-inplace) 1032 (sly-sync-to-top-level 3) 1033 (sly-test-expect "In-place macroexpansion is correct" 1034 expansion2 1035 (downcase (buffer-string)) 1036 #'sly-test-macroexpansion=) 1037 (sly-execute-as-command 'sly-macroexpand-undo) 1038 (sly-test-expect "Expansion after undo is correct" 1039 expansion1 1040 (downcase (buffer-string)) 1041 #'sly-test-macroexpansion=))) 1042 (setq sly-buffer-package ":cl-user")) 1043 1044 (defun sly-test-macroexpansion= (string1 string2 &optional ignore-case) 1045 (let ((string1 (replace-regexp-in-string " *\n *" " " string1)) 1046 (string2 (replace-regexp-in-string " *\n *" " " string2))) 1047 (compare-strings string1 nil nil 1048 string2 nil nil 1049 ignore-case))) 1050 1051 (def-sly-test indentation (buffer-content point-markers) 1052 "Check indentation update to work correctly." 1053 '((" 1054 \(in-package :slynk) 1055 1056 \(defmacro with-lolipop (&body body) 1057 `(progn ,@body)) 1058 1059 \(defmacro lolipop (&body body) 1060 `(progn ,@body)) 1061 1062 \(with-lolipop 1063 1 1064 2 1065 42) 1066 1067 \(lolipop 1068 1 1069 2 1070 23) 1071 " 1072 ("23" "42"))) 1073 (with-temp-buffer 1074 (lisp-mode) 1075 (sly-editing-mode 1) 1076 (insert buffer-content) 1077 (sly-compile-region (point-min) (point-max)) 1078 (sly-sync-to-top-level 3) 1079 (sly-update-indentation) 1080 (sly-sync-to-top-level 3) 1081 (dolist (marker point-markers) 1082 (search-backward marker) 1083 (beginning-of-defun) 1084 (indent-region (point) (progn (end-of-defun) (point)))) 1085 (sly-test-expect "Correct buffer content" 1086 buffer-content 1087 (substring-no-properties (buffer-string))))) 1088 1089 (def-sly-test break 1090 (times exp) 1091 "Test whether BREAK invokes SLY-DB." 1092 (let ((exp1 '(break))) 1093 `((1 ,exp1) (2 ,exp1) (3 ,exp1))) 1094 (accept-process-output nil 0.2) 1095 (sly-check-top-level) 1096 (sly-eval-async 1097 `(cl:eval (cl:read-from-string 1098 ,(prin1-to-string `(dotimes (i ,times) 1099 (unless (= i 0) 1100 (slynk::sleep-for 1)) 1101 ,exp))))) 1102 (dotimes (_i times) 1103 (sly-wait-condition "Debugger visible" 1104 (lambda () 1105 (and (sly-sly-db-level= 1) 1106 (get-buffer-window 1107 (sly-db-get-default-buffer)))) 1108 3) 1109 (with-current-buffer (sly-db-get-default-buffer) 1110 (sly-db-continue)) 1111 (sly-wait-condition "sly-db closed" 1112 (lambda () (not (sly-db-get-default-buffer))) 1113 0.5)) 1114 (sly-sync-to-top-level 1)) 1115 1116 (def-sly-test (break2 (:fails-for "cmucl" "allegro")) 1117 (times exp) 1118 "Backends should arguably make sure that BREAK does not depend 1119 on *DEBUGGER-HOOK*." 1120 (let ((exp2 1121 '(block outta 1122 (let ((*debugger-hook* (lambda (c h) (return-from outta 42)))) 1123 (break))))) 1124 `((1 ,exp2) (2 ,exp2) (3 ,exp2))) 1125 (sly-test-break times exp)) 1126 1127 (def-sly-test locally-bound-debugger-hook 1128 () 1129 "Test that binding *DEBUGGER-HOOK* locally works properly." 1130 '(()) 1131 (accept-process-output nil 1) 1132 (sly-check-top-level) 1133 (sly-compile-string 1134 (prin1-to-string `(defun cl-user::quux () 1135 (block outta 1136 (let ((*debugger-hook* 1137 (lambda (c hook) 1138 (declare (ignore c hook)) 1139 (return-from outta 42)))) 1140 (error "FOO"))))) 1141 0) 1142 (sly-sync-to-top-level 2) 1143 (sly-eval-async '(cl-user::quux)) 1144 ;; FIXME: sly-wait-condition returns immediately if the test returns true 1145 (sly-wait-condition "Checking that Debugger does not popup" 1146 (lambda () 1147 (not (sly-db-get-default-buffer))) 1148 3) 1149 (sly-sync-to-top-level 5)) 1150 1151 (def-sly-test end-of-file 1152 (expr) 1153 "Signalling END-OF-FILE should invoke the debugger." 1154 '(((cl:read-from-string "")) 1155 ((cl:error 'cl:end-of-file))) 1156 (let ((value (sly-eval 1157 `(cl:let ((condition nil)) 1158 (cl:with-simple-restart 1159 (cl:continue "continue") 1160 (cl:let ((cl:*debugger-hook* 1161 (cl:lambda (c h) 1162 (cl:setq condition c) 1163 (cl:continue)))) 1164 ,expr)) 1165 (cl:and (cl:typep condition 'cl:end-of-file)))))) 1166 (sly-test-expect "Debugger invoked" t value))) 1167 1168 (def-sly-test interrupt-at-toplevel 1169 () 1170 "Let's see what happens if we send a user interrupt at toplevel." 1171 '(()) 1172 (sly-check-top-level) 1173 (unless (and (eq (sly-communication-style) :spawn) 1174 (not (featurep 'sly-repl))) 1175 (sly-interrupt) 1176 (sly-wait-condition 1177 "Debugger visible" 1178 (lambda () 1179 (and (sly-sly-db-level= 1) 1180 (get-buffer-window (sly-db-get-default-buffer)))) 1181 5) 1182 (with-current-buffer (sly-db-get-default-buffer) 1183 (sly-db-quit)) 1184 (sly-sync-to-top-level 5))) 1185 1186 (def-sly-test interrupt-in-debugger (interrupts continues) 1187 "Let's see what happens if we interrupt the debugger. 1188 INTERRUPTS ... number of nested interrupts 1189 CONTINUES ... how often the continue restart should be invoked" 1190 '((1 0) (2 1) (4 2)) 1191 (sly-check "No debugger" (not (sly-db-get-default-buffer))) 1192 (when (and (eq (sly-communication-style) :spawn) 1193 (not (featurep 'sly-repl))) 1194 (sly-eval-async '(slynk::without-sly-interrupts 1195 (slynk::receive))) 1196 (sit-for 0.2)) 1197 (dotimes (i interrupts) 1198 (sly-interrupt) 1199 (let ((level (1+ i))) 1200 (sly-wait-condition (format "Debug level %d reachend" level) 1201 (lambda () (equal (sly-db-level) level)) 1202 2))) 1203 (dotimes (i continues) 1204 (with-current-buffer (sly-db-get-default-buffer) 1205 (sly-db-continue)) 1206 (let ((level (- interrupts (1+ i)))) 1207 (sly-wait-condition (format "Return to debug level %d" level) 1208 (lambda () (equal (sly-db-level) level)) 1209 2))) 1210 (with-current-buffer (sly-db-get-default-buffer) 1211 (sly-db-quit)) 1212 (sly-sync-to-top-level 1)) 1213 1214 (def-sly-test flow-control 1215 (n delay interrupts) 1216 "Let Lisp produce output faster than Emacs can consume it." 1217 `((300 0.03 3)) 1218 (when noninteractive 1219 (sly-skip-test "test is currently unstable")) 1220 (sly-check "No debugger" (not (sly-db-get-default-buffer))) 1221 (sly-eval-async `(slynk:flow-control-test ,n ,delay)) 1222 (sleep-for 0.2) 1223 (dotimes (_i interrupts) 1224 (sly-interrupt) 1225 (sly-wait-condition "In debugger" (lambda () (sly-sly-db-level= 1)) 5) 1226 (sly-check "In debugger" (sly-sly-db-level= 1)) 1227 (with-current-buffer (sly-db-get-default-buffer) 1228 (sly-db-continue)) 1229 (sly-wait-condition "No debugger" (lambda () (sly-sly-db-level= nil)) 3) 1230 (sly-check "Debugger closed" (sly-sly-db-level= nil))) 1231 (sly-sync-to-top-level 10)) 1232 1233 (def-sly-test sbcl-world-lock 1234 (n delay) 1235 "Print something from *MACROEXPAND-HOOK*. 1236 In SBCL, the compiler grabs a lock which can be problematic because 1237 no method dispatch code can be generated for other threads. 1238 This test will fail more likely before dispatch caches are warmed up." 1239 '((10 0.03) 1240 ;;((cl:+ slynk::send-counter-limit 10) 0.03) 1241 ) 1242 (sly-test-expect "no error" 1243 123 1244 (sly-eval 1245 `(cl:let ((cl:*macroexpand-hook* 1246 (cl:lambda (fun form env) 1247 (slynk:flow-control-test ,n ,delay) 1248 (cl:funcall fun form env)))) 1249 (cl:eval '(cl:macrolet ((foo () 123)) 1250 (foo))))))) 1251 1252 (def-sly-test (disconnect-one-connection (:style :spawn)) () 1253 "`sly-disconnect' should disconnect only the current connection" 1254 '(()) 1255 (let ((connection-count (length sly-net-processes)) 1256 (old-connection sly-default-connection) 1257 (sly-connected-hook nil)) 1258 (unwind-protect 1259 (let ((sly-dispatching-connection 1260 (sly-connect "localhost" 1261 ;; Here we assume that the request will 1262 ;; be evaluated in its own thread. 1263 (sly-eval `(slynk:create-server 1264 :port 0 ; use random port 1265 :style :spawn 1266 :dont-close nil))))) 1267 (sly-sync-to-top-level 3) 1268 (sly-disconnect) 1269 (sly-test-expect "Number of connections must remane the same" 1270 connection-count 1271 (length sly-net-processes))) 1272 (sly-select-connection old-connection)))) 1273 1274 (def-sly-test disconnect-and-reconnect 1275 () 1276 "Close the connetion. 1277 Confirm that the subprocess continues gracefully. 1278 Reconnect afterwards." 1279 '(()) 1280 (sly-check-top-level) 1281 (let* ((c (sly-connection)) 1282 (p (sly-inferior-process c))) 1283 (with-current-buffer (process-buffer p) 1284 (erase-buffer)) 1285 (delete-process c) 1286 (cl-assert (equal (process-status c) 'closed) nil "Connection not closed") 1287 (accept-process-output nil 0.1) 1288 (cl-assert (equal (process-status p) 'run) nil "Subprocess not running") 1289 (with-current-buffer (process-buffer p) 1290 (cl-assert (< (buffer-size) 500) nil "Unusual output")) 1291 (sly-inferior-connect p (sly-inferior-lisp-args p)) 1292 (let ((hook nil) (p p)) 1293 (setq hook (lambda () 1294 (sly-test-expect 1295 "We are connected again" p (sly-inferior-process)) 1296 (remove-hook 'sly-connected-hook hook))) 1297 (add-hook 'sly-connected-hook hook) 1298 (sly-wait-condition "Lisp restarted" 1299 (lambda () 1300 (not (member hook sly-connected-hook))) 1301 5)))) 1302 1303 1304 ;;;; SLY-loading tests that launch separate Emacsen 1305 ;;;; 1306 (defvar sly-test-check-repl-forms 1307 `((unless (and (featurep 'sly-mrepl) 1308 (assq 'slynk/mrepl sly-contrib--required-slynk-modules)) 1309 (die "`sly-repl' contrib not properly setup")) 1310 (let ((mrepl-buffer (sly-mrepl--find-buffer))) 1311 (unless mrepl-buffer 1312 (die "MREPL buffer not setup!")) 1313 (with-current-buffer mrepl-buffer 1314 ;; FIXME: suboptimal: wait one second for the lisp 1315 ;; to reply. 1316 (sit-for 1) 1317 (unless (and (string-match "^; +SLY" (buffer-string)) 1318 (string-match "CL-USER> *$" (buffer-string))) 1319 (die (format "MREPL prompt: %s" (buffer-string)))))))) 1320 1321 (defvar sly-test-check-asdf-loader-forms 1322 `((when (sly-eval '(cl:and (cl:find-package :slynk-loader) t)) 1323 (die "Didn't expect SLY to be loaded with slynk-loader.lisp")))) 1324 1325 (cl-defun sly-test-recipe-test-for 1326 (&key preflight 1327 (takeoff `((call-interactively 'sly))) 1328 (landing (append sly-test-check-repl-forms 1329 sly-test-check-asdf-loader-forms))) 1330 (let ((success nil) 1331 (test-file (make-temp-file "sly-recipe-" nil ".el")) 1332 (test-forms 1333 `((require 'cl) 1334 (labels 1335 ((die (reason &optional more) 1336 (princ reason) 1337 (terpri) 1338 (and more (pp more)) 1339 (kill-emacs 254))) 1340 (condition-case err 1341 (progn ,@preflight 1342 ,@takeoff 1343 ,(when (null landing) '(kill-emacs 0)) 1344 (add-hook 1345 'sly-connected-hook 1346 #'(lambda () 1347 (condition-case err 1348 (progn 1349 ,@landing 1350 (kill-emacs 0)) 1351 (error 1352 (die "Unexpected error running landing forms" 1353 err)))) 1354 t)) 1355 (error 1356 (die "Unexpected error running preflight/takeoff forms" err))) 1357 (with-timeout 1358 (30 1359 (die "Timeout waiting for recipe test to finish.")) 1360 (while t (sit-for 1))))))) 1361 (unwind-protect 1362 (progn 1363 (with-temp-buffer 1364 (mapc #'insert (mapcar #'pp-to-string test-forms)) 1365 (write-file test-file)) 1366 (with-temp-buffer 1367 (let ((retval 1368 (call-process (concat invocation-directory invocation-name) 1369 nil (list t nil) nil 1370 "-Q" "--batch" 1371 "-l" test-file))) 1372 (unless (= 0 retval) 1373 (ert-fail (buffer-string))))) 1374 (setq success t)) 1375 (if success (delete-file test-file) 1376 (message "Test failed: keeping %s for inspection" test-file))))) 1377 1378 (define-sly-ert-test readme-recipe () 1379 "Test the README.md's autoload recipe." 1380 (sly-test-recipe-test-for 1381 :preflight `((add-to-list 'load-path ,sly-path) 1382 (setq inferior-lisp-program ,inferior-lisp-program) 1383 (require 'sly-autoloads)))) 1384 1385 (define-sly-ert-test traditional-recipe () 1386 "Test the README.md's traditional recipe." 1387 (sly-test-recipe-test-for 1388 :preflight `((add-to-list 'load-path ,sly-path) 1389 (setq inferior-lisp-program ,inferior-lisp-program) 1390 (require 'sly) 1391 (sly-setup '(sly-fancy))))) 1392 1393 (define-sly-ert-test slynk-loader-fallback () 1394 "Test `sly-init-using-slynk-loader'" 1395 ;; TODO: another useful test would be to test 1396 ;; `sly-init-using-asdf's fallback to slynk-loader.lisp." 1397 (sly-test-recipe-test-for 1398 :preflight `((add-to-list 'load-path ,sly-path) 1399 (setq inferior-lisp-program ,inferior-lisp-program) 1400 (require 'sly-autoloads) 1401 (setq sly-contribs '(sly-fancy)) 1402 (setq sly-init-function 'sly-init-using-slynk-loader) 1403 (sly-setup '(sly-fancy))) 1404 :landing `((unless (sly-eval '(cl:and (cl:find-package :slynk-loader) t)) 1405 (die "Expected SLY to be loaded with slynk-loader.lisp")) 1406 ,@sly-test-check-repl-forms))) 1407 1408 1409 ;;; xref recompilation 1410 ;;; 1411 (defun sly-test--eval-now (string) 1412 (cl-second (sly-eval `(slynk:eval-and-grab-output ,string)))) 1413 1414 (def-sly-test (sly-recompile-all-xrefs (:fails-for "cmucl")) () 1415 "Test recompilation of all references within an xref buffer." 1416 '(()) 1417 (let* ((cell (cons nil nil)) 1418 (hook (sly-curry (lambda (cell &rest _) (setcar cell t)) cell)) 1419 (filename (make-temp-file "sly-recompile-all-xrefs" nil ".lisp")) 1420 (xref-buffer)) 1421 (add-hook 'sly-compilation-finished-hook hook) 1422 (unwind-protect 1423 (with-temp-file filename 1424 (set-visited-file-name filename) 1425 (sly-test--eval-now "(defparameter slynk::*.var.* nil)") 1426 (insert "(in-package :slynk) 1427 (defun .fn1. ()) 1428 (defun .fn2. () (.fn1.) #.*.var.*) 1429 (defun .fn3. () (.fn1.) #.*.var.*)") 1430 (save-buffer) 1431 (sly-compile-and-load-file) 1432 (sly-wait-condition "Compilation finished" 1433 (lambda () (car cell)) 1434 0.5) 1435 (sly-test--eval-now "(setq *.var.* t)") 1436 (setcar cell nil) 1437 (sly-xref :calls ".fn1." 1438 (lambda (&rest args) 1439 (setq xref-buffer (apply #'sly-xref--show-results args)) 1440 (setcar cell t))) 1441 (sly-wait-condition "Xrefs computed and displayed" 1442 (lambda () (car cell)) 1443 0.5) 1444 (setcar cell nil) 1445 (should (cl-equalp (list (sly-test--eval-now "(.fn2.)") 1446 (sly-test--eval-now "(.fn3.)")) 1447 '("nil" "nil"))) 1448 ;; Recompile now 1449 ;; 1450 (with-current-buffer xref-buffer 1451 (sly-recompile-all-xrefs) 1452 (sly-wait-condition "Compilation finished" 1453 (lambda () (car cell)) 1454 0.5)) 1455 (should (cl-equalp (list (sly-test--eval-now "(.fn2.)") 1456 (sly-test--eval-now "(.fn3.)")) 1457 '("T" "T")))) 1458 (remove-hook 'sly-compilation-finished-hook hook) 1459 (when xref-buffer 1460 (kill-buffer xref-buffer))))) 1461 1462 1463 ;;; window management after M-. 1464 ;;; 1465 (cl-defmacro sly-test--with-find-definition-window-checker (fn 1466 (window-splits 1467 total-windows 1468 starting-buffer-sym 1469 starting-window-sym) 1470 &rest body) 1471 (declare (indent 2)) 1472 (let ((temp-frame-sym (cl-gensym "temp-frame-"))) 1473 `(progn 1474 (sly-check-top-level) 1475 (let ((,temp-frame-sym nil)) 1476 (unwind-protect 1477 (progn 1478 (setq ,temp-frame-sym (if noninteractive 1479 (selected-frame) 1480 (make-frame))) 1481 ;; too large a frame will exhibit slightly different 1482 ;; window-popping behaviour 1483 (set-frame-width ,temp-frame-sym 100) 1484 (set-frame-height ,temp-frame-sym 40) 1485 (with-selected-frame ,temp-frame-sym 1486 (with-temp-buffer 1487 (delete-other-windows) 1488 (switch-to-buffer (current-buffer)) 1489 (let ((,starting-window-sym (selected-window)) 1490 (,starting-buffer-sym (current-buffer))) 1491 (dotimes (_i ,window-splits) 1492 (split-window)) 1493 (funcall ,fn "cl:print-object") 1494 (should (= ,total-windows (length (window-list ,temp-frame-sym)))) 1495 (with-current-buffer 1496 (window-buffer (selected-window)) 1497 (should (eq major-mode 'sly-xref-mode)) 1498 (forward-line 1) 1499 (sly-xref-goto)) 1500 ,@body)))) 1501 (unless noninteractive 1502 (delete-frame ,temp-frame-sym t))))))) 1503 1504 (def-sly-test find-definition-same-window (window-splits total-windows) 1505 "Test `sly-edit-definition' windows" 1506 '((0 2) 1507 (1 2) 1508 (2 3)) 1509 (sly-test--with-find-definition-window-checker 1510 'sly-edit-definition 1511 (window-splits 1512 total-windows 1513 temp-buffer 1514 original-window) 1515 (with-current-buffer 1516 (window-buffer (selected-window)) 1517 (should-not (eq temp-buffer (current-buffer))) 1518 (should (eq (selected-window) original-window))) 1519 (should (= (if (zerop window-splits) 1520 1 1521 total-windows) 1522 (length (window-list (selected-frame))))))) 1523 1524 (def-sly-test find-definition-other-window (window-splits total-windows) 1525 "Test `sly-edit-definition-other-window' windows" 1526 '((0 2) 1527 (1 2) 1528 (2 3)) 1529 (sly-test--with-find-definition-window-checker 1530 'sly-edit-definition-other-window 1531 (window-splits 1532 total-windows 1533 temp-buffer 1534 original-window) 1535 (with-current-buffer 1536 (window-buffer (selected-window)) 1537 (should (window-live-p original-window)) 1538 (should (eq temp-buffer (window-buffer original-window))) 1539 (should-not (eq (selected-window) original-window))) 1540 (should (= total-windows 1541 (length (window-list (selected-frame))))))) 1542 1543 1544 1545 (provide 'sly-tests)