Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create NodeMCU test system NTest based on gambiarra #2984

Merged
merged 36 commits into from Nov 8, 2020
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f5186c4
Create mispec_file.lua
HHHartmann Dec 24, 2019
0e2ff89
Initial commit of gambiarra
HHHartmann Jan 1, 2020
c764c95
Adapt gambiarra to NodeMCU
HHHartmann Jan 19, 2020
586d8ce
adapt to NodeMCU spacing and add nok functionality
HHHartmann Jan 19, 2020
5de4e2d
Some refactoring to make it easier to add new functionality
HHHartmann Jan 19, 2020
7a52b5c
Add methode `fail` to check failing code and pass error messages to o…
HHHartmann Jan 20, 2020
80ef63c
Create gambiarra_file.lua
HHHartmann Jan 20, 2020
12c8062
Add reporting of tests that failed with Lua error
HHHartmann Jul 5, 2020
30abc60
ok, nok and fail will terminate the running test
HHHartmann Jul 5, 2020
a7558c2
Add capability to run sync and async tests in mixed order and have a …
HHHartmann Jul 5, 2020
e999481
fix gambiarra self test to also run on device (not only host)
HHHartmann Jul 7, 2020
0340d42
Update file tests + add async tmr tests
HHHartmann Jul 7, 2020
50acd3f
Another fix in executing async test
HHHartmann Jul 14, 2020
55965c6
Catch errors in callbacks using node.setonerror
HHHartmann Jul 18, 2020
dc6445e
change interface to return an object with several test methods
HHHartmann Aug 1, 2020
14c9b8d
Update README.md
HHHartmann Aug 1, 2020
6ea3665
Change interface of Gambiarra + add reason for failed eq
HHHartmann Sep 19, 2020
23a9dab
Update gambiarra documentation
HHHartmann Sep 19, 2020
59c4962
Add coroutine testcases to gambiarra
HHHartmann Oct 5, 2020
e313395
Delete mispec_file.lua as it is superseeded by gambiarra_file.lua
HHHartmann Oct 6, 2020
5c4cefd
improve regexp for stack frame extraction
HHHartmann Oct 26, 2020
6b616d8
Use Lua 53 debug capabilities
HHHartmann Oct 31, 2020
2248316
move actual tests upfront
HHHartmann Oct 31, 2020
d3fc6e1
remove debug code + optimization
HHHartmann Oct 31, 2020
42af254
Show errors immediately instead of at the end of the test, freeing me…
HHHartmann Oct 31, 2020
832fc86
Split tests to be run in 2 tranches
HHHartmann Oct 31, 2020
fdbfe10
rename to NTest and move to new location
HHHartmann Nov 2, 2020
419b837
Add tests to checking mechanisms
HHHartmann Nov 2, 2020
4d16176
Add luacheck to tests
HHHartmann Nov 6, 2020
c6c979b
Some pushing around of files
HHHartmann Nov 6, 2020
c68e4aa
more (last) fixes and file juggling
HHHartmann Nov 7, 2020
42e9436
Minor tweaks and forgotten checkin
HHHartmann Nov 7, 2020
8ce26d4
Add NTest selftest to travis
HHHartmann Nov 7, 2020
2e0a5cd
Trying how to master travis
HHHartmann Nov 7, 2020
ad7b8e9
another try
HHHartmann Nov 7, 2020
b5c53f6
restrict NTest selftest to linux
HHHartmann Nov 7, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions .travis.yml
Expand Up @@ -32,7 +32,7 @@ script:
- if [ "$OS" = "linux" -a "$TRAVIS_PULL_REQUEST" != "false" ]; then bash "$TRAVIS_BUILD_DIR"/tools/travis/pr-build.sh; fi
- cd "$TRAVIS_BUILD_DIR"
- echo "checking:"
- find lua_modules lua_examples -iname "*.lua" -print0 | xargs -0 echo
- find lua_modules lua_examples -iname "*.lua" -print0 | xargs -0 $LUACC -p
- find lua_modules lua_examples tests/NTest -iname "*.lua" -print0 | xargs -0 echo
- find lua_modules lua_examples tests/NTest -iname "*.lua" -print0 | xargs -0 $LUACC -p
- if [ "$OS" = "linux" ]; then bash "$TRAVIS_BUILD_DIR"/tools/travis/run-luacheck-linux.sh; fi
- if [ "$OS" = "windows" ]; then bash "$TRAVIS_BUILD_DIR"/tools/travis/run-luacheck-windows.sh; fi
303 changes: 303 additions & 0 deletions tests/NTest/NTest.lua
@@ -0,0 +1,303 @@
local function TERMINAL_HANDLER(e, test, msg, errormsg)
if errormsg then
errormsg = ": "..errormsg
else
errormsg = ""
end
if e == 'start' then
print("######## "..e.."ed "..test.." tests")
elseif e == 'pass' then
print(" "..e.." "..test..': '..msg)
elseif e == 'fail' then
print(" ==> "..e.." "..test..': '..msg..errormsg)
elseif e == 'except' then
print(" ==> "..e.." "..test..': '..msg..errormsg)
elseif e == 'finish' then
print("######## "..e.."ed "..test.." tests")
else
print(e.." "..test)
end
end

--[[
if equal returns true
if different returns {msg = "<reason>"}
this will be handled spechially by ok and nok
--]]
local function deepeq(a, b)
local function notEqual(m)
return { msg=m }
end

-- Different types: false
if type(a) ~= type(b) then return notEqual("type 1 is "..type(a)..", type 2 is "..type(b)) end
-- Functions
if type(a) == 'function' then
if string.dump(a) == string.dump(b) then
return true
else
return notEqual("functions differ")
end
end
-- Primitives and equal pointers
if a == b then return true end
-- Only equal tables could have passed previous tests
if type(a) ~= 'table' then return notEqual("different "..type(a).."s expected "..a.." vs. "..b) end
-- Compare tables field by field
for k,v in pairs(a) do
if b[k] == nil then return notEqual("key "..k.."only contained in left part") end
local result = deepeq(v, b[k])
if type(result) == 'table' then return result end
end
for k,v in pairs(b) do
if a[k] == nil then return notEqual("key "..k.."only contained in right part") end
local result = deepeq(a[k], v)
if type(result) == 'table' then return result end
end
return true
end

-- Compatibility for Lua 5.1 and Lua 5.2
local function args(...)
return {n=select('#', ...), ...}
end

local function spy(f)
local mt = {}
setmetatable(mt, {__call = function(s, ...)
s.called = s.called or {}
local a = args(...)
table.insert(s.called, {...})
if f then
local r
r = args(pcall(f, unpack(a, 1, a.n)))
if not r[1] then
s.errors = s.errors or {}
s.errors[#s.called] = r[2]
else
return unpack(r, 2, r.n)
end
end
end})
return mt
end

local function getstackframe()
-- debug.getinfo() does not exist in NodeMCU Lua 5.1
if debug.getinfo then
return debug.getinfo(5, 'S').short_src:match("([^\\/]*)$")..":"..debug.getinfo(5, 'l').currentline
end

local msg
msg = debug.traceback()
msg = msg:match("\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t([^\t]*): in") -- Get 5th stack frame
msg = msg:match(".-([^\\/]*)$") -- cut off path of filename
return msg
end

local function assertok(handler, name, invert, cond, msg)
local errormsg
-- check if cond is return object of 'eq' call
if type(cond) == 'table' and cond.msg then
errormsg = cond.msg
cond = false
end
if not msg then
msg = getstackframe()
end

if invert then
cond = not cond
end
if cond then
handler('pass', name, msg)
else
handler('fail', name, msg, errormsg)
error('_*_TestAbort_*_')
end
end

local function fail(handler, name, func, expected, msg)
local status, err = pcall(func)
if not msg then
msg = getstackframe()
end
if status then
local messageParts = {"Expected to fail with Error"}
if expected then
messageParts[2] = " containing \"" .. expected .. "\""
end
handler('fail', name, msg, table.concat(messageParts, ""))
error('_*_TestAbort_*_')
end
if (expected and not string.find(err, expected)) then
err = err:match(".-([^\\/]*)$") -- cut off path of filename
handler('fail', name, msg, "expected errormessage \"" .. err .. "\" to contain \"" .. expected .. "\"")
error('_*_TestAbort_*_')
end
handler('pass', name, msg)
end

local function NTest(testrunname, failoldinterface)

if failoldinterface then error("The interface has changed. Please see documentstion.") end

local pendingtests = {}
local env = _G
local outputhandler = TERMINAL_HANDLER
local started

local function runpending()
if pendingtests[1] ~= nil then
node.task.post(node.task.LOW_PRIORITY, function()
pendingtests[1](runpending)
end)
else
outputhandler('finish', testrunname)
end
end

local function copyenv(dest, src)
dest.eq = src.eq
dest.spy = src.spy
dest.ok = src.ok
dest.nok = src.nok
dest.fail = src.fail
end

local function testimpl(name, f, async)
local testfn = function(next)

local prev = {}
copyenv(prev, env)

local handler = outputhandler

local restore = function(err)
if err then
err = err:match(".-([^\\/]*)$") -- cut off path of filename
if not err:match('_*_TestAbort_*_') then
handler('except', name, err)
end
end
if node then node.setonerror() end
copyenv(env, prev)
outputhandler('end', name)
table.remove(pendingtests, 1)
collectgarbage()
if next then next() end
end

local function wrap(method, ...)
method(handler, name, ...)
end

local function cbError(err)
err = err:match(".-([^\\/]*)$") -- cut off path of filename
if not err:match('_*_TestAbort_*_') then
handler('except', name, err)
end
restore()
end

env.eq = deepeq
env.spy = spy
env.ok = function (cond, msg1, msg2) wrap(assertok, false, cond, msg1, msg2) end
env.nok = function(cond, msg1, msg2) wrap(assertok, true, cond, msg1, msg2) end
env.fail = function (func, expected, msg) wrap(fail, func, expected, msg) end

handler('begin', name)
node.setonerror(cbError)
local ok, err = pcall(f, async and restore)
if not ok then
err = err:match(".-([^\\/]*)$") -- cut off path of filename
if not err:match('_*_TestAbort_*_') then
handler('except', name, err)
end
if async then
restore()
end
end

if not async then
restore()
end
end

if not started then
outputhandler('start', testrunname)
started = true
end


table.insert(pendingtests, testfn)
if #pendingtests == 1 then
runpending()
end
end

local function test(name, f)
testimpl(name, f)
end

local function testasync(name, f)
testimpl(name, f, true)
end

local function report(f, envP)
if type(f) == 'function' then
outputhandler = f
env = envP or _G
end
end
nwf marked this conversation as resolved.
Show resolved Hide resolved

local currentCoName

local function testco(name, func)
-- local t = tmr.create();
local co
testasync(name, function(Next)
currentCoName = name

local function getCB(cbName)
return function(...) -- upval: co, cbName
local result, err = coroutine.resume(co, cbName, ...)
if (not result) then
if (name == currentCoName) then
currentCoName = nil
Next(err)
else
outputhandler('fail', name, "Found stray Callback '"..cbName.."' from test '"..name.."'")
end
elseif coroutine.status(co) == "dead" then
currentCoName = nil
Next()
end
end
end

local function waitCb()
return coroutine.yield()
end

co = coroutine.create(function(wr, wa)
func(wr, wa)
end)

local result, err = coroutine.resume(co, getCB, waitCb)
if (not result) then
currentCoName = nil
Next(err)
elseif coroutine.status(co) == "dead" then
currentCoName = nil
Next()
end
end)
end


return {test = test, testasync = testasync, testco = testco, report = report}
end

return NTest