diff options
| author | Wilfred Hughes <me@wilfred.me.uk> | 2018-08-12 16:17:42 -0700 |
|---|---|---|
| committer | Wilfred Hughes <me@wilfred.me.uk> | 2018-08-12 16:17:42 -0700 |
| commit | 253fec8cccc7369f65bb314bdda01ee702efc725 (patch) | |
| tree | 7ba1f6bbb1d39e0cffd2dd8365eb6dd40120c67d /test/helpful-unit-test.el | |
| parent | 5568c780e1b609a18728c592c0f85d798b6a1a47 (diff) | |
Rename file to avoid confusion with other projects' unit tests0.13
Diffstat (limited to 'test/helpful-unit-test.el')
| -rw-r--r-- | test/helpful-unit-test.el | 782 |
1 files changed, 782 insertions, 0 deletions
diff --git a/test/helpful-unit-test.el b/test/helpful-unit-test.el new file mode 100644 index 0000000..7feb8a0 --- /dev/null +++ b/test/helpful-unit-test.el @@ -0,0 +1,782 @@ +(require 'ert) +(require 'edebug) +(require 'helpful) +(require 'python) + +(defun test-foo () + "Docstring here." + nil) + +(defun test-foo-advised () + "Docstring here too." + nil) + +(autoload 'some-unused-function "somelib.el") + +(defadvice test-foo-advised (before test-advice1 activate) + "Placeholder advice 1." + nil) + +(defadvice test-foo-advised (after test-advice2 activate) + "Placeholder advice 2." + nil) + +(ert-deftest helpful--docstring () + "Basic docstring fetching." + (should + (equal + (helpful--docstring #'test-foo t) + "Docstring here."))) + +(ert-deftest helpful--docstring-symbol () + "Correctly handle quotes around symbols." + ;; We should replace quoted symbols with links, so the punctuation + ;; should not be in the output. + (let* ((formatted-docstring (helpful--format-docstring "`message'"))) + (should + (equal formatted-docstring "message"))) + ;; We should handle stray backquotes. + (let* ((formatted-docstring (helpful--format-docstring "`foo `message'"))) + (should + (equal formatted-docstring "`foo message")))) + +(ert-deftest helpful--docstring-unescape () + "Discard \\=\\= in docstrings." + (let* ((docstring (helpful--docstring #'apply t)) + (formatted-docstring (helpful--format-docstring docstring))) + (should + (not (s-contains-p "\\=" formatted-docstring))))) + +(ert-deftest helpful--docstring-keymap () + "Handle keymap references in docstrings." + (let* ((formatted-docstring + (helpful--format-docstring + "\\<minibuffer-local-map>\\[next-history-element]"))) + ;; This test will fail in a local Emacs instance that has modified + ;; minibuffer keybindings. + (should + (string-equal formatted-docstring "M-n")))) + +(ert-deftest helpful--docstring-advice () + "Get the docstring on advised functions." + (should + (equal + (helpful--docstring #'test-foo-advised t) + "Docstring here too."))) + +(defun test-foo-no-docstring () + nil) + +(ert-deftest helpful--no-docstring () + "We should not crash on a function without a docstring." + (should (null (helpful--docstring #'test-foo-no-docstring t)))) + +(ert-deftest helpful--interactively-defined-fn () + "We should not crash on a function without source code." + (eval '(defun test-foo-defined-interactively () 42)) + (with-temp-buffer + (helpful-function #'test-foo-defined-interactively) + (should (equal (buffer-name) "*helpful function: test-foo-defined-interactively*")))) + +(ert-deftest helpful--edebug-fn () + "We should not crash on a function with edebug enabled." + (let ((edebug-all-forms t) + (edebug-all-defs t)) + (with-temp-buffer + (insert "(defun test-foo-edebug () 44)") + (goto-char (point-min)) + (shut-up + (eval (eval-sexp-add-defvars (edebug-read-top-level-form)) t)))) + (helpful-function #'test-foo-edebug)) + +(defun test-foo-return-arg (s) + "blah blah." + s) + +(ert-deftest helpful--edebug-p () + "Ensure that we don't crash on a function whose body ends with +symbol (not a form)." + (should + (not (helpful--edebug-p #'test-foo-return-arg)))) + +(defun test-foo-usage-docstring () + "\n\n(fn &rest ARGS)" + nil) + +(ert-deftest helpful--usage-docstring () + "If a function docstring only has usage, do not return it." + (should (null (helpful--docstring #'test-foo-usage-docstring t)))) + +(defun test-foo-no-properties () + nil) + +(ert-deftest helpful--primitive-p () + ;; Defined in C. + (should (helpful--primitive-p 'message t)) + ;; Defined in C, but an alias. + (should (helpful--primitive-p 'not t)) + ;; Defined in elisp. + (should (not (helpful--primitive-p 'when t)))) + +(ert-deftest helpful--primitive-p--advised () + "Ensure we handly advised primitive functions correctly." + ;; `rename-buffer' is primitive, but it's advised by uniquify. + (should (helpful--primitive-p 'rename-buffer t))) + +(ert-deftest helpful-callable () + ;; We should not crash when looking at macros. + (helpful-callable 'when) + ;; Special forms should work too. + (helpful-callable 'if) + ;; Smoke test for special forms when we have the Emacs C source + ;; loaded. + (let* ((emacs-src-path (f-join default-directory "emacs-25.3" "src"))) + (if (f-exists-p emacs-src-path) + (let ((find-function-C-source-directory emacs-src-path)) + (helpful-callable 'if)) + (message "No Emacs source code found at %S, skipping test. Run ./download_emacs_src.sh." + emacs-src-path)))) + +(ert-deftest helpful--no-symbol-properties () + "Helpful should handle functions without any symbol properties." + ;; Interactively evaluating this file will set edebug properties on + ;; test-foo, so remove all properties. + (setplist #'test-foo-no-properties nil) + + ;; This shouldn't throw any errors. + (helpful-function #'test-foo-no-properties)) + +(ert-deftest helpful--split-first-line () + ;; Don't modify a single line string. + (should + (equal (helpful--split-first-line "foo") "foo")) + ;; Don't modify a two-line string if we don't end with . + (should + (equal (helpful--split-first-line "foo\nbar") "foo\nbar")) + ;; If the second line is already empty, do nothing. + (should + (equal (helpful--split-first-line "foo.\n\nbar") "foo.\n\nbar")) + ;; But if we have a single sentence and no empy line, insert one. + (should + (equal (helpful--split-first-line "foo.\nbar") "foo.\n\nbar"))) + +(ert-deftest helpful--format-reference () + (should + (equal + (helpful--format-reference '(def foo) 10 1 123 "/foo/bar.el") + "(def foo ...) 1 reference")) + (should + (equal + (helpful--format-reference '(advice-add 'bar) 10 1 123 "/foo/bar.el") + "(advice-add 'bar ...) 1 reference"))) + +(ert-deftest helpful--format-docstring () + "Ensure we handle `foo' formatting correctly." + ;; If it's bound, we should link it. + (let* ((formatted (helpful--format-docstring "foo `message'.")) + (m-position (s-index-of "m" formatted))) + (should (get-text-property m-position 'button formatted))) + ;; If it's not bound, we should not. + (let* ((formatted (helpful--format-docstring "foo `messagexxx'.")) + (m-position (s-index-of "m" formatted))) + (should (not (get-text-property m-position 'button formatted))) + ;; But we should always remove the backticks. + (should (equal formatted "foo messagexxx."))) + ;; Don't require the text between the quotes to be a valid symbol, e.g. + ;; support `C-M-\' (found in `vhdl-mode'). + (let* ((formatted (helpful--format-docstring "foo `C-M-\\'"))) + (should (equal formatted "foo C-M-\\")))) + +(ert-deftest helpful--format-docstring-escapes () + "Ensure we handle escaped quotes correctly." + (let* ((formatted (helpful--format-docstring "foo \\=`message\\='.")) + (m-position (s-index-of "m" formatted))) + (should (equal formatted "foo `message'.")) + (should (not (get-text-property m-position 'button formatted))))) + +(ert-deftest helpful--format-docstring-command-keys () + "Ensure we propertize references to command key sequences." + ;; This test will fail in your current Emacs instance if you've + ;; overridden the `set-mark-command' keybinding. + (-let [formatted (helpful--format-docstring "\\[set-mark-command]")] + (should + (string-equal formatted "C-SPC")) + (should + (get-text-property 0 'button formatted))) + ;; If we have quotes around a key sequence, we should not propertize + ;; it as the button styling will no longer be visible. + (-let [formatted (helpful--format-docstring "`\\[set-mark-command]'")] + (should + (string-equal formatted "C-SPC")) + (should + (eq + (get-text-property 0 'face formatted) + 'button))) + ;; Propertize mode maps. + (-let [formatted (helpful--format-docstring "`\\{python-mode-map}'")] + (should + (s-contains-p "run-python" formatted))) + ;; Handle non-existent mode maps gracefully. + (-let [formatted (helpful--format-docstring "`\\{no-such-mode-map}'")] + (should + (s-contains-p "not currently defined" formatted)))) + +(ert-deftest helpful--format-docstring--info () + "Ensure we propertize references to the info manual." + ;; This is the typical format. + (let* ((formatted (helpful--format-docstring "Info node `(elisp)foo'")) + (paren-position (s-index-of "(" formatted))) + (should + (string-equal formatted "Info node (elisp)foo")) + (should + (get-text-property paren-position 'button formatted))) + ;; Some functions, such as `signal', use 'anchor'. + (let* ((formatted (helpful--format-docstring "Info anchor `(elisp)foo'")) + (paren-position (s-index-of "(" formatted))) + (should + (string-equal formatted "Info anchor (elisp)foo")) + (should + (get-text-property paren-position 'button formatted))) + ;; Ensure we handle wrapped lines too, e.g. in `org-odt-pixels-per-inch'. + (let* ((formatted (helpful--format-docstring "Info node `(elisp)foo \nbar'")) + (paren-position (s-index-of "(" formatted))) + (should + (string-equal formatted "Info node (elisp)foo \nbar")) + (should + (get-text-property paren-position 'button formatted)))) + +(ert-deftest helpful--format-docstring--url () + "Ensure we propertize URLs with backticks." + (let* ((formatted (helpful--format-docstring "URL `http://example.com'")) + (url-position (s-index-of "h" formatted))) + (should + (string-equal formatted "URL http://example.com")) + (should + (get-text-property url-position 'button formatted)))) + +(ert-deftest helpful--format-docstring--bare-url () + "Ensure we propertize URLs without backticks." + (let* ((formatted (helpful--format-docstring "http://example.com\nbar")) + (url-position (s-index-of "h" formatted))) + (should + (string-equal formatted "http://example.com\nbar")) + (should + (get-text-property url-position 'button formatted)) + (should + (equal + (get-text-property url-position 'url formatted) + "http://example.com"))) + ;; Don't consider trailing punctuation to be part of the URL. + (let* ((formatted (helpful--format-docstring "See http://example.com.")) + (url-position (s-index-of "h" formatted))) + (should + (string-equal formatted "See http://example.com.")) + (should + (equal + (get-text-property url-position 'url formatted) + "http://example.com"))) + ;; Format markdown-style links. + (let* ((formatted (helpful--format-docstring "See <http://example.com>.")) + (url-position (s-index-of "h" formatted))) + (should + (equal + (get-text-property url-position 'url formatted) + "http://example.com")))) + +(ert-deftest helpful--definition-c-vars () + "Handle definitions of variables in C source code." + (let* ((emacs-src-path (f-join default-directory "emacs-25.3" "src"))) + (if (f-exists-p emacs-src-path) + (let ((find-function-C-source-directory emacs-src-path)) + (helpful--definition 'default-directory nil)) + (message "No Emacs source code found at %S, skipping test. Run ./download_emacs_src.sh" + emacs-src-path)))) + +(setq helpful-var-without-defvar 'foo) + +(ert-deftest helpful--definition-no-defvar () + "Ensure we don't crash on calling `helpful--definition' on +variables defined without `defvar'." + (helpful--definition 'helpful-var-without-defvar nil)) + +(ert-deftest helpful--definition-buffer-opened () + "Ensure we mark buffers as opened for variables." + (require 'python) + ;; This test will fail if you already have python.el.gz open in your + ;; Emacs instance. + (-let [(buf pos opened) (helpful--definition 'python-indent-offset nil)] + (should (bufferp buf)) + (should opened))) + +(ert-deftest helpful--definition-edebug-fn () + "Ensure we use the position information set by edebug, if present." + ;; Test with both edebug enabled and disabled. The edebug property + ;; on the symbol varies based on this. + (dolist (edebug-on (list nil t)) + (let ((edebug-all-forms edebug-on) + (edebug-all-defs edebug-on)) + (with-temp-buffer + (insert "(defun test-foo-edebug-defn () 44)") + (goto-char (point-min)) + (shut-up + (eval (eval-sexp-add-defvars (edebug-read-top-level-form)) t)) + + (-let [(buf pos opened) (helpful--definition 'test-foo-edebug-defn t)] + (should buf)))))) + +(ert-deftest helpful-variable () + "Smoke test for `helpful-variable'." + (helpful-variable 'tab-width)) + +(ert-deftest helpful-visit-reference () + "Smoke test for `helpful-visit-reference'." + (helpful-function 'replace-regexp-in-string) + (goto-char (point-min)) + ;; Move forward to the first reference. + (while (not (get-text-property (point) 'helpful-pos)) + (forward-char 1)) + (helpful-visit-reference)) + +(ert-deftest helpful--signature () + "Ensure that autoloaded functions are handled gracefully" + (should + (equal (helpful--signature 'some-unused-function) + "(some-unused-function [Arg list not available until function definition is loaded.])"))) + +(ert-deftest helpful--signature--advertised () + "Ensure that we respect functions that declare `advertised-calling-convention'." + (should + (equal (helpful--signature 'start-process-shell-command) + "(start-process-shell-command NAME BUFFER COMMAND)"))) + +(ert-deftest helpful-function--single-buffer () + "Ensure that calling `helpful-buffer' does not leave any extra +buffers lying around." + (let ((initial-buffers (buffer-list)) + expected-buffers results-buffer) + (helpful-function #'enable-theme) + (setq results-buffer (get-buffer "*helpful command: enable-theme*")) + (setq expected-buffers + (cons results-buffer + initial-buffers)) + (should + (null + (-difference (buffer-list) expected-buffers))))) + +(ert-deftest helpful--kind-name () + (should + (equal + (helpful--kind-name 'message nil) + "variable")) + (should + (equal + (helpful--kind-name 'message t) + "function")) + (should + (equal + (helpful--kind-name 'save-excursion t) + "special form"))) + +(ert-deftest helpful--pretty-print () + ;; Strings should be formatted with double-quotes. + (should (equal "\"foo\"" (helpful--pretty-print "foo"))) + ;; Don't crash on large plists using keywords. + (helpful--pretty-print + '(:foo foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo :bar bar))) + +(ert-deftest helpful-update-after-killing-buf () + "If we originally looked at a variable in a specific buffer, +and that buffer has been killed, handle it gracefully." + ;; Don't crash if the underlying buffer has been killed. + (let (helpful-buf) + (with-temp-buffer + (helpful-variable 'tab-width) + (setq helpful-buf (current-buffer))) + (with-current-buffer helpful-buf + (helpful-update)))) + +(ert-deftest helpful--canonical-symbol () + (should + (eq (helpful--canonical-symbol 'not t) + 'null)) + (should + (eq (helpful--canonical-symbol 'emacs-bzr-version nil) + 'emacs-repository-version))) + +(ert-deftest helpful--aliases () + (should + (equal (helpful--aliases 'null t) + (list 'not))) + (should + (equal (helpful--aliases 'emacs-repository-version nil) + (list 'emacs-bzr-version)))) + +(defun helpful-fn-in-elc ()) + +(ert-deftest helpful--elc-only () + "Ensure we handle functions where we have the .elc but no .el +file." + ;; Pretend that we've loaded `helpful-fn-in-elc' from /tmp/foo.elc. + (let ((load-history (cons '("/tmp/foo.elc" (defun . helpful-fn-in-elc)) + load-history))) + ;; This should not error. + (helpful-function 'helpful-fn-in-elc))) + +(ert-deftest helpful--unnamed-func () + "Ensure we handle unnamed functions too. + +This is important for `helpful-key', where a user may have +associated a lambda with a keybinding." + (let* ((fun (lambda (x) x)) + (buf (helpful--buffer fun t))) + ;; There's no name, so just show lambda in the buffer name. + (should + (equal (buffer-name buf) "*helpful lambda*")) + ;; Don't crash when we show the buffer. + (with-current-buffer buf + (helpful-update)))) + +(ert-deftest helpful--keymap-keys--sparse () + (let* ((parent-keymap (make-sparse-keymap)) + (keymap (make-sparse-keymap))) + (set-keymap-parent keymap parent-keymap) + (define-key parent-keymap (kbd "a") #'forward-char) + (define-key keymap (kbd "C-c C-M-a") #'backward-char) + (define-key keymap [remap quoted-insert] #'forward-line) + (should + (equal + (helpful--keymap-keys keymap) + '(([17] forward-line) + ([3 27 1] backward-char) + ([97] forward-char)))))) + +(defvar helpful--dummy-keymap + (let ((keymap (make-sparse-keymap))) + (define-key keymap (kbd "a") #'forward-char) + keymap)) + +;; `fset' is necessary for keymaps as prefixes. This is a quirky Emacs +;; API: https://emacs.stackexchange.com/q/28576/304 +(fset 'helpful--dummy-keymap helpful--dummy-keymap) + +(ert-deftest helpful--keymap-keys--prefix () + "Test we flatten keymaps with prefix keys." + (let* ((keymap (make-sparse-keymap))) + (define-key keymap (kbd "C-c") 'helpful--dummy-keymap) + (should + (equal + (helpful--keymap-keys keymap) + '(([3 97] forward-char)))))) + +(ert-deftest helpful--keymap-keys () + (let* ((parent-keymap (make-keymap)) + (keymap (make-keymap))) + (set-keymap-parent keymap parent-keymap) + (define-key parent-keymap (kbd "a") #'forward-char) + (define-key keymap (kbd "C-c C-M-a") #'backward-char) + (define-key keymap [remap quoted-insert] #'forward-line) + (should + (equal + ;; This order differs from a sparse keymap. We should fix that + ;; if it makes any difference. + (helpful--keymap-keys keymap) + '(([3 27 1] backward-char) + ([17] forward-line) + ([97] forward-char)))))) + +(ert-deftest helpful--keymap-keys--strings () + "Test that we handle maps with format (TYPE ITEM-NAME . BINDING)." + ;; This is an actual piece of smerge-mode-map. + (let ((keymap '(keymap (3 keymap + (94 keymap + (61 keymap + (61 "upper-lower" . smerge-diff-upper-lower) + (62 "base-lower" . smerge-diff-base-lower) + (60 "base-upper" . smerge-diff-base-upper) + "Diff")))))) + (should + (equal + (helpful--keymap-keys keymap) + '(([3 94 61 61] smerge-diff-upper-lower) + ([3 94 61 62] smerge-diff-base-lower) + ([3 94 61 60] smerge-diff-base-upper)))))) + +(ert-deftest helpful--keymap-keys--anonymous-fns () + (let* ((keymap (make-keymap))) + (define-key keymap (kbd "a") + (lambda () (message))) + (define-key keymap (kbd "a") + (byte-compile-sexp (lambda () (message)))) + + ;; Don't crash on anonymous functions in a keymap. + (helpful--keymap-keys keymap))) + +(defun helpful--dummy-command () + (interactive)) + +(ert-deftest helpful--keymaps-containing () + "Ensure that we find keymaps for variables with bindings." + ;; This is defined in the global map. + (should + (helpful--keymaps-containing #'where-is)) + + ;; Only defined in `minor-mode-map-alist'. + (let ((keymap (make-sparse-keymap))) + (define-key keymap (kbd "a") #'helpful--dummy-command) + (let ((minor-mode-map-alist + (cons (cons 'foo-mode keymap) minor-mode-map-alist))) + (should + (helpful--keymaps-containing #'helpful--dummy-command)))) + + ;; Don't crash if there are dodgy values in `minor-mode-map-alist'. + (let ((minor-mode-map-alist + ;; I'm not convinced this is legal, but + ;; pdf-cache-prefetch-minor-mode in pdf-tools has t as a + ;; keymap. + (cons (cons 'foo-mode t) minor-mode-map-alist))) + (helpful--keymaps-containing #'helpful--dummy-command)) + + ;; Create a keybinding that is very unlikely to clobber actually + ;; defined keybindings in the current emacs instance. + (global-set-key (kbd "C-c M-S-c") #'helpful--dummy-command) + + ;; This command should only be in `global-map' and + ;; `mode-specific-map'. + (should + (equal + (length (helpful--keymaps-containing #'helpful--dummy-command)) + 2)) + + ;; Undo keybinding. + (global-set-key (kbd "C-c M-S-c") nil) + + ;; Check for ido command remapping. + (ido-mode 1) + (should + (equal + (helpful--keymaps-containing 'ido-find-file) + '(("minor-mode-map-alist (ido-mode)" "<open>" "C-x C-f")))) + (ido-mode 0)) + +(defalias 'helpful--dummy-command-alias #'helpful--dummy-command) + +(ert-deftest helpful--keymaps-containing-aliases () + "Ensure that we find keymaps that we've bound command aliases +in." + ;; Create keybindings that are very unlikely to clobber actually + ;; defined keybindings in the current emacs instance. + (global-set-key (kbd "C-c M-S-c") #'helpful--dummy-command) + (global-set-key (kbd "C-c M-S-d") #'helpful--dummy-command-alias) + + (unwind-protect + (let* ((keymaps (helpful--keymaps-containing-aliases #'helpful--dummy-command)) + (global-keybindings (cdr (assoc "global-map" keymaps)))) + (should + (equal global-keybindings (list "C-c M-S-c" "C-c M-S-d")))) + + ;; Undo keybindings. + (global-set-key (kbd "C-c M-S-c") nil) + (global-set-key (kbd "C-c M-S-d") nil))) + +(ert-deftest helpful--merge-alists () + (should + (equal + (helpful--merge-alists '((a . (1 2 3)) (b . (4))) + '((a . (10)) (c . (11)))) + '((a . (1 2 3 10)) + (b . (4)) + (c . (11)))))) + +(ert-deftest helpful--source () + (-let* (((buf pos opened) (helpful--definition #'helpful--source t)) + (source (helpful--source #'helpful--source t buf pos))) + (should + (s-starts-with-p "(defun " source)))) + +(ert-deftest helpful--source-autoloaded () + "We should include the autoload cookie." + (-let* (((buf pos opened) (helpful--definition #'helpful-at-point t)) + (source (helpful--source #'helpful-at-point t buf pos))) + (should + (s-starts-with-p ";;;###autoload" source)))) + +(ert-deftest helpful--source--interactively-defined-fn () + "We should return the raw sexp for functions where we can't +find the source code." + (eval '(defun test-foo-defined-interactively () 42)) + (-let* (((buf pos opened) (helpful--definition #'test-foo-defined-interactively t))) + (should + (not + (null + (helpful--source #'test-foo-defined-interactively t buf pos)))))) + +(ert-deftest helpful--outer-sexp () + ;; If point is in the middle of a form, we should return its position. + (with-temp-buffer + (insert "(foo bar baz)") + (goto-char (point-min)) + (search-forward "b") + + (-let [(pos subforms) + (helpful--outer-sexp (current-buffer) (point))] + (should + (equal pos (point-min))) + (should + (equal subforms '(foo bar))))) + ;; If point is at the beginning of a form, we should still return its position. + (with-temp-buffer + (insert "(foo) (bar)") + (goto-char (point-min)) + (search-forward "b") + (backward-char 2) + + (-let [(pos subforms) + (save-excursion + (helpful--outer-sexp (current-buffer) (point)))] + (should + (equal pos (point))) + (should + (equal subforms '(bar)))))) + +(ert-deftest helpful--summary--aliases () + ;; exclude the sym itself + "Ensure we mention that a symbol is an alias." + (-let* (((buf pos opened) (helpful--definition '-select t)) + (summary (helpful--summary '-select t buf pos))) + (when opened + (kill-buffer buf)) + ;; Strip properties to make assertion messages more readable. + (set-text-properties 0 (1- (length summary)) nil summary) + (should + (equal + summary + "-select is a function alias for -filter, defined in dash.el.")))) + +(ert-deftest helpful--summary--special-form () + "Ensure we describe special forms correctly" + (-let* ((summary (helpful--summary 'if t nil nil))) + ;; Strip properties to make assertion messages more readable. + (set-text-properties 0 (1- (length summary)) nil summary) + (should + (s-starts-with-p "if is a special form defined in" summary)))) + +(ert-deftest helpful--bound-p () + ;; Functions. + (should (helpful--bound-p 'message)) + ;; Variables + (should (helpful--bound-p 'tab-width)) + ;; Unbound. + (should (not (helpful--bound-p 'this-variable-does-not-exist))) + ;; For our purposes, we don't consider nil or t to be bound. + (should (not (helpful--bound-p 'nil))) + (should (not (helpful--bound-p 't)))) + +(ert-deftest helpful--callees () + (should + (equal + (helpful--callees '(quote (foo))) + nil)) + ;; Simple function calls. + (should + (equal + (helpful--callees '(foo (bar 1) 2)) + '(foo bar)))) + +(ert-deftest helpful--callees-let () + (should + (equal + (helpful--callees + '(progn + (let ((x (foo)) + (y t)) + (bar x y)) + (let (y) + (baz)) + (let* ((z (quux)))))) + '(foo bar baz quux)))) + +(ert-deftest helpful--callees--lambda () + (should + (equal + (helpful--callees '(lambda (x) (foo x))) + '(foo)))) + +(ert-deftest helpful--callees--closure () + (should + (equal + (helpful--callees '(closure (t) (x) (foo x))) + '(foo)))) + +(ert-deftest helpful--callees--function () + (should + (equal + (helpful--callees '(function (lambda (x) (foo x)))) + '(foo))) + (should + (equal + (helpful--callees '(function foo)) + '(foo)))) + +(ert-deftest helpful--callees--cond () + (should + (equal + (helpful--callees + '(cond + (x) + ((foo)) + ((bar) + (baz)) + (t + (quux)) + )) + '(foo bar baz quux)))) + +(ert-deftest helpful--callees--condition-case () + (should + (equal + (helpful--callees + '(condition-case e + (foo) + (error (bar)) + ((arith-error file-error) (baz)))) + '(foo bar baz)))) + +(ert-deftest helpful--callees--funcall () + (let ((result (helpful--callees + '(progn + (funcall 'foo 1) + (apply 'bar 2) + (apply (baz) 3) + (apply unknown-var 3))))) + (should (memq 'foo result)) + (should (memq 'bar result)) + (should (memq 'baz result)) + (should (not (memq 'unknown-var result)))) + (let ((result (helpful--callees + '(progn + (funcall #'foo 1) + (apply #'bar 2))))) + (should (memq 'foo result)) + (should (memq 'bar result)))) + +(ert-deftest helpful--callees-button--smoke () + (with-temp-buffer + (let ((button (helpful--make-callees-button + 'whatever + '(defun whatever () (something) (test 5))))) + (insert button) + (goto-char (point-min)) + (push-button))) + (with-temp-buffer + (let ((button (helpful--make-callees-button + '(lambda () (interactive) (other-window -1)) + '(lambda () (interactive) (other-window -1))))) + (insert button) + (goto-char (point-min)) + (push-button)))) + +(ert-deftest helpful--autoloaded-p () + (-let [(buf pos opened) (helpful--definition 'rx-to-string t)] + (should (helpful--autoloaded-p 'rx-to-string buf)) + (when opened + (kill-buffer buf)))) |
