ido-ubiquitous-test.el 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  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. (let*
  83. ((ido-ubiquitous-options
  84. '(ido-ubiquitous-allow-on-functional-collection
  85. ido-ubiquitous-command-overrides
  86. ido-ubiquitous-debug-mode
  87. ido-ubiquitous-default-state
  88. ido-ubiquitous-function-overrides
  89. ido-cr+-fallback-function
  90. ido-cr+-max-items
  91. ido-cr+-replace-completely))
  92. (idu-bindings
  93. (loop for var in ido-ubiquitous-options collect
  94. (list var
  95. (list 'quote
  96. (default-value var))))))
  97. `(with-mode ido-ubiquitous-mode 1
  98. (let ,idu-bindings ,@body))))
  99. (defun collection-as-function (collection)
  100. "Return a function equivalent to COLLECTION.
  101. The returned function will work equivalently to COLLECTION when
  102. passed to `all-completions' and `try-completion'."
  103. (if (functionp collection)
  104. collection
  105. ;; Capture collection in a closure
  106. (lambda (string pred all)
  107. (funcall (if all #'all-completions #'try-completion)
  108. string collection pred))))
  109. (defun test-ido-ubiquitous-expected-mode (override)
  110. "Test whether observed ido-ubiquitous behavior matches OVERRIDE."
  111. (if (eq override 'disable)
  112. (progn
  113. (should
  114. ;; Verify that we get standard completion
  115. (string=
  116. "g"
  117. (with-simulated-input "g RET"
  118. (completing-read "Prompt: " '("blue" "yellow" "green")))))
  119. ;; Test is disabled because of an apparent bug in Emacs:
  120. ;; http://debbugs.gnu.org/cgi/bugreport.cgi?bug=21644
  121. ;; ;; Match is required, so with standard completion the input should
  122. ;; ;; be incomplete and throw an error.
  123. ;; (should-error
  124. ;; (with-simulated-input "g RET"
  125. ;; (completing-read "Prompt: " '("blue" "yellow" "green") nil t))
  126. ;; :type 'error)
  127. )
  128. ;; Common tests whenever ido-ubiquitous is enabled in any way
  129. (should
  130. ;; Verify that ido completion is active
  131. (string=
  132. "green"
  133. (with-simulated-input "g RET"
  134. (completing-read "Prompt: " '("blue" "yellow" "green")))))
  135. ;; Verify that C-j is working correctly
  136. (should
  137. (string=
  138. "g"
  139. (with-simulated-input "g C-j"
  140. (completing-read "Prompt: " '("blue" "yellow" "green")))))
  141. (let ((collection '("brown" "blue" "yellow" "green")))
  142. (should
  143. (member
  144. (with-simulated-input "b RET"
  145. (completing-read "Prompt: " collection))
  146. (all-completions "b" collection))))
  147. (case override
  148. (enable
  149. ;; Test for new style
  150. (should
  151. (string=
  152. "blue"
  153. (with-simulated-input "RET"
  154. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))))
  155. (should
  156. (string=
  157. ""
  158. (with-simulated-input "C-j"
  159. (completing-read "Prompt: " '("blue" "yellow" "green") nil t)))))
  160. (enable-old
  161. (should
  162. (string=
  163. ""
  164. (with-simulated-input "RET"
  165. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))))
  166. (should
  167. (string=
  168. "blue"
  169. (with-simulated-input "C-j"
  170. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))))
  171. ;; Verify that doing other stuff reverts RET and C-j to standard
  172. ;; meanings
  173. (should
  174. (string=
  175. "blue"
  176. (with-simulated-input "g DEL RET"
  177. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))))
  178. (should
  179. (string=
  180. "blue"
  181. (with-simulated-input "<right> <left> RET"
  182. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))))
  183. (should
  184. (string=
  185. ""
  186. (with-simulated-input "g DEL C-j"
  187. (completing-read "Prompt: " '("blue" "yellow" "green") nil t))))
  188. (should
  189. (string=
  190. ""
  191. (with-simulated-input "<right> <left> C-j"
  192. (completing-read "Prompt: " '("blue" "yellow" "green") nil t)))))
  193. (otherwise (error "Unknown override %S" override)))))
  194. (defun test-ido-ubiquitous-expected-mode-on-functional-collection (override)
  195. "Test whether observed ido-ubiquitous behavior on functional collection matches OVERRIDE."
  196. (cl-letf* ((original-completing-read (symbol-function #'completing-read))
  197. ((symbol-function #'completing-read)
  198. (lambda (prompt collection &rest args)
  199. (apply original-completing-read prompt
  200. (collection-as-function collection)
  201. args))))
  202. (test-ido-ubiquitous-expected-mode override)))
  203. (ert-deftest ido-ubiquitous-test-simple ()
  204. "Test that basic ido-ubiquitous functionality is working."
  205. (with-ido-ubiquitous-standard-env
  206. (ido-ubiquitous-mode 1)
  207. (test-ido-ubiquitous-expected-mode 'enable)
  208. (ido-ubiquitous-mode 0)
  209. (test-ido-ubiquitous-expected-mode 'disable)))
  210. (ert-deftest ido-ubiquitous-test-oldstyle ()
  211. (with-ido-ubiquitous-standard-env
  212. (let ((ido-ubiquitous-default-state 'enable-old))
  213. (test-ido-ubiquitous-expected-mode 'enable-old))))
  214. (ert-deftest ido-ubiquitous-test-maxitems ()
  215. (with-ido-ubiquitous-standard-env
  216. (let ((ido-cr+-max-items -1))
  217. (test-ido-ubiquitous-expected-mode 'disable))))
  218. (ert-deftest ido-ubiquitous-test-override ()
  219. (with-ido-ubiquitous-standard-env
  220. (ido-ubiquitous-with-override 'enable
  221. (test-ido-ubiquitous-expected-mode 'enable))
  222. (ido-ubiquitous-with-override 'enable-old
  223. (test-ido-ubiquitous-expected-mode 'enable-old))
  224. (ido-ubiquitous-with-override 'disable
  225. (test-ido-ubiquitous-expected-mode 'disable))))
  226. (ert-deftest ido-ubiquitous-test-functional-collection ()
  227. (with-ido-ubiquitous-standard-env
  228. (test-ido-ubiquitous-expected-mode-on-functional-collection 'disable)
  229. (ido-ubiquitous-with-override 'enable
  230. (test-ido-ubiquitous-expected-mode-on-functional-collection 'enable))
  231. (ido-ubiquitous-with-override 'enable-old
  232. (test-ido-ubiquitous-expected-mode-on-functional-collection 'enable-old))))
  233. (ert-deftest ido-ubiquitous-require-match ()
  234. "Test whether require-match works."
  235. :expected-result :failed
  236. (should-error
  237. (with-simulated-input "b C-j"
  238. (ido-completing-read "Prompt: " '("blue" "brown" "yellow" "green") nil t))))
  239. ;; TODO: Test function and command overrides
  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. (provide 'ido-ubiquitous-test)
  314. ;;; ido-ubiquitous-test.el ends here