瀏覽代碼

Switch from ERT to buttercup test suite

Ryan C. Thompson 8 年之前
父節點
當前提交
ab3f4c65f5
共有 4 個文件被更改,包括 566 次插入512 次删除
  1. 1 1
      .travis.yml
  2. 5 1
      Cask
  3. 0 510
      test/ido-cr+-test.el
  4. 560 0
      tests/test-ido-completing-read+.el

+ 1 - 1
.travis.yml

@@ -30,4 +30,4 @@ before_script:
   - cask install
   - cask install
 
 
 script:
 script:
-  cask exec ert-runner -L .
+  cask exec buttercup -L .

+ 5 - 1
Cask

@@ -11,4 +11,8 @@
  (depends-on "with-simulated-input"
  (depends-on "with-simulated-input"
              :git "https://github.com/DarwinAwardWinner/with-simulated-input.git"
              :git "https://github.com/DarwinAwardWinner/with-simulated-input.git"
              :files ("*.el"))
              :files ("*.el"))
- (depends-on "ert-runner"))
+ (depends-on "buttercup"
+             :git "https://github.com/DarwinAwardWinner/emacs-buttercup.git"
+             :branch "show-exprs"
+             :files ("*.el"
+                     ("bin" "bin/*"))))

+ 0 - 510
test/ido-cr+-test.el

@@ -1,510 +0,0 @@
-;;; -*- lexical-binding: t -*-
-
-(require 'ido-completing-read+)
-(require 'ert)
-(require 'cl-lib)
-(require 'with-simulated-input)
-
-(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 ,mode)
-          (restore-arg (if orig-status 1 0)))
-     (unwind-protect
-         (progn
-           (,mode ,arg)
-           ,@body)
-       (,mode restore-arg))))
-
-(defmacro with-ido-cr+-standard-env (&rest body)
-  "Execute BODY with standard ido-cr+ settings.
-
-All ido-cr+ options will be let-bound to their default values,
-and `ido-ubiquitous-mode' will be enabled. The values will all br
-restored to what they were previously after BODY exits."
-  (declare (indent 0))
-  (let*
-      ((options
-        '(ido-cr+-debug-mode
-          ido-cr+-auto-update-blacklist
-          ido-cr+-fallback-function
-          ido-cr+-max-items
-          ido-cr+-function-blacklist
-          ido-cr+-function-whitelist
-          ido-cr+-replace-completely
-          ido-confirm-unique-completion
-          ido-enable-flex-matching))
-       (bindings
-        (cl-loop for var in options collect
-                 (list var
-                       (list 'quote
-                             (eval (car (get var 'standard-value))))))))
-    `(with-mode ido-ubiquitous-mode 1
-       (let ,bindings ,@body))))
-
-(defmacro collection-as-function (collection)
-  "Return a function equivalent to COLLECTION.
-
-The returned function will work equivalently to COLLECTION when
-passed to `all-completions' and `try-completion'."
-  `(completion-table-dynamic (lambda (string) (all-completions string ,collection))))
-
-(ert-deftest ido-cr+-test-basic ()
-  :tags '(ido ido-cr+)
-  "Test that basic ido-cr+ functionality is working."
-  (with-ido-cr+-standard-env
-    ;; Verify that pressing RET selects a matching item
-    (should
-     (string=
-      "green"
-      (with-simulated-input "g RET"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))))
-
-    ;; Verify that pressing RET with multiple matches selects the
-    ;; first one
-    (should
-     (string=
-      "brown"
-      (with-simulated-input "b RET"
-        (ido-completing-read+ "Prompt: " '("brown" "blue" "yellow" "green")))))
-
-    ;; Verify that cycling with <left> and <right> works, including
-    ;; wrap-around
-    (should
-     (string=
-      "blue"
-      (with-simulated-input "b <right> <right> <right> <right> <left> RET"
-        (ido-completing-read+ "Prompt: " '("brown" "blue" "yellow" "green")))))
-
-    ;; Verify that RET or C-j on empty input returns "" when
-    ;; REQUIRE-MATCH is t
-    (should
-     (string=
-      ""
-      (with-simulated-input "RET"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t))))
-    (should
-     (string=
-      ""
-      (with-simulated-input "C-j"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t))))
-
-    ;; Verify that DEF works, whether or not it is an element of
-    ;; COLLECTION
-    (should
-     (string=
-      "green"
-      (with-simulated-input "RET"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil "green"))))
-    (should
-     (string=
-      "green"
-      (with-simulated-input "RET"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t nil nil "green"))))
-    (should
-     (string=
-      "brown"
-      (with-simulated-input "RET"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "brown") nil nil nil nil "brown"))))
-    (should
-     (string=
-      "brown"
-      (with-simulated-input "RET"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "brown") nil t nil nil "brown"))))
-    (should
-     (string=
-      "brown"
-      (with-simulated-input "RET"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "brown") nil t nil nil '("brown" "green")))))
-
-    ;; Verify that INITIAL-INPUT works
-    (should
-     (string=
-      "green"
-      (with-simulated-input "RET"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr"))))
-
-    ;; Verify that INITIAL-INPUT and DEF don't interfere with each other
-    (should
-     (string=
-      "green"
-      (with-simulated-input "RET"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr" nil "blue"))))
-    (should
-     (string=
-      "blue"
-      (with-simulated-input "DEL DEL RET"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr" nil "blue"))))
-
-    ;; Verify that ido-cr+ works on function collections
-    (should
-     (string=
-      "green"
-      (with-simulated-input "g RET"
-        (ido-completing-read+ "Prompt: " (collection-as-function '("blue" "yellow" "green"))))))))
-
-(ert-deftest ido-cr+-mode-activation ()
-  :tags '(ido ido-cr+)
-  "Test whether ido-ubiquitous-mode can be turned on and off."
-  (with-ido-cr+-standard-env
-    (cl-letf (((symbol-function 'test-command)
-               (lambda ()
-                 (interactive)
-                 (completing-read "Prompt: " '("blue" "yellow" "green")))))
-      ;; Verify that the mode can be activated
-      (should
-       (string=
-        "green"
-        (with-mode ido-ubiquitous-mode 1
-          (with-simulated-input "g RET"
-            (call-interactively 'test-command)))))
-      ;; Verify that the mode can be deactivated
-      (should
-       (string=
-        "g"
-        (with-mode ido-ubiquitous-mode 0
-          (with-simulated-input "g RET"
-            (call-interactively 'test-command))))))))
-
-(ert-deftest ido-cr+-test-maxitems ()
-  :tags '(ido ido-cr+)
-  "Test whether the large-collection fallback works."
-  (with-ido-cr+-standard-env
-    ;; This should not fall back
-    (should
-     (string=
-      "green"
-      (with-simulated-input "g RET"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))))
-    (let ((ido-cr+-max-items -1))
-      ;; This should fall back because the collection is too large
-      (should
-       (string=
-        "g"
-        (with-simulated-input "g RET"
-          (ido-completing-read+ "Prompt: " '("blue" "yellow" "green"))))))))
-
-(ert-deftest ido-cr+-test-blacklist ()
-  :tags '(ido ido-cr+)
-  "Test whether `ido-ubiquitous-function-blacklist' works."
-  (with-ido-cr+-standard-env
-    (cl-letf (((symbol-function 'blacklisted-command)
-               (lambda (arg)
-                 (interactive (list (completing-read "Prompt: " '("blue" "yellow" "green"))))
-                 arg))
-              ((symbol-function 'blacklisted-function)
-               (lambda ()
-                 (completing-read "Prompt: " '("blue" "yellow" "green"))))
-              ((symbol-function 'cmd-that-calls-blacklisted-function)
-               (lambda ()
-                 (interactive)
-                 (funcall 'blacklisted-function)))
-              ((symbol-function 'blacklisted-collection)
-               (collection-as-function '("blue" "yellow" "green"))))
-      ;; First verify that they work normally before blacklisting them
-      (should
-       (string=
-        "green"
-        (with-simulated-input "g RET"
-          (call-interactively 'blacklisted-command))))
-      (should
-       (string=
-        "green"
-        (with-simulated-input "g RET"
-          (call-interactively 'cmd-that-calls-blacklisted-function))))
-      (should
-       (string=
-        "green"
-        (with-simulated-input "g RET"
-          (ido-completing-read+ "Prompt: " 'blacklisted-collection))))
-      ;; Now add them to the blacklist and try again
-      (let ((ido-cr+-function-blacklist
-             (append '(blacklisted-command
-                       blacklisted-function
-                       blacklisted-collection)
-                     ido-cr+-function-blacklist)))
-        ;; All should now have ido-cr+ disabled
-        (should
-         (string=
-          "g"
-          (with-simulated-input "g RET"
-            (call-interactively 'blacklisted-command))))
-        (should
-         (string=
-          "g"
-          (with-simulated-input "g RET"
-            (call-interactively 'cmd-that-calls-blacklisted-function))))
-        (should
-         (string=
-          "g"
-          (with-simulated-input "g RET"
-            (ido-completing-read+ "Prompt: " 'blacklisted-collection))))))))
-
-(ert-deftest ido-cr+-test-whitelist ()
-  :tags '(ido ido-cr+)
-  "Test whether `ido-ubiquitous-function-whitelist' works."
-  (with-ido-cr+-standard-env
-    (cl-letf (((symbol-function 'whitelisted-command)
-               (lambda (arg)
-                 (interactive (list (completing-read "Prompt: " '("blue" "yellow" "green"))))
-                 arg))
-              ((symbol-function 'whitelisted-function)
-               (lambda ()
-                 (completing-read "Prompt: " '("blue" "yellow" "green"))))
-              ((symbol-function 'cmd-that-calls-whitelisted-function)
-               (lambda ()
-                 (interactive)
-                 (funcall 'whitelisted-function)))
-              ((symbol-function 'whitelisted-collection)
-               (lambda (string pred action)
-                 (complete-with-action action '("blue" "yellow" "green") string pred))))
-      ;; Now add them to the whitelist
-      (let ((ido-cr+-function-whitelist
-             (append '(whitelisted-command
-                       whitelisted-function
-                       whitelisted-collection)
-                     ido-cr+-function-whitelist)))
-        ;; All should now have ido-cr+ enabled
-        (should
-         (string=
-          "green"
-          (with-simulated-input "g RET"
-            (call-interactively 'whitelisted-command))))
-        (should
-         (string=
-          "green"
-          (with-simulated-input "g RET"
-            (call-interactively 'cmd-that-calls-whitelisted-function))))
-        (should
-         (string=
-          "green"
-          (with-simulated-input "g RET"
-            (ido-completing-read+ "Prompt: " 'whitelisted-collection)))))
-      ;; Run again with nothing whitelisted
-      (let ((ido-cr+-function-whitelist '(nil)))
-        ;; All should now have ido-cr+ disabled
-        (should
-         (string=
-          "g"
-          (with-simulated-input "g RET"
-            (call-interactively 'whitelisted-command))))
-        (should
-         (string=
-          "g"
-          (with-simulated-input "g RET"
-            (call-interactively 'cmd-that-calls-whitelisted-function))))
-        (should
-         (string=
-          "g"
-          (with-simulated-input "g RET"
-            (ido-completing-read+ "Prompt: " 'whitelisted-collection))))))))
-
-(ert-deftest ido-cr+-require-match ()
-  :tags '(ido ido-cr+)
-  "Test whether REQUIRE-MATCH and DEF work as expected together."
-  (with-ido-cr+-standard-env
-    ;; "C-j" should be allowed to return an empty string even if
-    ;; require-match is non-nil, as long as default is nil
-    (should
-     (string=
-      ""
-      (with-simulated-input "C-j"
-        (ido-completing-read+
-         "Prompt: "
-         '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))))
-    ;; "C-j" should NOT be allowed to return an empty string if
-    ;; require-match and default are both non-nil.
-    (should-error
-     (with-simulated-input "C-j"
-       (ido-completing-read+
-        "Prompt: "
-        '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t nil nil "yellow")))
-    ;; Multiple presses of C-j won't just select the first match
-    (should-error
-     (with-simulated-input "b C-j C-j C-j"
-       (ido-completing-read+
-        "Prompt: "
-        '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t)))
-    ;; First press of C-j should complete unique common prefix after the
-    ;; first b, but then get stuck on the choice for the second b.
-    (should-error
-     (with-simulated-input "b C-j b C-j C-j"
-       (ido-completing-read+
-        "Prompt: "
-        '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t)))
-    ;; This should complete to "blueberry" via 2 rounds of unique common
-    ;; prefix completion, and then return on the 3rd "C-j"
-    (should
-     (string=
-      "blueberry"
-      (with-simulated-input "b C-j b C-j e C-j C-j"
-        (ido-completing-read+
-         "Prompt: "
-         '("bluebird" "blues" "bluegrass" "blueberry" "yellow" "green") nil t))))
-    ;; The "C-j" should complete to "bluegrass" and return, because
-    ;; `ido-confirm-unique-completion is nil.
-    (should
-     (string=
-      "bluegrass"
-      (with-simulated-input "b l u e g C-j"
-        (ido-completing-read+
-         "Prompt: "
-         '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))))
-    (let ((ido-confirm-unique-completion t))
-      ;; Now the first "C-j" should complete to "bluegrass" but should
-      ;; not return.
-      (should-error
-       (with-simulated-input "b l u e g C-j"
-         (ido-completing-read+
-          "Prompt: "
-          '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t)))
-      ;; The first "C-j" should complete to "bluegrass", and the second
-      ;; should return.
-      (should
-       (string=
-        "bluegrass"
-        (with-simulated-input "b l u e g C-j C-j"
-          (ido-completing-read+
-           "Prompt: "
-           '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t)))))
-    ;; Finally, a few tests for the expected wrong behavior without
-    ;; ido-cr+. If ido.el ever fixes this bug, it will cause this test
-    ;; to fail as a signal that the workaround can be phased out.
-    (should
-     (string=
-      ""
-      (with-simulated-input "C-j"
-        (ido-completing-read
-         "Prompt: "
-         '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))))
-    (should
-     (string=
-      "b"
-      (with-simulated-input "b C-j"
-        (ido-completing-read
-         "Prompt: "
-         '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))))))
-
-(ert-deftest ido-cr+-test-fallback ()
-  :tags '(ido ido-cr+)
-  "Test whether manually invoking fallback works."
-  (with-ido-cr+-standard-env
-    (should
-     ;; C-b/f not at beginning/end of input should not fall back
-     (string=
-      "green"
-      (with-simulated-input "g C-b C-f RET"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))))
-    (should
-     ;; C-f at end of input should fall back
-     (string=
-      "g"
-      (with-simulated-input "g C-f RET"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))))
-    (should
-     ;; Repeated C-b should not fall back
-     (string=
-      "green"
-      (with-simulated-input "g C-b C-b C-b C-b RET"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))))
-    (should
-     ;; C-b at beginning of line should fall back (if previous action
-     ;; was not also C-b)
-     (string=
-      "g"
-      (with-simulated-input "g C-b x DEL C-b RET"
-        (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))))))
-
-(ert-deftest ido-cr+-dot-prefix-empty-string ()
-  :tags '(ido ido-cr+)
-  "Test whether ido-ubiquitous successfully works around a bug in ido.
-
-See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=26997 for more
-information on this bug."
-  (with-ido-cr+-standard-env
-    (let ((ido-enable-dot-prefix t))
-      (should
-       (string=
-        ""
-        (with-simulated-input "RET"
-          (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac")))))
-      (should
-       (string=
-        "aab"
-        (with-simulated-input "a a b RET"
-          (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac"))))))))
-
-(defvar mycollection)
-
-(ert-deftest ido-cr+-dynamic-collection ()
-  :tags '(ido ico=cr+)
-  "Test whether dynamic collection updating works."
-  (with-ido-cr+-standard-env
-    (let ((ido-enable-flex-matching t)
-          (mycollection
-           (completion-table-dynamic
-            (lambda (text)
-              (cond
-               ;; Sub-completions for "hello"
-               ((s-prefix-p "hello" text)
-                '("hello" "hello-world" "hello-everyone" "hello-universe"))
-               ;; Sub-completions for "goodbye"
-               ((s-prefix-p "goodbye" text)
-                '("goodbye" "goodbye-world" "goodbye-everyone" "goodbye-universe"))
-               ;; General completions
-               (t
-                '("hello" "goodbye" "helicopter" "goodness")))))))
-      (should
-       (string=
-        (with-simulated-input "hello- RET"
-          (completing-read "Say something: " mycollection))
-        "hello-world"))
-      ;; Flex-matching should work in dynamic collections
-      (should
-       (string=
-        (with-simulated-input "hello-ld RET"
-          (completing-read "Say something: " mycollection))
-        "hello-world"))
-      ;; TAB should do a dynamic update, and if the update makes the
-      ;; completion no longer unique, it should not exit when
-      ;; `ido-confirm-unique-completion' is non-nil
-      (should
-       (string=
-        (with-simulated-input '("hell TAB <right> RET")
-          (completing-read "Say something: " mycollection))
-        "hello-world"))
-      ;; But if the completion is unique after updating, then it should exit
-      (should
-       (string=
-        (with-simulated-input '("heli TAB")
-          (completing-read "Say something: " mycollection))
-        "helicopter"))
-      ;; Waiting idly should do a dynamic update
-      (should
-       (string=
-        (with-simulated-input '("hello"
-                                (wsi-simulate-idle-time (1+ ido-cr+-dynamic-update-idle-time)) "<right> RET")
-          (completing-read "Say something: " mycollection))
-        "hello-world"))
-      ;; Dynamic update should optimistically check the first
-      ;; available match for extended completions. ("hell" causes
-      ;; "hello" to be the first and only match, which causes an
-      ;; immediate update that checks "hello" for completions even
-      ;; though it hasn't been typed in yet, which makes "hello-world"
-      ;; available, which is flex-matched by the "ld".)
-      (should
-       (string=
-        (with-simulated-input '("hellld RET")
-          (completing-read "Say something: " mycollection))
-        "hello-world")))))
-
-(defun ido-cr+-run-all-tests ()
-  (interactive)
-  (ert "^ido-cr\\+-"))
-
-(provide 'ido-ubiquitous-test)
-
-;;; ido-ubiquitous-test.el ends here

+ 560 - 0
tests/test-ido-completing-read+.el

@@ -0,0 +1,560 @@
+;;; -*- lexical-binding: t -*-
+
+(require 'ido-completing-read+)
+(require 'buttercup)
+(require 'cl-lib)
+(require 'with-simulated-input)
+
+(defun collection-as-function (collection)
+  "Return a function equivalent to COLLECTION.
+
+The returned function will work equivalently to COLLECTION when
+passed to `all-completions' and `try-completion'."
+  (completion-table-dynamic (lambda (string) (all-completions string collection))))
+
+(defun ido-cr+-test-save-custom-vars (vars)
+  (cl-loop
+   for var in vars
+   if (not (custom-variable-p var))
+   do (error "Variable `%s' is not a customizable variable" var)
+   for curval = (symbol-value var)
+   for stdval = (eval (car (get var 'standard-value)))
+   for setter = (or (get var 'custom-set) 'set-default)
+   do
+   (progn
+     ;; Save the current value
+     (put var 'ido-cr+-test-saved-value curval)
+     ;; Set it to the standard value, using it's custom setter
+     ;; function
+     (funcall setter var stdval))))
+
+(defun ido-cr+-test-restore-custom-vars (vars)
+  (cl-loop
+   for var in vars
+   for savedval = (get var 'ido-cr+-test-saved-value)
+   for setter = (or (get var 'custom-set) 'set-default)
+   do
+   (progn
+     ;; Set it to the saved value, using it's custom setter function
+     (funcall setter var savedval)
+     ;; Delete the saved value from the symbol plist
+     (put var 'ido-cr+-test-saved-value nil))))
+
+(describe "Within the `ido-completing-read+' package"
+
+  ;; All these need to be saved before and restored after each each test
+  :var (ido-mode
+        ido-ubiquitous-mode
+        ido-cr+-debug-mode
+        ido-cr+-auto-update-blacklist
+        ido-cr+-fallback-function
+        ido-cr+-max-items
+        ido-cr+-function-blacklist
+        ido-cr+-function-whitelist
+        ido-cr+-replace-completely
+        ido-confirm-unique-completion
+        ido-enable-flex-matching)
+
+  ;; Reset all of these variables to their standard values before each
+  ;; test
+  (before-each
+    (ido-cr+-test-save-custom-vars
+     '(ido-mode
+       ido-ubiquitous-mode
+       ido-cr+-debug-mode
+       ido-cr+-auto-update-blacklist
+       ido-cr+-fallback-function
+       ido-cr+-max-items
+       ido-cr+-function-blacklist
+       ido-cr+-function-whitelist
+       ido-cr+-replace-completely
+       ido-confirm-unique-completion
+       ido-enable-flex-matching))
+    ;; Now enable ido-mode and ido-ubiquitous-mode
+    (ido-mode 1)
+    (ido-ubiquitous-mode 1))
+
+  ;; Restore the saved value after each test
+  (after-each
+    (ido-cr+-test-restore-custom-vars
+     '(ido-mode
+       ido-ubiquitous-mode
+       ido-cr+-debug-mode
+       ido-cr+-auto-update-blacklist
+       ido-cr+-fallback-function
+       ido-cr+-max-i
+       tems
+       ido-cr+-function-blacklist
+       ido-cr+-function-whitelist
+       ido-cr+-replace-completely
+       ido-confirm-unique-completion
+       ido-enable-flex-matching)))
+
+  (describe "the `ido-completing-read+' function"
+
+    (it "should complete with a matching item on RET"
+      (expect
+       (with-simulated-input "g RET"
+         (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
+       :to-equal "green"))
+    (it "should complete with the first match when multiple matches are available"
+      (expect
+       (with-simulated-input "b RET"
+         (ido-completing-read+ "Prompt: " '("brown" "blue" "yellow" "green")))
+       :to-equal "brown"))
+    (it "should allow <left> and <right> to cycle completions, with wrap-around"
+      (expect
+       (with-simulated-input "b <right> <right> <right> <right> <left> RET"
+         (ido-completing-read+ "Prompt: " '("brown" "blue" "yellow" "green")))
+       :to-equal
+       "blue"))
+
+    (it "should return \"\" when RET or C-j is pressed on an empty input even when REQUIRE-MATCH is non-nil"
+      ;; No REQUIRE-MATCH
+      (expect
+       (with-simulated-input "RET"
+         (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
+       :to-equal "blue")
+      (expect
+       (with-simulated-input "C-j"
+         (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
+       :to-equal "")
+      ;; Again, with REQUIRE-MATCH
+      (expect
+       (with-simulated-input "RET"
+         (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t))
+       :to-equal "")
+      (expect
+       (with-simulated-input "C-j"
+         (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t))
+       :to-equal ""))
+
+    ;; Verify that DEF works, whether or not it is an element of
+    ;; COLLECTION
+    (it "should accept all the same forms of DEF as `completing-read-default'"
+      (expect
+       (with-simulated-input "RET"
+         (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil "green"))
+       :to-equal "green")
+      (expect
+       (with-simulated-input "RET"
+         (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t nil nil "green"))
+       :to-equal "green")
+      (expect
+       (with-simulated-input "RET"
+         (ido-completing-read+ "Prompt: " '("blue" "yellow" "brown") nil nil nil nil "brown"))
+       :to-equal "brown")
+      (expect
+       (with-simulated-input "RET"
+         (ido-completing-read+ "Prompt: " '("blue" "yellow" "brown") nil t nil nil "brown"))
+       :to-equal "brown")
+      (expect
+       (with-simulated-input "RET"
+         (ido-completing-read+ "Prompt: " '("blue" "yellow" "brown") nil t nil nil '("brown" "green")))
+       :to-equal "brown"))
+
+    ;; Verify that INITIAL-INPUT works
+    (it "should work with INITIAL-INPUT"
+      (expect
+       (with-simulated-input "RET"
+         (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr"))
+       :to-equal "green"))
+
+    ;; Verify that INITIAL-INPUT and DEF don't interfere with each other
+    (it "should properly handle both INITIAL-INPUT and DEF at the same time"
+      (expect
+       (with-simulated-input "RET"
+         (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr" nil "blue"))
+       :to-equal "green")
+      (expect
+       (with-simulated-input "DEL DEL RET"
+         (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr" nil "blue"))
+       :to-equal "blue"))
+
+    ;; Verify that ido-cr+ works on function collections
+    (it "should work when COLLECTION is a function"
+      (expect
+       (with-simulated-input "g RET"
+         (ido-completing-read+ "Prompt: " (collection-as-function '("blue" "yellow" "green"))))
+       :to-equal "green"))
+
+    (describe "when `ido-cr+-max-items' is set"
+      (it "should not trigger a fallback for small collections"
+        (expect
+         (with-simulated-input "g RET"
+           (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
+         :to-equal "green"))
+      (it "should trigger a fallback for large collections"
+        (expect
+         ;; With max-items negative, all collections are considered "too
+         ;; large"
+         (let ((ido-cr+-max-items -1))
+           (with-simulated-input "g RET"
+             (ido-completing-read+ "Prompt: " '("blue" "yellow" "green"))))
+         :to-equal "g")))
+
+    (describe "when REQUIRE-MATCH is non-nil"
+      (it "should still allow exiting with an empty string if DEF is nil"
+        (expect
+         (with-simulated-input "C-j"
+           (ido-completing-read+
+            "Prompt: "
+            '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
+         :to-equal ""))
+      ;; "C-j" should NOT be allowed to return an empty string if
+      ;; require-match and default are both non-nil.
+      (it "should not alow exiting with an empty string if DEF is non-nil"
+        (expect
+         (lambda ()
+           (with-simulated-input "C-j"
+             (ido-completing-read+
+              "Prompt: "
+              '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t nil nil "yellow")))
+         :to-throw))
+      (it "shouldn't allow C-j to select an ambiguous match"
+        (expect
+         (lambda ()
+           (with-simulated-input "b C-j C-j C-j"
+             (ido-completing-read+
+              "Prompt: "
+              '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t)))
+         :to-throw)
+        ;; First press of C-j should complete to "blue" after the
+        ;; first b, but then get stuck on the choice for the second b.
+        (expect
+         (lambda ()
+           (with-simulated-input "b C-j b C-j C-j"
+             (ido-completing-read+
+              "Prompt: "
+              '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t)))
+         :to-throw))
+      (it "should allow exiting with an unambiguous match"
+        (expect
+         (with-simulated-input "b C-j b C-j e C-j C-j"
+           (ido-completing-read+
+            "Prompt: "
+            '("bluebird" "blues" "bluegrass" "blueberry" "yellow" "green") nil t))
+         :to-equal "blueberry")
+        ;; The "C-j" should complete to "bluegrass" and return, because
+        ;; `ido-confirm-unique-completion is nil.
+        (expect
+         (with-simulated-input "b l u e g C-j"
+           (ido-completing-read+
+            "Prompt: "
+            '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
+         :to-equal "bluegrass"))
+      (it "should require an extra C-j to exit when `ido-confirm-unique-completion' is non-nil"
+        (setq ido-confirm-unique-completion t)
+        ;; Now the first "C-j" should complete to "bluegrass" but should
+        ;; not return.
+        (expect
+         (lambda ()
+           (with-simulated-input "b l u e g C-j"
+             (ido-completing-read+
+              "Prompt: "
+              '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t)))
+         :to-throw)
+        ;; The first "C-j" should complete to "bluegrass", and the second
+        ;; should return.
+        (expect
+         (with-simulated-input "b l u e g C-j C-j"
+           (ido-completing-read+
+            "Prompt: "
+            '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
+         :to-equal "bluegrass"))
+
+      ;; Finally, a test for the expected wrong behavior without
+      ;; ido-cr+. If ido.el ever fixes this bug, it will cause this test
+      ;; to fail as a signal that the workaround can be phased out.
+      (it "should return a non-match when ordinary `ido-completing-read' is used"
+        (expect
+         (with-simulated-input "b C-j"
+           (ido-completing-read
+            "Prompt: "
+            '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))
+         :to-equal "b")))
+
+    (describe "with manual fallback shortcuts"
+      (it "should not fall back when C-b or C-f is used in the middle of the input"
+        (expect
+         ;; C-b/f not at beginning/end of input should not fall back
+         (with-simulated-input "g C-b C-f RET"
+           (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
+         :to-equal "green"))
+      (it "should fall back on C-f at end of input"
+        (expect
+         ;; C-f at end of input should fall back
+         (with-simulated-input "g C-f RET"
+           (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
+         :to-equal "g"))
+      (it "should not fall back from repeated C-b that hits the start of input"
+        (expect
+         ;; Repeated C-b should not fall back
+         (with-simulated-input "g C-b C-b C-b C-b RET"
+           (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
+         :to-equal "green"))
+      (it "should fall back on C-b at beginning of input (if previous action was not C-b)"
+        (expect
+         ;; C-b at beginning of line should fall back (if previous action
+         ;; was not also C-b)
+         (with-simulated-input "g C-b x DEL C-b RET"
+           (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))
+         :to-equal "g")))
+
+    (describe "with a work workaround for an bug with `ido-enable-dot-prefix'"
+      ;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=26997
+      ;; for more information on this bug.
+      (before-each
+        (setq ido-enable-dot-prefix t))
+      (it "should not throw an error when `ido-enable-dot-prefix' is non-nil and \"\" is in the collection"
+        (expect
+         (with-simulated-input "RET"
+           (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac")))
+         :to-equal "")
+        (expect
+         (with-simulated-input "a a b RET"
+           (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac")))
+         :to-equal "aab")))
+
+    (describe "with dynamic collections"
+      (before-all
+        (setq my-dynamic-collection
+              (completion-table-dynamic
+               (lambda (text)
+                 (cond
+                  ;; Sub-completions for "hello"
+                  ((s-prefix-p "hello" text)
+                   '("hello" "hello-world" "hello-everyone" "hello-universe"))
+                  ;; Sub-completions for "goodbye"
+                  ((s-prefix-p "goodbye" text)
+                   '("goodbye" "goodbye-world" "goodbye-everyone" "goodbye-universe"))
+                  ;; General completions
+                  (t
+                   '("hello" "goodbye" "helicopter" "helium" "goodness" "goodwill")))))))
+      (after-all
+        (setq my-dynamic-collection nil))
+      (before-each
+        (setq ido-enable-flex-matching t
+              ido-confirm-unique-completion nil))
+
+      (it "should allow selection of dynamically-added completions"
+        (expect
+         (with-simulated-input "hello- RET"
+           (ido-completing-read+ "Say something: " my-dynamic-collection))
+         :to-equal "hello-world"))
+
+      (it "should allow ido flex-matching of dynamically-added completions"
+        (expect
+         (with-simulated-input "hello-ld RET"
+           (ido-completing-read+ "Say something: " my-dynamic-collection))
+         :to-equal
+         "hello-world"))
+      (it "should do a dynamic update when pressing TAB"
+        (expect
+         (with-simulated-input "h TAB -ld RET"
+           (ido-completing-read+ "Say something: " my-dynamic-collection))
+         :to-equal
+         "hello-world"))
+      (it "should do a dynamic update when idle"
+        (expect
+         (with-simulated-input
+             '("h"
+               (wsi-simulate-idle-time (1+ ido-cr+-dynamic-update-idle-time))
+               "-ld RET")
+           (ido-completing-read+ "Say something: " my-dynamic-collection))
+         :to-equal
+         "hello-world"))
+      (it "should do a dynamic update when there is only one match remaining"
+        (expect
+         (with-simulated-input "hell-ld RET"
+           (ido-completing-read+ "Say something: " my-dynamic-collection))
+         :to-equal
+         "hello-world"))
+      (it "should not exit with a unique match if new matches are dynamically added"
+        (expect
+         (with-simulated-input '("hell TAB -ld RET")
+           (ido-completing-read+ "Say something: " my-dynamic-collection))
+         :to-equal
+         "hello-world"))
+      (it "should exit with a match that is still unique after dynamic updating"
+        (expect
+         (with-simulated-input '("helic TAB")
+           (ido-completing-read+ "Say something: " my-dynamic-collection))
+         :to-equal
+         "helicopter"))))
+
+  (describe "ido-ubiquitous-mode"
+    ;; Set up a test command that calls `completing-read'
+    (before-all
+      (setf (symbol-function 'test-command)
+            (lambda ()
+              (interactive)
+              (completing-read "Prompt: " '("blue" "yellow" "green")))))
+    ;; Delete the test command
+    (after-all
+      (setf (symbol-function 'test-command) nil))
+
+    ;; Verify that the mode can be activated
+    (it "should enable itself properly"
+      (expect
+       (progn
+         (ido-ubiquitous-mode 1)
+         (with-simulated-input "g RET"
+           (command-execute 'test-command)))
+       :to-equal "green"))
+    (it "should disable itself properly"
+      (expect
+       (progn
+         (ido-ubiquitous-mode 0)
+         (with-simulated-input "g RET"
+           (command-execute 'test-command)))
+       :to-equal "g"))
+
+    (describe "with `ido-cr+-function-blacklist'"
+      (before-all
+        (setf (symbol-function 'blacklisted-command)
+              (lambda (arg)
+                (interactive (list (completing-read "Prompt: " '("blue" "yellow" "green"))))
+                arg)
+              (symbol-function 'blacklisted-function)
+              (lambda ()
+                (completing-read "Prompt: " '("blue" "yellow" "green")))
+              (symbol-function 'cmd-that-calls-blacklisted-function)
+              (lambda ()
+                (interactive)
+                (funcall 'blacklisted-function))
+              (symbol-function 'blacklisted-collection)
+              (collection-as-function '("blue" "yellow" "green"))))
+      (after-all
+        (setf (symbol-function 'blacklisted-command) nil
+              (symbol-function 'blacklisted-function) nil
+              (symbol-function 'cmd-that-calls-blacklisted-function) nil
+              (symbol-function 'blacklisted-collection) nil))
+      ;; First verify that they work normally before blacklisting them
+      (describe "when the blacklist is empty"
+        (it "should not affect a non-blacklisted command"
+          (expect
+           (with-simulated-input "g RET"
+             (call-interactively 'blacklisted-command))
+           :to-equal "green"))
+        (it "should not affect a non-blacklisted function"
+          (expect
+           (with-simulated-input "g RET"
+             (call-interactively 'cmd-that-calls-blacklisted-function))
+           :to-equal "green"))
+        (it "should not affect a non-blacklisted collection"
+          (expect
+           (with-simulated-input "g RET"
+             (ido-completing-read+ "Prompt: " 'blacklisted-collection))
+           :to-equal "green")))
+
+      (describe "when the specified functions are blacklisted"
+        (before-each
+          (setq ido-cr+-function-blacklist
+                (append '(blacklisted-command
+                          blacklisted-function
+                          blacklisted-collection)
+                        ido-cr+-function-blacklist)))
+        (it "should prevent ido in a blacklisted command"
+          (expect
+           (with-simulated-input "g RET"
+             (call-interactively 'blacklisted-command))
+           :to-equal "g"))
+        (it "should prevent ido in a blacklisted function"
+          (expect
+           (with-simulated-input "g RET"
+             (call-interactively 'cmd-that-calls-blacklisted-function))
+           :to-equal "g"))
+        (it "should prevent ido with a blacklisted collection"
+          (expect
+           (with-simulated-input "g RET"
+             (ido-completing-read+ "Prompt: " 'blacklisted-collection))
+           :to-equal "g"))))
+
+    (describe "with `ido-cr+-function-whitelist'"
+      (before-all
+        (setf (symbol-function 'whitelisted-command)
+              (lambda (arg)
+                (interactive
+                 (list
+                  (completing-read "Prompt: " '("blue" "yellow" "green"))))
+                arg)
+              (symbol-function 'whitelisted-function)
+              (lambda ()
+                (completing-read "Prompt: " '("blue" "yellow" "green")))
+              (symbol-function 'cmd-that-calls-whitelisted-function)
+              (lambda ()
+                (interactive)
+                (funcall 'whitelisted-function))
+              (symbol-function 'whitelisted-collection)
+              (lambda (string pred action)
+                (complete-with-action action '("blue" "yellow" "green") string pred))))
+      (after-all
+        (setf (symbol-function 'whitelisted-command) nil
+              (symbol-function 'whitelisted-function) nil
+              (symbol-function 'cmd-that-calls-whitelisted-function) nil
+              (symbol-function 'whitelisted-collection) nil))
+      (describe "when the whitelist is inactive (i.e. everything is whitelisted)"
+        (before-each
+          (setq ido-cr+-function-whitelist nil))
+        (it "should enable ido in a command"
+          (expect
+           (with-simulated-input "g RET"
+             (call-interactively 'whitelisted-command))
+           :to-equal "green"))
+        (it "should enable ido in a function"
+          (expect
+           (with-simulated-input "g RET"
+             (call-interactively 'cmd-that-calls-whitelisted-function))
+           :to-equal "green"))
+        (it "should enable ido for a collection"
+          (expect
+           (with-simulated-input "g RET"
+             (ido-completing-read+ "Prompt: " 'whitelisted-collection))
+           :to-equal "green")))
+      (describe "when the specified functions are whitelisted"
+        (before-each
+          (setq ido-cr+-function-whitelist
+                (append '(whitelisted-command
+                          whitelisted-function
+                          whitelisted-collection)
+                        ido-cr+-function-whitelist)))
+        (it "should enable ido in a whitelisted command"
+          (expect
+           (with-simulated-input "g RET"
+             (call-interactively 'whitelisted-command))
+           :to-equal "green"))
+        (it "should enable ido in a whitelisted function"
+          (expect
+           (with-simulated-input "g RET"
+             (call-interactively 'cmd-that-calls-whitelisted-function))
+           :to-equal "green"))
+        (it "should enable ido for a whitelisted collection"
+          (expect
+           (with-simulated-input "g RET"
+             (ido-completing-read+ "Prompt: " 'whitelisted-collection))
+           :to-equal "green")))
+      (describe "when the whitelist is active but empty (i.e. nothing whitelisted)"
+        (before-each
+          (setq ido-cr+-function-whitelist (list nil)))
+        (it "should prevent ido in a command"
+          (expect
+           (with-simulated-input "g RET"
+             (call-interactively 'whitelisted-command))
+           :to-equal "g"))
+        (it "should prevent ido in a function"
+          (expect
+           (with-simulated-input "g RET"
+             (call-interactively 'cmd-that-calls-whitelisted-function))
+           :to-equal "g"))
+        (it "should prevent ido for a collection"
+          (expect
+           (with-simulated-input "g RET"
+             (ido-completing-read+ "Prompt: " 'whitelisted-collection))
+           :to-equal "g"))))))
+
+;; (defun ido-cr+-run-all-tests ()
+;;   (interactive)
+;;   (ert "^ido-cr\\+-"))
+
+;;; test-ido-completing-read+.el ends here