瀏覽代碼

Add a minimal test suite

The key thing here is the implementation of some very useful helper
macros that make it possible to conduct non-interactive tests of
functions that need to read input, like "completing-read". More tests
will later be added using these macros.
Ryan C. Thompson 9 年之前
父節點
當前提交
8d89026a21
共有 1 個文件被更改,包括 129 次插入0 次删除
  1. 129 0
      ido-ubiquitous-tests.el

+ 129 - 0
ido-ubiquitous-tests.el

@@ -0,0 +1,129 @@
+;;; ido-ubiquitous-tests.el ---  -*- "lexical-binding": t -*-
+
+;; Copyright (C) 2015 Ryan C. Thompson
+
+;; Filename: ido-ubiquitous-tests.el
+;; Author: Ryan C. Thompson
+;; Created: Tue Oct  6 20:52:45 2015 (-0700)
+;; Version: 
+;; Package-Requires: ()
+;; URL: 
+;; Keywords: 
+
+;; This file is NOT part of GNU Emacs.
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 
+;;; Commentary: 
+
+;; 
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or (at
+;; your option) any later version.
+;; 
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;; General Public License for more details.
+;; 
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+;; 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; 
+;;; Code:
+
+;; This is a series of macros to facilitate the testing of completion
+;; non-interactively by simulating input.
+
+(require 'ido-ubiquitous)
+
+(defmacro keyboard-quit-to-error (&rest body)
+  "Evaluate BODY but signal an error on `keyboard-quit'."
+  `(condition-case nil
+       (progn ,@body)
+     (quit
+      (error "Caught `keyboard-quit'"))))
+
+(defmacro with-simulated-input (keys &rest body)
+  "Eval body with KEYS as simulated input.
+
+This macro is intended for testing normally interactive functions
+by simulating input. If BODY tries to read more input events than
+KEYS provides, `keyboard-quit' is invoked (by means of appending
+multple C-g keys to KEYS). This is to ensure that BODY will never
+block waiting for input, since this macro is intended for
+noninteractive use. As such, BODY should not invoke
+`keyboard-quit' under normal operation, and KEYS should not
+include C-g, or this macro will interpret it as reading past the
+end of input."
+  ;; It would be better to detect end-of-input by overriding
+  ;; `read-event' to throw an error, since theoretically C-g could be
+  ;; rebound to something other than `keyboard-quit'. But apparently
+  ;; some functions read input directly in C code, and redefining
+  ;; `read-event' has no effect on those. So the suboptimal solution
+  ;; is to rely on C-g.
+  (declare (indent 1))
+  `(let* ((key-sequence (listify-key-sequence (kbd ,keys)))
+          (C-g-key-sequence
+           (listify-key-sequence
+            ;; We *really* want to trigger `keyboard-quit' if we reach
+            ;; the end of the input.
+            (kbd "C-g C-g C-g C-g C-g C-g C-g")))
+          (unread-command-events
+           (append key-sequence C-g-key-sequence)))
+     (when (member (car C-g-key-sequence) key-sequence)
+       (error "KEYS must include C-g"))
+     (condition-case nil
+         ,@body
+       (quit
+        (error "Reached end of simulated input while evaluating body")))))
+
+(defmacro with-mode (mode arg &rest body)
+  "Eval (MODE ARG), then body, then restore previous status of MODE.
+
+This will only work on modes that respect the normal conventions
+for activation and deactivation."
+  (declare (indent 2))
+  (let* ((orig-status (eval mode))
+         (restore-arg (if orig-status 1 0)))
+    `(unwind-protect
+         (progn
+           (,mode ,arg)
+           ,@body)
+       (,mode ,restore-arg))))
+
+(ert-deftest ido-ubiquitous-test ()
+  "Do some ido-ubiquitous tests"
+  (with-mode ido-ubiquitous-mode 1
+    (should
+     (string=
+      "green"
+      (with-simulated-input "g RET"
+        (completing-read "Prompt: " '("blue" "yellow" "green")))))
+    (should
+     (string=
+      "g"
+      (with-simulated-input "g C-j"
+        (completing-read "Prompt: " '("blue" "yellow" "green"))))))
+  ;; Normal completing-read
+  (with-mode ido-ubiquitous-mode 0
+    ;; No match required
+    (should
+     (string=
+      "g"
+      (with-simulated-input "g RET"
+        (completing-read "Prompt: " '("blue" "yellow" "green")))))
+    ;; Match required, so input should be incomplete
+    (should-error
+     (with-simulated-input "g RET"
+       (completing-read "Prompt: " '("blue" "yellow" "green") nil 'require-match))
+     :type 'error))))
+
+(provide 'ido-ubiquitous-tests)
+
+;;; ido-ubiquitous-tests.el ends here