viernes, 12 de febrero de 2016

Test spies with Lua metatables

Dabbling with Lua metatables, I tried to write a minimal testing library that does not impose you any funny 'describe(...)' or 'it(....)' nesting, and one can just organise the tests as he pleases.

What

I called it spacesuit.lua as it wraps your functions and gives you minimal support to write tests (assertions and spies) in the wild. If you need your tests to be TAP compliant, runnable from any platform, and a well known solution, I can recommend busted, but for me, I tried to keep it minimal so I can put it in my bag and run the files I need from my console, using some silly bash/zsh script using globbing. no need for luarocks, native compilation of lfs or anything.

Apart from providing some sugar for assert_equal (which I'll probably delete in favour of plain assert(foo==42)), it provides:

  • assert_raise(fun): runs the function and asserts an error is thrown during its execution.
  • spy(fun): returns a proxy function (it's a table with __call in its metatble) that logs all the calls (both actual parmeters and results). The usage is quite simple:
  • s = spy(function(x) return x+1 end)
    s(42)
    s(45)
    -- inspect the log
    s.called_with(42) -- true
    s.called_with(42).and_returns_with(43) -- true
    s.called_with(43) -- error
    s.called_with(42).and_returns_with(44) -- error
    
    --number of times called
    s.called() -- true
    s.called(1) -- error
    s.called(2) -- true
    
  • make_spy(Module, 'fun_name'): hijacks Module.fun_name so that you can track executions of functions inside modules. It provides a clean() method that releases the hijacking.
  • stub(Module, 'fun_name', fun): hijacks Module.fun_name and substitutes it for 'fun'.
The whole ungolfed code is (without tests) about 100 lines of lua, which is very impresive for a non-batteries included language.

How


The how is what is interesting. When you make a spy out of a function, spacesuit creates a func table which responds to called_with. called_with  returns a table with and_returns_with key which will do the matching. It's quite a nice usage of lexical scope juggling.

For the hijacking part, I wanted to wrap everything into another table which would have the 'clean' method, and use __call to call the spy table (that would cascade to its __call entry in its metatable, but lua doesn't let you chain __call's. So you have to write the outer one as a function that calls the inner one (and then the __call is run).


jueves, 11 de febrero de 2016

With this trick, Helm-dash gets find-as-you-type for free, developers hate it...

It's been a long time since I had this feature in mind. Where helm-dash would open documentation pages as you typed. 

It is clearly possible to run actions while pressing keys (helm-swoop does it). But when I looked at its code, I saw the author had rewritten and rebound most of the keys to add the 'hooks'. I clearly didn't want to do that.  

So I kept looking for cleaner solutions (helm-dash didn't have to do complex things, just trigger the action at every keypress), and wrote my finding in this issue. hooking to helm-idle, or hooking to 'helm-after-update-hook seemed the sanest options, but none was compelling enough.

Then I learned that you can set a 'persistent-action', that will run when you press c-z and won't quit helm. Ok, that's something. Not exactly what I ment but I can live with it.

FastForward a year , and while I was using helm-dash, I spotted the hints in the modeline.


So after c-h m, I discovered that c-c c-f does exactly what I want, that is running the persistent-action on every keypress.


So yeah, standing on the shoulders of giants, in this case I got my feature without needing to code a single line.

Issue closed!