Back to the schedule
Previous: Turbo Bindat
Next: Perso-Arabic Input Methods And Making More Emacs Apps BIDI Aware

Test blocks

Eduardo Ochs

Q&A: IRC
Duration: 6:04

If you have questions and the speaker has not indicated public contact information on this page, please feel free to e-mail us at emacsconf-submit@gnu.org and we'll forward your question to the speaker.

Description

In this presentation I will show an idea that feels completely obvious once we see it, but that only occured to me after after using Emacs and eev as my main interface to the computer for more than 20 years. Take any interpreted language that supports multi-line comments, and whose interpreter can be run in an Emacs buffer - for example Lua, Haskell, Python, or Julia; let's say just "Lua" from here on for simplicity. So: suppose that we have a Lua script that we wrote, that is called "foo.lua" and that defines lots of functions and defines the classes Bar and Bletch. We can put after the definition of the class Bar a multi-line comment that contains an eepitch block that when executed starts a Lua interpreter, loads the script foo.lua (by running 'dofile "foo.lua"'), and then has several tests for that class and its methods; and we can put another block with tests like that after the class Bletch, and other blocks after some functions. Eepitch allows sending these tests line by line to the Lua interpreter by typing on each line that we want to send, and this lets us create tests that are very easy to understand even without writing comments; this gives us a very quick way to document code by executable tests, that is super-great for experimental code that is still going to change a lot before running the risk of being read by other people.

These multi-line comments with eepitch blocks that run an interpreter and make it load the current file are called "test blocks". The command M-x eeit' inserts a test block at point, using the major mode to decide the right syntax to use for the multi-line comments and for the "dofile". We can configure the syntax of the test blocks for the current major mode by runningM-x find-eeit-links'; this can also be used to add support for test blocks to more languages (or, more precisely: to more major modes).

Eduardo Ochs http://angg.twu.net/emacsconf2021.html

Discussion

IRC nick: edrx

  • I love the slide annotations! They are really nice. :-)
  • Love the Racket's (module+ ...) but this test blocks are interesting too...
  • looks like eev should be using eieio to me :)
  • am I the only person who never heard of eev before this talk and now has a million uses for it? I've wanted this thing for ages
  • does language support take a lot of work, or did I miss you relying on a different package that handles that?
  • Cool! So this is kind of like a generic version of Python's doctests? (https://docs.python.org/3/library/doctest.html)
    • edrx: I don't think so... is it possible to run python's doctests line by line? Also, see this: https://www.youtube.com/watch?v=QUMo7vgkHJI#t=6m36s - we can change the tests on the fly...

Transcript

Hi! My name is Eduardo Ochs. I'm the author of an Emacs package called eev, and this talk is about a new feature of eev called "test blocks". Let's start by a demo. This is a file in Lua that defines these two functions here, and if we type <f8> several times here, the <f8>s create a Lua REPL here and then they send these lines to the REPL, where this line here loads this file into the REPL, and these other lines here are tests for these lines. There's a lot of information here, so let me organize them in a more visual way. This is our file in Lua. Lua sees this thing as a multi-line comment, but we are going to see it as a test block. And eev mode is active, so <f8> does the right thing. These three lines here set up the target buffer running a Lua REPL. You can see the the prompt of the REPL here, and these lines here are sent to the REPL. When we type <f8> on a line that starts with a red star, like these lines here, what <f8> does is that it sends the rest of the line-- sorry, it executes the rest of the line as Lisp. So the three <f8>s here executes these lines as Lisp, and they set up the target buffer here. When we type <f8> on a line that does not start with a red star, the <f8> sends the line to the target buffer and moves down. This line loads this file in the REPL, and these lines are tests. So we just saw how to use an existing test block; let's now see how to create a new test block. We just have to run this: M-x ee-insert-test-block - or M-x eeit. The result depends on the major mode. Let's understand that by looking at the source code. eeit is an alias to this function here, and this function is just five lines of code plus a docstring... and the docstring explains that if the major mode is foo-mode, then this function tries to call a function called ee-insert-test-foo-mode if that function exists, and that, if that function does not exist, then it yields an error. And here's an example of one such function. That's a function that inserts a test block in haskell-mode. Here we can see two functions like this: one for haskell-mode and one for js-mode. These functions look quite similar, but their effects look quite different. To make this comparison here, I started by writing-- by creating seven files, each one in a different language. Initially, each one of these files only had a comment with the name of the language... so: C, Haskell, Javascript, Org Mode, etc. In each one of these files, I typed M-x eeit to insert a test block. So here we can see that these test blocks are different. For example, the syntax for multi-line comments is different depending on the language. This block here that selects which REPL to run is also different, and this line here that tells the REPL to load the current file is also different, depending on the language. In some cases, I had to improvise a bit. For example, to implement test blocks in shell mode, I had to use this weird syntax using a here-document. In Tcl, I also had to improvise a bit, and in some cases, I had to improvise a lot. For example, in Org Mode, there isn't an obvious REPL to run, and there isn't an obvious way to load the the current Org file into the REPL, so the default action of M-x eeit in Org Mode is just to insert this thing here, that we can use to run a shell in a REPL. So these functions are quite similar. In the beginning, I was writing all of them by hand... but then I got bored and I wrote a function to help me write functions like that. This function is called find-eeit-links, and it creates a temporary buffer, and the contents of this temporary buffer depends on the major mode. For example, if the current mode is python-mode, then running this function here creates a temporary buffer that lets me write the support for test blocks in python-mode, or rewrite the function that supports test blocks in python-mode. So if I'm in python-mode and I run this, I get a temporary buffer like this, in which this thing is my template for the function. Usually, this string is totally wrong, I have to rewrite this string, but the rest is right. You can see python-mode here in the name of the function. So we have to edit this and save that to our ~/.emacs. By the way, these things here hyperlinks to many different things... This Elisp hyperlink here points to the source code, to the section in which these functions are defined. So you can see this here, the function that supports C, the function for Haskell, the function for Javascript, etc... and that's it! This is a five-minute talk, so I can't say much... If you want more information, or if you want to see real-world examples, how I use test blocks, etc. etc., see this page here... and I do not have time to explain this "By the way" here. So that's it! Thanks! =) captions by Eduardo Ochs

Back to the schedule
Previous: Turbo Bindat
Next: Perso-Arabic Input Methods And Making More Emacs Apps BIDI Aware