4

I wrote a minor mode with the purpose of modifying written text on the fly.

It is currently creating a post-self-insert-command-hook, and triggers only on specific keypresses (e.g. the space bar, a semicolon, an open parens).

As an example, the user would type select and when they press the space key, the buffer would now contain SELECT.

I'm having a hard time writing a test for this with ert. I've tried writing it thus:

(ert-deftest upcase-select ()
  (with-temp-buffer
    (sqlup-mode)
    (insert "select")
    (execute-kbd-macro (kbd "SPC"))
    (should (equal (buffer-string) "SELECT "))))

This test should verify that when I press the space key, the word "select" becomes the word SELECT But the test ends up comparing " " and "SELECT ".

How can I write a test that triggers the behavior I desire?

(and further: is this a terrible idea? what's a better idea?)

2
  • 2
    Not an answer, but it sounds like you're reimplementing abbrevs – Malabarba Dec 27 '14 at 21:45
  • I just took another look at this. It looks like execute-kbd-macro take pre- and post-command hooks into account. In other words, your code should work. Are you sure the underlying mode sqlup-mode work as intended? – Lindydancer Nov 1 '15 at 8:00
1

I've been using ecukes to write these sorts of tests and it's brilliant.

Test cases look like this:

  # Check *args works ok
  Scenario: Space *args on its own
    When I type "f(*args)"
    Then I should see "f(*args)"

  Scenario: Space *args with other args
    When I type "f(a,*args)"
    Then I should see "f(a, *args)"

  # And **kwargs
  Scenario: Space **kwargs on its own
    When I type "f(**kwargs)"
    Then I should see "f(**kwargs)"

  Scenario: Space **kwargs with other args
    When I type "f(a,**kwargs)"
    Then I should see "f(a, **kwargs)"

I can add more details on setting it up if needed, or you can look at the project I linked (the Cask file and the features directory are relevant to ecukes).

3

You can see how I solved a similar problem with lispy-test.el. Here's an example test:

(ert-deftest lispy-braces ()
  (should (string= (lispy-with "\"a regex \\\\|\"" "{")
                   "\"a regex \\\\{|\\\\}\""))
  (should (string= (lispy-with "\"a string |" "{")
                   "\"a string {|}")))

lispy-with is a macro that creates a temporary file with emacs-lisp-mode, inserts the first string, calls the command sequence decoded from the second string and returns the final buffer contents:

(defmacro lispy-with (in &rest body)
  `(with-temp-buffer
     (emacs-lisp-mode)
     (lispy-mode)
     (insert ,in)
     (when (search-backward "~" nil t)
       (delete-char 1)
       (set-mark (point))
       (goto-char (point-max)))
     (search-backward "|")
     (delete-char 1)
     ,@(mapcar (lambda (x) (if (stringp x) `(lispy-unalias ,x) x)) body)
     (insert "|")
     (when (region-active-p)
       (exchange-point-and-mark)
       (insert "~"))
     (buffer-substring-no-properties
      (point-min)
      (point-max))))
4
  • So.. This simulates a self-insert-command ? – Trevoke Jan 3 '15 at 20:33
  • No, it resolves the command from the mode-map and calls it. Simulating stuff is kind of fragile. It's best to just call the command, bypassing all the interactive stuff. – abo-abo Jan 3 '15 at 20:54
  • And what's "lispy-unalias" ? – Trevoke Jan 3 '15 at 21:58
  • Just some function that looks up a key binding. See github.com/abo-abo/lispy/blob/master/lispy-test.el#L93 – abo-abo Jan 3 '15 at 22:51
3

My take on this is that execute-kbd-macro acts on the buffer of the selected window (it is effectively typed keystrokes from the user, after all), and you must therefore ensure that this is set to your temporary buffer.

e.g.:

(with-temp-buffer
  (insert "select")
  (save-window-excursion
    (set-window-buffer nil (current-buffer))
    (execute-kbd-macro (kbd "SPC")))
  (buffer-string))

I note that the same kind of thing is going on in the code of Lindydancer's answer.

2

First, the idea is great, you should write this kind of test cases for your packages.

One way to do this is to insert a suitable content into a temporary buffer, add suitable keys to the input queue, break into recursive edit, and let nature have its course. This way, things like post-command hooks are executed.

For example:

(defun test-upcase-interactively ()
  (let ((unread-command-events
         (append
          (kbd "M-u")
          ;; Remove the following line to stay in recursive edit.
          (kbd "C-M-c")          ; Exit recursive edit.
          unread-command-events)))
    (with-temp-buffer
      (save-excursion
        (insert "words"))
      (save-window-excursion
        (select-window (display-buffer (current-buffer)))
        (recursive-edit))
      (buffer-substring (point-min) (point-max)))))



(ert-deftest my-test ()
  (should (equal (test-upcase-interactively)
           "WORDS")))

Of course, it's overkill to use the recursive edit trick for something like upcase-word, as it doesn't rely on pre- and post-commands.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.