ido-cr+-test.el 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. ;;; -*- lexical-binding: t -*-
  2. (require 'ido-completing-read+)
  3. (require 'ert)
  4. (require 'cl-lib)
  5. (require 'with-simulated-input)
  6. (defmacro with-mode (mode arg &rest body)
  7. "Eval (MODE ARG), then body, then restore previous status of MODE.
  8. This will only work on modes that respect the normal conventions
  9. for activation and deactivation."
  10. (declare (indent 2))
  11. `(let* ((orig-status ,mode)
  12. (restore-arg (if orig-status 1 0)))
  13. (unwind-protect
  14. (progn
  15. (,mode ,arg)
  16. ,@body)
  17. (,mode restore-arg))))
  18. (defmacro with-ido-cr+-standard-env (&rest body)
  19. "Execute BODY with standard ido-cr+ settings.
  20. All ido-cr+ options will be let-bound to their default values,
  21. and `ido-ubiquitous-mode' will be enabled. The values will all br
  22. restored to what they were previously after BODY exits."
  23. (declare (indent 0))
  24. (let*
  25. ((options
  26. '(ido-cr+-debug-mode
  27. ido-cr+-fallback-function
  28. ido-cr+-max-items
  29. ido-cr+-function-blacklist
  30. ido-cr+-function-whitelist
  31. ido-confirm-unique-completion))
  32. (bindings
  33. (cl-loop for var in options collect
  34. (list var
  35. (list 'quote
  36. (eval (car (get var 'standard-value))))))))
  37. `(with-mode ido-ubiquitous-mode 1
  38. (let ,bindings ,@body))))
  39. (defmacro collection-as-function (collection)
  40. "Return a function equivalent to COLLECTION.
  41. The returned function will work equivalently to COLLECTION when
  42. passed to `all-completions' and `try-completion'."
  43. `(completion-table-dynamic (lambda (string) (all-completions string ,collection))))
  44. (ert-deftest ido-cr+-test-basic ()
  45. :tags '(ido ido-cr+)
  46. "Test that basic ido-cr+ functionality is working."
  47. (with-ido-cr+-standard-env
  48. ;; Verify that pressing RET selects a matching item
  49. (should
  50. (string=
  51. "green"
  52. (with-simulated-input "g RET"
  53. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))))
  54. ;; Verify that pressing RET with multiple matches selects the
  55. ;; first one
  56. (should
  57. (string=
  58. "brown"
  59. (with-simulated-input "b RET"
  60. (ido-completing-read+ "Prompt: " '("brown" "blue" "yellow" "green")))))
  61. ;; Verify that cycling with <left> and <right> works, including
  62. ;; wrap-around
  63. (should
  64. (string=
  65. "blue"
  66. (with-simulated-input "b <right> <right> <right> <right> <left> RET"
  67. (ido-completing-read+ "Prompt: " '("brown" "blue" "yellow" "green")))))
  68. ;; Verify that RET or C-j on empty input returns "" when
  69. ;; REQUIRE-MATCH is t
  70. (should
  71. (string=
  72. ""
  73. (with-simulated-input "RET"
  74. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t))))
  75. (should
  76. (string=
  77. ""
  78. (with-simulated-input "C-j"
  79. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t))))
  80. ;; Verify that DEF works, whether or not it is an element of
  81. ;; COLLECTION
  82. (should
  83. (string=
  84. "green"
  85. (with-simulated-input "RET"
  86. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil nil nil "green"))))
  87. (should
  88. (string=
  89. "green"
  90. (with-simulated-input "RET"
  91. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil t nil nil "green"))))
  92. (should
  93. (string=
  94. "brown"
  95. (with-simulated-input "RET"
  96. (ido-completing-read+ "Prompt: " '("blue" "yellow" "brown") nil nil nil nil "brown"))))
  97. (should
  98. (string=
  99. "brown"
  100. (with-simulated-input "RET"
  101. (ido-completing-read+ "Prompt: " '("blue" "yellow" "brown") nil t nil nil "brown"))))
  102. (should
  103. (string=
  104. "brown"
  105. (with-simulated-input "RET"
  106. (ido-completing-read+ "Prompt: " '("blue" "yellow" "brown") nil t nil nil '("brown" "green")))))
  107. ;; Verify that INITIAL-INPUT works
  108. (should
  109. (string=
  110. "green"
  111. (with-simulated-input "RET"
  112. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr"))))
  113. ;; Verify that INITIAL-INPUT and DEF don't interfere with each other
  114. (should
  115. (string=
  116. "green"
  117. (with-simulated-input "RET"
  118. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr" nil "blue"))))
  119. (should
  120. (string=
  121. "blue"
  122. (with-simulated-input "DEL DEL RET"
  123. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green") nil nil "gr" nil "blue"))))
  124. ;; Verify that ido-cr+ works on function collections
  125. (should
  126. (string=
  127. "green"
  128. (with-simulated-input "g RET"
  129. (ido-completing-read+ "Prompt: " (collection-as-function '("blue" "yellow" "green"))))))))
  130. (ert-deftest ido-cr+-mode-activation ()
  131. :tags '(ido ido-cr+)
  132. "Test whether ido-ubiquitous-mode can be turned on and off."
  133. (with-ido-cr+-standard-env
  134. (cl-letf (((symbol-function 'test-command)
  135. (lambda ()
  136. (interactive)
  137. (completing-read "Prompt: " '("blue" "yellow" "green")))))
  138. ;; Verify that the mode can be activated
  139. (should
  140. (string=
  141. "green"
  142. (with-mode ido-ubiquitous-mode 1
  143. (with-simulated-input "g RET"
  144. (call-interactively 'test-command)))))
  145. ;; Verify that the mode can be deactivated
  146. (should
  147. (string=
  148. "g"
  149. (with-mode ido-ubiquitous-mode 0
  150. (with-simulated-input "g RET"
  151. (call-interactively 'test-command))))))))
  152. (ert-deftest ido-cr+-test-maxitems ()
  153. :tags '(ido ido-cr+)
  154. "Test whether the large-collection fallback works."
  155. (with-ido-cr+-standard-env
  156. ;; This should not fall back
  157. (should
  158. (string=
  159. "green"
  160. (with-simulated-input "g RET"
  161. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))))
  162. (let ((ido-cr+-max-items -1))
  163. ;; This should fall back because the collection is too large
  164. (should
  165. (string=
  166. "g"
  167. (with-simulated-input "g RET"
  168. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green"))))))))
  169. (ert-deftest ido-cr+-test-blacklist ()
  170. :tags '(ido ido-cr+)
  171. "Test whether `ido-ubiquitous-function-blacklist' works."
  172. (with-ido-cr+-standard-env
  173. (cl-letf (((symbol-function 'blacklisted-command)
  174. (lambda (arg)
  175. (interactive (list (completing-read "Prompt: " '("blue" "yellow" "green"))))
  176. arg))
  177. ((symbol-function 'blacklisted-function)
  178. (lambda ()
  179. (completing-read "Prompt: " '("blue" "yellow" "green"))))
  180. ((symbol-function 'cmd-that-calls-blacklisted-function)
  181. (lambda ()
  182. (interactive)
  183. (funcall 'blacklisted-function)))
  184. ((symbol-function 'blacklisted-collection)
  185. (collection-as-function '("blue" "yellow" "green"))))
  186. ;; First verify that they work normally before blacklisting them
  187. (should
  188. (string=
  189. "green"
  190. (with-simulated-input "g RET"
  191. (call-interactively 'blacklisted-command))))
  192. (should
  193. (string=
  194. "green"
  195. (with-simulated-input "g RET"
  196. (call-interactively 'cmd-that-calls-blacklisted-function))))
  197. (should
  198. (string=
  199. "green"
  200. (with-simulated-input "g RET"
  201. (ido-completing-read+ "Prompt: " 'blacklisted-collection))))
  202. ;; Now add them to the blacklist and try again
  203. (let ((ido-cr+-function-blacklist
  204. (append '(blacklisted-command
  205. blacklisted-function
  206. blacklisted-collection)
  207. ido-cr+-function-blacklist)))
  208. ;; All should now have ido-cr+ disabled
  209. (should
  210. (string=
  211. "g"
  212. (with-simulated-input "g RET"
  213. (call-interactively 'blacklisted-command))))
  214. (should
  215. (string=
  216. "g"
  217. (with-simulated-input "g RET"
  218. (call-interactively 'cmd-that-calls-blacklisted-function))))
  219. (should
  220. (string=
  221. "g"
  222. (with-simulated-input "g RET"
  223. (ido-completing-read+ "Prompt: " 'blacklisted-collection))))))))
  224. (ert-deftest ido-cr+-test-whitelist ()
  225. :tags '(ido ido-cr+)
  226. "Test whether `ido-ubiquitous-function-whitelist' works."
  227. (with-ido-cr+-standard-env
  228. (cl-letf (((symbol-function 'whitelisted-command)
  229. (lambda (arg)
  230. (interactive (list (completing-read "Prompt: " '("blue" "yellow" "green"))))
  231. arg))
  232. ((symbol-function 'whitelisted-function)
  233. (lambda ()
  234. (completing-read "Prompt: " '("blue" "yellow" "green"))))
  235. ((symbol-function 'cmd-that-calls-whitelisted-function)
  236. (lambda ()
  237. (interactive)
  238. (funcall 'whitelisted-function)))
  239. ((symbol-function 'whitelisted-collection)
  240. (lambda (string pred action)
  241. (complete-with-action action '("blue" "yellow" "green") string pred))))
  242. ;; Now add them to the whitelist
  243. (let ((ido-cr+-function-whitelist
  244. (append '(whitelisted-command
  245. whitelisted-function
  246. whitelisted-collection)
  247. ido-cr+-function-whitelist)))
  248. ;; All should now have ido-cr+ enabled
  249. (should
  250. (string=
  251. "green"
  252. (with-simulated-input "g RET"
  253. (call-interactively 'whitelisted-command))))
  254. (should
  255. (string=
  256. "green"
  257. (with-simulated-input "g RET"
  258. (call-interactively 'cmd-that-calls-whitelisted-function))))
  259. (should
  260. (string=
  261. "green"
  262. (with-simulated-input "g RET"
  263. (ido-completing-read+ "Prompt: " 'whitelisted-collection)))))
  264. ;; Run again with nothing whitelisted
  265. (let ((ido-cr+-function-whitelist '(nil)))
  266. ;; All should now have ido-cr+ disabled
  267. (should
  268. (string=
  269. "g"
  270. (with-simulated-input "g RET"
  271. (call-interactively 'whitelisted-command))))
  272. (should
  273. (string=
  274. "g"
  275. (with-simulated-input "g RET"
  276. (call-interactively 'cmd-that-calls-whitelisted-function))))
  277. (should
  278. (string=
  279. "g"
  280. (with-simulated-input "g RET"
  281. (ido-completing-read+ "Prompt: " 'whitelisted-collection))))))))
  282. (ert-deftest ido-cr+-require-match ()
  283. :tags '(ido ido-cr+)
  284. "Test whether REQUIRE-MATCH and DEF work as expected together."
  285. (with-ido-cr+-standard-env
  286. ;; "C-j" should be allowed to return an empty string even if
  287. ;; require-match is non-nil, as long as default is nil
  288. (should
  289. (string=
  290. ""
  291. (with-simulated-input "C-j"
  292. (ido-completing-read+
  293. "Prompt: "
  294. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))))
  295. ;; "C-j" should NOT be allowed to return an empty string if
  296. ;; require-match and default are both non-nil.
  297. (should-error
  298. (with-simulated-input "C-j"
  299. (ido-completing-read+
  300. "Prompt: "
  301. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t nil nil "yellow")))
  302. ;; Multiple presses of C-j won't just select the first match
  303. (should-error
  304. (with-simulated-input "b C-j C-j C-j"
  305. (ido-completing-read+
  306. "Prompt: "
  307. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t)))
  308. ;; First press of C-j should complete unique common prefix after the
  309. ;; first b, but then get stuck on the choice for the second b.
  310. (should-error
  311. (with-simulated-input "b C-j b C-j C-j"
  312. (ido-completing-read+
  313. "Prompt: "
  314. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t)))
  315. ;; This should complete to "blueberry" via 2 rounds of unique common
  316. ;; prefix completion, and then return on the 3rd "C-j"
  317. (should
  318. (string=
  319. "blueberry"
  320. (with-simulated-input "b C-j b C-j e C-j C-j"
  321. (ido-completing-read+
  322. "Prompt: "
  323. '("bluebird" "blues" "bluegrass" "blueberry" "yellow" "green") nil t))))
  324. ;; The "C-j" should complete to "bluegrass" and return, because
  325. ;; `ido-confirm-unique-completion is nil.
  326. (should
  327. (string=
  328. "bluegrass"
  329. (with-simulated-input "b l u e g C-j"
  330. (ido-completing-read+
  331. "Prompt: "
  332. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))))
  333. (let ((ido-confirm-unique-completion t))
  334. ;; Now the first "C-j" should complete to "bluegrass" but should
  335. ;; not return.
  336. (should-error
  337. (with-simulated-input "b l u e g C-j"
  338. (ido-completing-read+
  339. "Prompt: "
  340. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t)))
  341. ;; The first "C-j" should complete to "bluegrass", and the second
  342. ;; should return.
  343. (should
  344. (string=
  345. "bluegrass"
  346. (with-simulated-input "b l u e g C-j C-j"
  347. (ido-completing-read+
  348. "Prompt: "
  349. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t)))))
  350. ;; Finally, a few tests for the expected wrong behavior without
  351. ;; ido-cr+. If ido.el ever fixes this bug, it will cause this test
  352. ;; to fail as a signal that the workaround can be phased out.
  353. (should
  354. (string=
  355. ""
  356. (with-simulated-input "C-j"
  357. (ido-completing-read
  358. "Prompt: "
  359. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))))
  360. (should
  361. (string=
  362. "b"
  363. (with-simulated-input "b C-j"
  364. (ido-completing-read
  365. "Prompt: "
  366. '("bluebird" "blues" "bluegrass" "blueberry" "yellow ""green") nil t))))))
  367. (ert-deftest ido-cr+-test-fallback ()
  368. :tags '(ido ido-cr+)
  369. "Test whether manually invoking fallback works."
  370. (with-ido-cr+-standard-env
  371. (should
  372. ;; C-b/f not at beginning/end of input should not fall back
  373. (string=
  374. "green"
  375. (with-simulated-input "g C-b C-f RET"
  376. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))))
  377. (should
  378. ;; C-f at end of input should fall back
  379. (string=
  380. "g"
  381. (with-simulated-input "g C-f RET"
  382. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))))
  383. (should
  384. ;; Repeated C-b should not fall back
  385. (string=
  386. "green"
  387. (with-simulated-input "g C-b C-b C-b C-b RET"
  388. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))))
  389. (should
  390. ;; C-b at beginning of line should fall back (if previous action
  391. ;; was not also C-b)
  392. (string=
  393. "g"
  394. (with-simulated-input "g C-b x DEL C-b RET"
  395. (ido-completing-read+ "Prompt: " '("blue" "yellow" "green")))))))
  396. (ert-deftest ido-cr+-dot-prefix-empty-string ()
  397. :tags '(ido ido-cr+)
  398. "Test whether ido-ubiquitous successfully works around a bug in ido.
  399. See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=26997 for more
  400. information on this bug."
  401. (with-ido-cr+-standard-env
  402. (let ((ido-enable-dot-prefix t))
  403. (should
  404. (string=
  405. ""
  406. (with-simulated-input "RET"
  407. (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac")))))
  408. (should
  409. (string=
  410. "aab"
  411. (with-simulated-input "a a b RET"
  412. (ido-completing-read+ "Pick: " '("" "aaa" "aab" "aac"))))))))
  413. (defun ido-cr+-run-all-tests ()
  414. (interactive)
  415. (ert "^ido-cr\\+-"))
  416. (provide 'ido-ubiquitous-test)
  417. ;;; ido-ubiquitous-test.el ends here