lunarmodules / busted

Elegant Lua unit testing.
https://lunarmodules.github.io/busted/
MIT License
1.38k stars 184 forks source link

Fennel loader? #738

Open HiPhish opened 4 months ago

HiPhish commented 4 months ago

Hello,

I would like to write tests in Fennel. My understanding is that this would be possible by implementing a Fennel runner. Looking at the source code it seems like the files under busted/modules/files implement loaders for Lua, Moonscript and Terra. A loader is a table with two functions load and match, but there is no explanation as to what interface these two functions must implement.

I would like to try my hands at writing a Fennel runner. Is there some explanation, or can someone here please elaborate on the protocol that needs to be implemented?

HiPhish commented 4 months ago

I have made some progress, but it's not yet there.

local path = require 'pl.path'
local ok, fennel = pcall(require, 'fennel')

local ret = {}

local getTrace = function(filename, info)
  return info  -- TODO
end

local rewriteMessage = function(filename, message)
  return message  -- TODO
end

ret.match = function(busted, filename)
  return ok and path.extension(filename) == '.fnl'
end

ret.load = function(busted, filename)
  if not ok then
    error 'unable to load Fennel.'
  end

  local err = nil
  local file = function()
    return fennel.dofile(filename, {env = _G})
  end
  -- TODO: Check if filename exists and is readable
  if not file then
    busted.publish({ 'error', 'file' }, { descriptor = 'file', name = filename }, nil, err, {})
  end
  return file, getTrace, rewriteMessage
end

return ret

This lets me at least load Fennel tests, but they won't work because the it function cannot be found.

Error → ...phish/Developer/busted/luarocks/share/lua/5.3/fennel.lua @ 3590
suite test/arithmetic_spec.fnl
test/arithmetic_spec.fnl:3:1: Compile error: unknown identifier: it

(it "Adds two numbers"
* Try looking to see if there's a typo.
* Try using the _G table instead, eg. _G.it if you really want a global.
* Try moving this code to somewhere that it is in scope.
* Try binding it as a local in the scope of this code.

What kind of magic is busted using to make it and related function globally available at runtime? I can seen in the Lua loader that it is using loadfile to create a thunk which will load the test code. Fennel does not have its own loadfile, so I wrap dofile inside an anonymous function. Could this be the problem?

HiPhish commented 4 months ago

OK, I think I got it working using a custom loadfile function for Fennel.

local function loadfile_(fname, ...)
  local file, err = io.open(fname)
  if not (file) then
    return nil, err
  end
  local text = assert(file:read("*a"))
  file:close()
  local lua = fennel.compileString(text)
  return (loadstring or load)(lua)
end

Now that I have a working minimum example I can start making the code good.