Changed test framework to not require outside deps

This commit is contained in:
XeroOl 2023-03-23 14:55:06 -05:00
parent f4298b02be
commit 33d080f6c6
13 changed files with 275 additions and 20 deletions

View File

@ -14,7 +14,8 @@ all: $(EXE)
$(EXE): $(SRC) $(EXE): $(SRC)
echo "#!/usr/bin/env lua" > $@ echo "#!/usr/bin/env lua" > $@
LUA_PATH="./src/?.lua;./src/?/init.lua" FENNEL_PATH="./src/?.fnl;./src/?/init.fnl" \ LUA_PATH="./src/?.lua" \
FENNEL_PATH="./src/?.fnl" \
$(FENNEL) --require-as-include --compile src/fennel-ls.fnl >> $@ $(FENNEL) --require-as-include --compile src/fennel-ls.fnl >> $@
chmod 755 $@ chmod 755 $@
@ -22,8 +23,8 @@ clean:
rm -f $(EXE) rm -f $(EXE)
test: test:
# requires busted to be installed LUA_PATH="./src/?.lua;./?.lua" FENNEL_PATH="./src/?.fnl;./?.fnl" \
FENNEL_PATH="./src/?.fnl;./src/?/init.fnl" $(FENNEL) --correlate test/init.fnl --verbose $(FENNEL) test/init.fnl
install: $(EXE) install: $(EXE)
mkdir -p $(DESTDIR)$(BIN_DIR) && cp $< $(DESTDIR)$(BIN_DIR)/ mkdir -p $(DESTDIR)$(BIN_DIR) && cp $< $(DESTDIR)$(BIN_DIR)/

View File

@ -1,5 +1,5 @@
(import-macros {: is-matching : describe : it : before-each} :test) (import-macros {: is-matching : describe : it : before-each} :test)
(local is (require :luassert)) (local is (require :test.is))
(local {: view} (require :fennel)) (local {: view} (require :fennel))
(local {: ROOT-URI (local {: ROOT-URI
@ -20,7 +20,7 @@
suggestion.label suggestion.label)] suggestion.label suggestion.label)]
(if expected (if expected
(each [_ exp (ipairs expected)] (each [_ exp (ipairs expected)]
(is.truthy (. seen exp) (.. exp " was not suggested, but should be")))) (is (. seen exp) (.. exp " was not suggested, but should be"))))
(if ?unexpected (if ?unexpected
(each [_ exp (ipairs ?unexpected)] (each [_ exp (ipairs ?unexpected)]
(is.nil (. seen exp) (.. exp " was suggested, but shouldn't be")))))) (is.nil (. seen exp) (.. exp " was suggested, but shouldn't be"))))))

View File

@ -1,5 +1,5 @@
(import-macros {: is-matching : describe : it : before-each} :test) (import-macros {: is-matching : describe : it : before-each} :test)
(local is (require :luassert)) (local is (require :test.is))
(local {: view} (require :fennel)) (local {: view} (require :fennel))
(local {: ROOT-URI (local {: ROOT-URI

View File

@ -1,6 +1,6 @@
(import-macros {: is-matching : describe : it : before-each} :test) (import-macros {: is-matching : describe : it : before-each} :test)
(local is (require :luassert)) (local is (require :test.is))
(local {: ROOT-URI (local {: ROOT-URI
: setup-server} (require :test.utils)) : setup-server} (require :test.utils))

View File

@ -1,5 +1,5 @@
(import-macros {: is-matching : describe : it : before-each} :test) (import-macros {: is-matching : describe : it : before-each} :test)
(local is (require :luassert)) (local is (require :test.is))
(local {: view} (require :fennel)) (local {: view} (require :fennel))
(local {: ROOT-URI (local {: ROOT-URI

View File

@ -2,24 +2,24 @@
"This document does not include tests. Instead it includes macros that are used for tests." "This document does not include tests. Instead it includes macros that are used for tests."
(fn it [desc ...] (fn it [desc ...]
"busted's `it` function" "lust's `it` function"
(let [body [...]] (let [body [...]]
(table.insert body `nil) (table.insert body `nil)
`((. (require :busted) :it) `((. (require :test.lust) :it)
,desc (fn [] ,desc ,(unpack body))))) ,desc (fn [] ,desc ,(unpack body)))))
(fn describe [desc ...] (fn describe [desc ...]
"busted's `describe` function" "lust's `describe` function"
(let [body [...]] (let [body [...]]
(table.insert body `nil) (table.insert body `nil)
`((. (require :busted) :describe) `((. (require :test.lust) :describe)
,desc (fn [] ,desc ,(unpack body))))) ,desc (fn [] ,desc ,(unpack body)))))
(fn before-each [...] (fn before-each [...]
"busted's `describe` function" "lust's `before_each` function"
(let [body [...]] (let [body [...]]
(table.insert body `nil) (table.insert body `nil)
`((. (require :busted) :before_each) `((. (require :test.lust) :before_each)
(fn [] ,(unpack body))))) (fn [] ,(unpack body)))))

View File

@ -1,5 +1,3 @@
((require :busted.runner))
(require :test.completion-test) (require :test.completion-test)
(require :test.diagnostic-test) (require :test.diagnostic-test)
(require :test.goto-definition-test) (require :test.goto-definition-test)
@ -8,3 +6,7 @@
(require :test.misc-test) (require :test.misc-test)
(require :test.string-processing-test) (require :test.string-processing-test)
(require :test.settings-test) (require :test.settings-test)
(let [{: passes : errors} (require :test.lust)]
(print (.. passes " passes. " errors " errors."))
(os.exit errors))

10
test/is.fnl Normal file
View File

@ -0,0 +1,10 @@
;; this package is here to translate into lust's weird dsl
(local {: view} (require :fennel))
(local {: expect} (require :test.lust))
;; lust uses weird terminology, but equal is by __eq, same is by recursively having the same contents
(setmetatable {:equal #((. (expect $1) :to :be) $2)
:same #((. (expect $1) :to :equal) $2)
:nil #((. (expect $1) :to_not :exist))
:not {:nil #((. (expect $1) :to :exist))}
:truthy #((. (expect $1) :to :be :truthy))}
{:__call #((. (expect $2) :to :be :truthy))})

View File

@ -1,5 +1,5 @@
(import-macros {: is-matching : describe : it} :test) (import-macros {: is-matching : describe : it} :test)
(local is (require :luassert)) (local is (require :test.is))
(local stringio (require :test.pl.stringio)) (local stringio (require :test.pl.stringio))
(local json-rpc (require :fennel-ls.json-rpc)) (local json-rpc (require :fennel-ls.json-rpc))

242
test/lust.lua Normal file
View File

@ -0,0 +1,242 @@
-- lust v0.1.0 - Lua test framework
-- https://github.com/bjornbytes/lust
-- MIT LICENSE
local lust = {}
lust.level = 0
lust.passes = 0
lust.errors = 0
lust.befores = {}
lust.afters = {}
local red = string.char(27) .. '[31m'
local green = string.char(27) .. '[32m'
local normal = string.char(27) .. '[0m'
local function indent(level) return string.rep('\t', level or lust.level) end
local function has(t, x)
for k, v in pairs(t) do
if v == x then return true end
end
return false
end
function lust.nocolor()
red, green, normal = '', '', ''
return lust
end
function lust.describe(name, fn)
print(indent() .. name)
lust.level = lust.level + 1
fn()
lust.befores[lust.level] = {}
lust.afters[lust.level] = {}
lust.level = lust.level - 1
end
function lust.it(name, fn)
for level = 1, lust.level do
if lust.befores[level] then
for i = 1, #lust.befores[level] do
lust.befores[level][i](name)
end
end
end
local success, err = xpcall(fn, require('fennel').traceback)
if success then lust.passes = lust.passes + 1
else lust.errors = lust.errors + 1 end
local color = success and green or red
local label = success and 'PASS' or 'FAIL'
print(indent() .. color .. label .. normal .. ' ' .. name)
if err then
print(indent(lust.level + 1) .. red .. tostring(err) .. normal)
end
for level = 1, lust.level do
if lust.afters[level] then
for i = 1, #lust.afters[level] do
lust.afters[level][i](name)
end
end
end
if has(arg, "--quit-at-first-test") then os.exit(1) end
end
function lust.before(fn)
lust.befores[lust.level] = lust.befores[lust.level] or {}
table.insert(lust.befores[lust.level], fn)
end
function lust.after(fn)
lust.afters[lust.level] = lust.afters[lust.level] or {}
table.insert(lust.afters[lust.level], fn)
end
-- Assertions
local function isa(v, x)
if type(x) == 'string' then
return type(v) == x,
'expected ' .. tostring(v) .. ' to be a ' .. x,
'expected ' .. tostring(v) .. ' to not be a ' .. x
elseif type(x) == 'table' then
if type(v) ~= 'table' then
return false,
'expected ' .. tostring(v) .. ' to be a ' .. tostring(x),
'expected ' .. tostring(v) .. ' to not be a ' .. tostring(x)
end
local seen = {}
local meta = v
while meta and not seen[meta] do
if meta == x then return true end
seen[meta] = true
meta = getmetatable(meta) and getmetatable(meta).__index
end
return false,
'expected ' .. tostring(v) .. ' to be a ' .. tostring(x),
'expected ' .. tostring(v) .. ' to not be a ' .. tostring(x)
end
error('invalid type ' .. tostring(x))
end
local function strict_eq(t1, t2)
if type(t1) ~= type(t2) then return false end
if type(t1) ~= 'table' then return t1 == t2 end
for k, _ in pairs(t1) do
if not strict_eq(t1[k], t2[k]) then return false end
end
for k, _ in pairs(t2) do
if not strict_eq(t2[k], t1[k]) then return false end
end
return true
end
local paths = {
[''] = { 'to', 'to_not' },
to = { 'have', 'equal', 'be', 'exist', 'fail', 'match' },
to_not = { 'have', 'equal', 'be', 'exist', 'fail', 'match', chain = function(a) a.negate = not a.negate end },
a = { test = isa },
an = { test = isa },
be = { 'a', 'an', 'truthy',
test = function(v, x)
return v == x,
'expected ' .. tostring(v) .. ' and ' .. tostring(x) .. ' to be equal',
'expected ' .. tostring(v) .. ' and ' .. tostring(x) .. ' to not be equal'
end
},
exist = {
test = function(v)
return v ~= nil,
'expected ' .. tostring(v) .. ' to exist',
'expected ' .. tostring(v) .. ' to not exist'
end
},
truthy = {
test = function(v)
return v,
'expected ' .. tostring(v) .. ' to be truthy',
'expected ' .. tostring(v) .. ' to not be truthy'
end
},
equal = {
test = function(v, x)
return strict_eq(v, x),
'expected ' .. tostring(v) .. ' and ' .. tostring(x) .. ' to be exactly equal',
'expected ' .. tostring(v) .. ' and ' .. tostring(x) .. ' to not be exactly equal'
end
},
have = {
test = function(v, x)
if type(v) ~= 'table' then
error('expected ' .. tostring(v) .. ' to be a table')
end
return has(v, x),
'expected ' .. tostring(v) .. ' to contain ' .. tostring(x),
'expected ' .. tostring(v) .. ' to not contain ' .. tostring(x)
end
},
fail = {
test = function(v)
return not pcall(v),
'expected ' .. tostring(v) .. ' to fail',
'expected ' .. tostring(v) .. ' to not fail'
end
},
match = {
test = function(v, p)
if type(v) ~= 'string' then v = tostring(v) end
local result = string.find(v, p)
return result ~= nil,
'expected ' .. v .. ' to match pattern [[' .. p .. ']]',
'expected ' .. v .. ' to not match pattern [[' .. p .. ']]'
end
},
}
function lust.expect(v)
local assertion = {}
assertion.val = v
assertion.action = ''
assertion.negate = false
setmetatable(assertion, {
__index = function(t, k)
if has(paths[rawget(t, 'action')], k) then
rawset(t, 'action', k)
local chain = paths[rawget(t, 'action')].chain
if chain then chain(t) end
return t
end
return rawget(t, k)
end,
__call = function(t, ...)
if paths[t.action].test then
local res, err, nerr = paths[t.action].test(t.val, ...)
if assertion.negate then
res = not res
err = nerr or err
end
if not res then
error(err or 'unknown failure', 2)
end
end
end
})
return assertion
end
function lust.spy(target, name, run)
local spy = {}
local subject
local function capture(...)
table.insert(spy, {...})
return subject(...)
end
if type(target) == 'table' then
subject = target[name]
target[name] = capture
else
run = name
subject = target or function() end
end
setmetatable(spy, {__call = function(_, ...) return capture(...) end})
if run then run() end
return spy
end
lust.test = lust.it
lust.paths = paths
return lust

View File

@ -1,5 +1,5 @@
(import-macros {: is-matching : describe : it : before-each} :test) (import-macros {: is-matching : describe : it : before-each} :test)
(local is (require :luassert)) (local is (require :test.is))
(local {: view &as fennel} (require :fennel)) (local {: view &as fennel} (require :fennel))
(local {: setup-server (local {: setup-server

View File

@ -1,5 +1,5 @@
(import-macros {: is-matching : describe : it : before-each} :test) (import-macros {: is-matching : describe : it : before-each} :test)
(local is (require :luassert)) (local is (require :test.is))
(local {: view} (require :fennel)) (local {: view} (require :fennel))
(local {: ROOT-URI (local {: ROOT-URI

View File

@ -1,5 +1,5 @@
(import-macros {: is-matching : describe : it} :test) (import-macros {: is-matching : describe : it} :test)
(local is (require :luassert)) (local is (require :test.is))
(local fennel (require :fennel)) (local fennel (require :fennel))
(local utils (require :fennel-ls.utils)) (local utils (require :fennel-ls.utils))