ido-ubiquitous-test.el 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. ;;; ido-ubiquitous-test.el --- -*- lexical-binding: t -*-
  2. ;; Copyright (C) 2015 Ryan C. Thompson
  3. ;; Filename: ido-ubiquitous-test.el
  4. ;; Author: Ryan C. Thompson
  5. ;; Created: Tue Oct 6 20:52:45 2015 (-0700)
  6. ;; This file is NOT part of GNU Emacs.
  7. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  8. ;;
  9. ;; This program is free software: you can redistribute it and/or modify
  10. ;; it under the terms of the GNU General Public License as published by
  11. ;; the Free Software Foundation, either version 3 of the License, or (at
  12. ;; your option) any later version.
  13. ;;
  14. ;; This program is distributed in the hope that it will be useful, but
  15. ;; WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. ;; General Public License for more details.
  18. ;;
  19. ;; You should have received a copy of the GNU General Public License
  20. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  21. ;;
  22. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  23. ;;
  24. ;;; Code:
  25. ;; This is a series of macros to facilitate the testing of completion
  26. ;; non-interactively by simulating input.
  27. (require 'ido-ubiquitous)
  28. (require 'ert)
  29. (require 'cl-macs)
  30. (defmacro keyboard-quit-to-error (&rest body)
  31. "Evaluate BODY but signal an error on `keyboard-quit'."
  32. `(condition-case nil
  33. (progn ,@body)
  34. (quit
  35. (error "Caught `keyboard-quit'"))))
  36. (defmacro with-simulated-input (keys &rest body)
  37. "Eval body with KEYS as simulated input.
  38. This macro is intended for testing normally interactive functions
  39. by simulating input. If BODY tries to read more input events than
  40. KEYS provides, `keyboard-quit' is invoked (by means of appending
  41. multple C-g keys to KEYS). This is to ensure that BODY will never
  42. block waiting for input, since this macro is intended for
  43. noninteractive use. As such, BODY should not invoke
  44. `keyboard-quit' under normal operation, and KEYS should not
  45. include C-g, or this macro will interpret it as reading past the
  46. end of input."
  47. ;; It would be better to detect end-of-input by overriding
  48. ;; `read-event' to throw an error, since theoretically C-g could be
  49. ;; rebound to something other than `keyboard-quit'. But apparently
  50. ;; some functions read input directly in C code, and redefining
  51. ;; `read-event' has no effect on those. So the suboptimal solution
  52. ;; is to rely on C-g.
  53. (declare (indent 1))
  54. `(let* ((key-sequence (listify-key-sequence (kbd ,keys)))
  55. (C-g-key-sequence
  56. (listify-key-sequence
  57. ;; We *really* want to trigger `keyboard-quit' if we reach
  58. ;; the end of the input.
  59. (kbd "C-g C-g C-g C-g C-g C-g C-g")))
  60. (unread-command-events
  61. (append key-sequence C-g-key-sequence)))
  62. (when (member (car C-g-key-sequence) key-sequence)
  63. (error "KEYS must include C-g"))
  64. (condition-case nil
  65. ,@body
  66. (quit
  67. (error "Reached end of simulated input while evaluating body")))))
  68. (defmacro with-mode (mode arg &rest body)
  69. "Eval (MODE ARG), then body, then restore previous status of MODE.
  70. This will only work on modes that respect the normal conventions
  71. for activation and deactivation."
  72. (declare (indent 2))
  73. (let* ((orig-status (eval mode))
  74. (restore-arg (if orig-status 1 0)))
  75. `(unwind-protect
  76. (progn
  77. (,mode ,arg)
  78. ,@body)
  79. (,mode ,restore-arg))))
  80. (defmacro with-ido-ubiquitous-standard-env (&rest body)
  81. "Execute BODY with standard ido-ubiquitous settings.\n\nAll ido-ubiquitous and ido-cr+ options will be let-bound to their\ndefault values, and `ido-ubiquitous-mode' will be enabled."
  82. (declare (indent 0))
  83. (let*
  84. ((ido-ubiquitous-options
  85. '(ido-ubiquitous-allow-on-functional-collection
  86. ido-ubiquitous-command-overrides
  87. ido-ubiquitous-debug-mode
  88. ido-ubiquitous-default-state
  89. ido-ubiquitous-function-overrides
  90. ido-cr+-fallback-function
  91. ido-cr+-max-items
  92. ido-cr+-replace-completely))
  93. (idu-bindings
  94. (loop for var in ido-ubiquitous-options collect
  95. (list var
  96. (list 'quote
  97. (default-value var))))))
  98. `(with-mode ido-ubiquitous-mode 1
  99. (let ,idu-bindings ,@body))))
  100. (defun collection-as-function (collection)
  101. "Return a function equivalent to COLLECTION.
  102. The returned function will work equivalently to COLLECTION when
  103. passed to `all-completions' and `try-completion'."
  104. (if (functionp collection)
  105. collection
  106. ;; Capture collection in a closure
  107. (lambda (string pred all)
  108. (funcall (if all #'all-completions #'try-completion)
  109. string collection pred))))
  110. (defun test-ido-ubiquitous-expected-mode (override)
  111. "Test whether observed ido-ubiquitous behavior matches OVERRIDE."
  112. (if (eq override 'disable)
  113. (progn
  114. (should
  115. ;; Verify that we get standard completion
  116. (string=
  117. "g"
  118. (with-simulated-input "g RET"
  119. (completing-read "Prompt: " '("blue" "yellow" "green")))))
  120. ;; Test is disabled because of an apparent bug in Emacs:
  121. ;; http://debbugs.gnu.org/cgi/bugreport.cgi?bug=21644
  122. ;; ;; Match is required, so with standard completion the input should
  123. ;; ;; be incomplete and throw an error.
  124. ;; (should-error
  125. ;; (with-simulated-input "g RET"
  126. ;; (completing-read "Prompt: " '("blue" "yellow" "green") nil t))
  127. ;; :type 'error)
  128. )
  129. ;; Common tests whenever ido-ubiquitous is enabled in any way
  130. (should
  131. ;; Verify that ido completion is active
  132. (string=
  133. "green"
  134. (with-simulated-input "g RET"
  135. (completing-read "Prompt: " '("blue" "yellow" "green")))))
  136. ;; Verify that C-j is working correctly
  137. (should
  138. (string=
  139. "g"
  140. (with-simulated-input "g C-j"
  141. (completing-read "Prompt: " '("blue" "yellow" "green")))))
  142. (let ((collection '("brown" "blue" "yellow" "green")))
  143. (should
  144. (member
  145. (with-simulated-input "b RET"
  146. (completing-read "Prompt: " collection))
  147. (all-completions "b" collection))))
  148. (case override
  149. (enable
  150. ;; Test for new style
  151. (should
  152. (string=
  153. "blue"
  154. (with-simulated-input "RET"
  155. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))))
  156. (should
  157. (string=
  158. ""
  159. (with-simulated-input "C-j"
  160. (completing-read "Prompt: " '("blue" "yellow" "green") nil t)))))
  161. (enable-old
  162. (should
  163. (string=
  164. ""
  165. (with-simulated-input "RET"
  166. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))))
  167. (should
  168. (string=
  169. "blue"
  170. (with-simulated-input "C-j"
  171. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))))
  172. ;; Verify that doing other stuff reverts RET and C-j to standard
  173. ;; meanings
  174. (should
  175. (string=
  176. "blue"
  177. (with-simulated-input "g DEL RET"
  178. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))))
  179. (should
  180. (string=
  181. "blue"
  182. (with-simulated-input "<right> <left> RET"
  183. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))))
  184. (should
  185. (string=
  186. ""
  187. (with-simulated-input "g DEL C-j"
  188. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))))
  189. (should
  190. (string=
  191. ""
  192. (with-simulated-input "<right> <left> C-j"
  193. (completing-read "Prompt: " '("blue" "yellow" "green") nil t)))))
  194. (otherwise (error "Unknown override %S" override)))))
  195. (defun test-ido-ubiquitous-expected-mode-on-functional-collection (override)
  196. "Test whether observed ido-ubiquitous behavior on functional collection matches OVERRIDE."
  197. (cl-letf* ((original-completing-read (symbol-function #'completing-read))
  198. ((symbol-function #'completing-read)
  199. (lambda (prompt collection &rest args)
  200. (apply original-completing-read prompt
  201. (collection-as-function collection)
  202. args))))
  203. (test-ido-ubiquitous-expected-mode override)))
  204. (ert-deftest ido-ubiquitous-test-simple ()
  205. "Test that basic ido-ubiquitous functionality is working."
  206. (with-ido-ubiquitous-standard-env
  207. (ido-ubiquitous-mode 1)
  208. (test-ido-ubiquitous-expected-mode 'enable)
  209. (ido-ubiquitous-mode 0)
  210. (test-ido-ubiquitous-expected-mode 'disable)))
  211. (ert-deftest ido-ubiquitous-test-oldstyle ()
  212. (with-ido-ubiquitous-standard-env
  213. (let ((ido-ubiquitous-default-state 'enable-old))
  214. (test-ido-ubiquitous-expected-mode 'enable-old))))
  215. (ert-deftest ido-ubiquitous-test-maxitems ()
  216. (with-ido-ubiquitous-standard-env
  217. (let ((ido-cr+-max-items -1))
  218. (test-ido-ubiquitous-expected-mode 'disable))))
  219. (ert-deftest ido-ubiquitous-test-override ()
  220. (with-ido-ubiquitous-standard-env
  221. (ido-ubiquitous-with-override 'enable
  222. (test-ido-ubiquitous-expected-mode 'enable))
  223. (ido-ubiquitous-with-override 'enable-old
  224. (test-ido-ubiquitous-expected-mode 'enable-old))
  225. (ido-ubiquitous-with-override 'disable
  226. (test-ido-ubiquitous-expected-mode 'disable))))
  227. (ert-deftest ido-ubiquitous-test-functional-collection ()
  228. (with-ido-ubiquitous-standard-env
  229. (test-ido-ubiquitous-expected-mode-on-functional-collection 'disable)
  230. (ido-ubiquitous-with-override 'enable
  231. (test-ido-ubiquitous-expected-mode-on-functional-collection 'enable))
  232. (ido-ubiquitous-with-override 'enable-old
  233. (test-ido-ubiquitous-expected-mode-on-functional-collection 'enable-old))))
  234. (ert-deftest ido-ubiquitous-require-match ()
  235. "Test whether require-match works."
  236. :expected-result :failed
  237. (should-error
  238. (with-simulated-input "b C-j"
  239. (ido-completing-read "Prompt: " '("blue" "brown" "yellow" "green") nil t))))
  240. ;; Functions to define overrides on for testing
  241. (defun idu-no-override-testfunc ()
  242. (test-ido-ubiquitous-expected-mode 'enable)
  243. (test-ido-ubiquitous-expected-mode-on-functional-collection 'disable))
  244. (defun idu-enabled-testfunc (&rest args)
  245. (test-ido-ubiquitous-expected-mode 'enable)
  246. (test-ido-ubiquitous-expected-mode-on-functional-collection 'enable))
  247. (defun idu-disabled-testfunc (&rest args)
  248. (test-ido-ubiquitous-expected-mode 'disable)
  249. (test-ido-ubiquitous-expected-mode-on-functional-collection 'disable))
  250. (defun idu-enabled-oldstyle-testfunc (&rest args)
  251. (test-ido-ubiquitous-expected-mode 'enable-old)
  252. (test-ido-ubiquitous-expected-mode-on-functional-collection 'enable-old))
  253. ;; commands to define overrides on for testing
  254. (defun idu-no-override-testcmd (&rest args)
  255. (interactive
  256. (list
  257. (test-ido-ubiquitous-expected-mode 'enable)
  258. (test-ido-ubiquitous-expected-mode-on-functional-collection 'disable)))
  259. (test-ido-ubiquitous-expected-mode 'enable)
  260. (test-ido-ubiquitous-expected-mode-on-functional-collection 'disable))
  261. (defun idu-enabled-testcmd (&rest args)
  262. (interactive
  263. (list
  264. (test-ido-ubiquitous-expected-mode 'enable)
  265. (test-ido-ubiquitous-expected-mode-on-functional-collection 'enable)))
  266. (test-ido-ubiquitous-expected-mode 'enable)
  267. (test-ido-ubiquitous-expected-mode-on-functional-collection 'enable))
  268. (defun idu-disabled-testcmd (&rest args)
  269. (interactive
  270. (list
  271. (test-ido-ubiquitous-expected-mode 'disable)
  272. (test-ido-ubiquitous-expected-mode-on-functional-collection 'disable)))
  273. (test-ido-ubiquitous-expected-mode 'disable)
  274. (test-ido-ubiquitous-expected-mode-on-functional-collection 'disable))
  275. (defun idu-enabled-oldstyle-testcmd (&rest args)
  276. (interactive
  277. (list
  278. (test-ido-ubiquitous-expected-mode 'enable-old)
  279. (test-ido-ubiquitous-expected-mode-on-functional-collection 'enable-old)))
  280. (test-ido-ubiquitous-expected-mode 'enable-old)
  281. (test-ido-ubiquitous-expected-mode-on-functional-collection 'enable-old))
  282. (ert-deftest ido-ubiquitous-test-command-and-function-overrides ()
  283. (let ((orig-func-overrides ido-ubiquitous-function-overrides)
  284. (orig-cmd-overrides ido-ubiquitous-command-overrides))
  285. (unwind-protect
  286. (progn
  287. (customize-set-variable
  288. 'ido-ubiquitous-function-overrides
  289. (append ido-ubiquitous-function-overrides
  290. '((enable exact "idu-enabled-testfunc")
  291. (disable exact "idu-disabled-testfunc")
  292. (enable-old exact "idu-enabled-oldstyle-testfunc"))))
  293. (loop for func in
  294. '(idu-no-override-testfunc
  295. idu-enabled-testfunc
  296. idu-disabled-testfunc
  297. idu-enabled-oldstyle-testfunc)
  298. do (funcall func))
  299. (customize-set-variable
  300. 'ido-ubiquitous-command-overrides
  301. (append ido-ubiquitous-command-overrides
  302. '((enable exact "idu-enabled-testcmd")
  303. (disable exact "idu-disabled-testcmd")
  304. (enable-old exact "idu-enabled-oldstyle-testcmd"))))
  305. (loop for cmd in
  306. '(idu-no-override-testcmd
  307. idu-enabled-testcmd
  308. idu-disabled-testcmd
  309. idu-enabled-oldstyle-testcmd)
  310. do (call-interactively cmd)))
  311. (customize-set-variable 'ido-ubiquitous-function-overrides orig-func-overrides)
  312. (customize-set-variable 'ido-ubiquitous-command-overrides orig-cmd-overrides))))
  313. (ert-deftest ido-ubiquitous-test-fallback ()
  314. (with-ido-ubiquitous-standard-env
  315. (should
  316. ;; C-b/f not at beginning/end of input should not fall back
  317. (string=
  318. "green"
  319. (with-simulated-input "g C-b C-f RET"
  320. (completing-read "Prompt: " '("blue" "yellow" "green")))))
  321. (should
  322. ;; C-f at end of input should fall back
  323. (string=
  324. "g"
  325. (with-simulated-input "g C-f RET"
  326. (completing-read "Prompt: " '("blue" "yellow" "green")))))
  327. (should
  328. ;; Repeated C-b should not fall back
  329. (string=
  330. "green"
  331. (with-simulated-input "g C-b C-b C-b C-b RET"
  332. (completing-read "Prompt: " '("blue" "yellow" "green")))))
  333. (should
  334. ;; C-b at beginning of line should fall back (if previous action
  335. ;; was not also C-b)
  336. (string=
  337. "g"
  338. (with-simulated-input "g C-b x DEL C-b RET"
  339. (completing-read "Prompt: " '("blue" "yellow" "green")))))))
  340. (provide 'ido-ubiquitous-test)
  341. ;;; ido-ubiquitous-test.el ends here