123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- ;;; -*- 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
|