From f5186c47de026ff8d1bfa8cf20b17cd1c909f106 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 24 Dec 2019 10:41:25 +0100 Subject: [PATCH 01/36] Create mispec_file.lua --- lua_tests/mispec_file.lua | 177 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 lua_tests/mispec_file.lua diff --git a/lua_tests/mispec_file.lua b/lua_tests/mispec_file.lua new file mode 100644 index 0000000000..651c1137c9 --- /dev/null +++ b/lua_tests/mispec_file.lua @@ -0,0 +1,177 @@ +require 'mispec' + +describe('file', function(it) + +-- it:initialize(function() end) + + it:cleanup(function() + file.remove("testfile") + file.remove("testfile2") + local testfiles = {"testfile1&", "testFILE2"} + for _, n in ipairs(testfiles) do + file.remove(n,n) + end + end) + + it:should('exist', function() + ko(file.exists("non existant file"), "non existant file") + + file.putcontents("testfile", "testcontents") + ok(file.exists("testfile")) + end) + + it:should('fscfg', function() + local start, size = file.fscfg() + ok(start, "start") + ok(size, "size") + end) + + it:should('fsinfo', function() + local remaining, used, total = file.fsinfo() + ok(remaining, "remaining") + ok(used, "used") + ok(total, "total") + ok(eq(remaining+used, total), "size maths") + end) + + it:should('getcontents', function() + local testcontent = "some content \0 and more" + file.putcontents("testfile", testcontent) + local content = file.getcontents("testfile") + ok(testcontent, content) + end) + + it:should('getcontents non existent file', function() + ko(file.getcontents("non existant file"), "non existent file") + end) + + it:should('getcontents more than 1K', function() + local f = file.open("testfile", "w") + local i + for i = 1,100 do + f:write("some text to test") + end + f:close() + content = file.getcontents("testfile") + eq(#content, 1700, "partial read") + end) + + it:should('list', function() + local files + + local function count(files) + local filecount = 0 + for k,v in pairs(files) do filecount = filecount+1 end + return filecount + end + + local function testfile(name) + ok(eq(files[name],#name)) + end + + local testfiles = {"testfile1&", "testFILE2"} + for _, n in ipairs(testfiles) do + file.putcontents(n,n) + end + + files = file.list("testfile%.*") + ok(eq(count(files), 1)) + testfile("testfile1&") + + files = file.list("^%l*%u+%d%.-") + ok(eq(count(files), 1)) + testfile("testFILE2") + + files = file.list() + ok(count(files) >= 2) + end) + + it:should('open non existing', function() + local function testopen(outcome, filename, mode) + outcome(file.open(filename, mode), mode) + file.close() + file.remove(filename) + end + + testopen(ko, "testfile", "r") + testopen(ok, "testfile", "w") + testopen(ok, "testfile", "a") + testopen(ko, "testfile", "r+") + testopen(ok, "testfile", "w+") + testopen(ok, "testfile", "a+") + + fail(file.open, "testfile", "x") -- shouldn't this fail? + end) + + it:should('open existing', function() + local function testopen(mode, position) + file.putcontents("testfile", "testfile") + ok(file.open("testfile", mode), mode) + file.write("") + ok(eq(file.seek(), position)) + file.close() + end + + testopen("r", 0) + testopen("w", 0) + testopen("a", 8) + testopen("r+", 0) + testopen("w+", 0) + testopen("a+", 8) + + fail(file.open, "testfile", "x") -- shouldn't this fail? + end) + + it:should('remove', function() + file.putcontents("testfile", "testfile") + + ok(file.remove("testfile") == nil, "existing file") + ok(file.remove("testfile") == nil, "non existing file") + end) + + it:should('rename', function() + file.putcontents("testfile", "testfile") + + ok(file.rename("testfile", "testfile2"), "rename existing") + ko(file.exists("testfile"), "old file removed") + ok(file.exists("testfile2"), "new file exists") + + ko(file.rename("testfile", "testfile3"), "rename non existing") + + file.putcontents("testfile", "testfile") + + ko(file.rename("testfile", "testfile2"), "rename to existing") + ok(file.exists("testfile"), "from file exists") + ok(file.exists("testfile2"), "to file exists") + end) + + it:should('stat existing file', function() + file.putcontents("testfile", "testfile") + + local stat = file.stat("testfile") + + ko(stat == nil, "stat existing") + ok(eq(stat.size, 8), "size") + ok(eq(stat.name, "testfile"), "name") + ko(stat.time == nil, "no time") + ok(eq(stat.time.year, 1970), "year") + ok(eq(stat.time.mon, 01), "mon") + ok(eq(stat.time.day, 01), "day") + ok(eq(stat.time.hour, 0), "hour") + ok(eq(stat.time.min, 0), "min") + ok(eq(stat.time.sec, 0), "sec") + ko(stat.is_dir, "is_dir") + ko(stat.is_rdonly, "is_rdonly") + ko(stat.is_hidden, "is_hidden") + ko(stat.is_sys, "is_sys") + ko(stat.is_arch, "is_arch") + end) + + it:should('stat non existing file', function() + local stat = file.stat("not existing file") + + ok(stat == nil, "stat empty") + end) +end) + +mispec.run() From 0e2ff892fb416b52391aebbb922da51fc429a955 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 1 Jan 2020 19:36:46 +0100 Subject: [PATCH 02/36] Initial commit of gambiarra --- lua_tests/gambiarra/README.md | 122 +++++++++++++ lua_tests/gambiarra/gambiarra.lua | 126 +++++++++++++ lua_tests/gambiarra/gambiarra_test.lua | 234 +++++++++++++++++++++++++ 3 files changed, 482 insertions(+) create mode 100644 lua_tests/gambiarra/README.md create mode 100644 lua_tests/gambiarra/gambiarra.lua create mode 100644 lua_tests/gambiarra/gambiarra_test.lua diff --git a/lua_tests/gambiarra/README.md b/lua_tests/gambiarra/README.md new file mode 100644 index 0000000000..f42965b570 --- /dev/null +++ b/lua_tests/gambiarra/README.md @@ -0,0 +1,122 @@ +# Gambiarra + +Gambiarra is a Lua version of Kludjs, and follows the idea of ultimately +minimal unit testing. + +## Install + +Get the sources: + +`hg clone https://bitbucket.org/zserge/gambiarra` + +Or get only `gambiarra.lua` and start writing tests: + +`wget https://bitbucket.org/zserge/gambiarra/raw/tip/gambiarra.lua` + +## Example + + -- Simple synchronous test + test('Check dogma', function() + ok(2+2 == 5, 'two plus two equals five') + end) + + -- A more advanced asyncrhonous test + test('Do it later', function(done) + someAsyncFn(function(result) + ok(result == expected) + done() -- this starts the next async test + end) + end, true) -- 'true' means 'async test' here + +## API + +`require('gambiarra')` returns a test function which can also be used to +customize test reports: + + local test = require('gambiarra') + +`test(name:string, f:function, [async:bool])` allows you to define a new test: + + test('My sync test', function() + end) + + test('My async test', function(done) + done() + end, true) + +`test()` defines also three helper functions that are added when test is +executed - `ok`, `eq` and `spy`. + +`ok(cond:bool, [msg:string])` is a simple assertion helper. It takes any +boolean condition and an optional assertion message. If no message is define - +current filename and line will be used. + + ok(1 == 1) -- prints 'foo.lua:42' + ok(1 == 1, 'one equals one') -- prints 'one equals one' + +`eq(a, b)` is a helper to deeply compare lua variables. It supports numbers, +strings, booleans, nils, functions and tables. It's mostly useful within ok(): + + ok(eq(1, 1)) + ok(eq('foo', 'bar')) + ok(eq({a='b',c='d'}, {c='d',a='b'}) + +Finally, `spy([f])` creates function wrappers that remember each their call +(arguments, errors) but behaves much like the real function. Real function is +optional, in this case spy will return nil, but will still record its calls. +Spies are most helpful when passing them as callbacks and testing that they +were called with correct values. + + local f = spy(function(s) return #s end) + ok(f('hello') == 5) + ok(f('foo') == 3) + ok(#f.called == 2) + ok(eq(f.called[1], {'hello'}) + ok(eq(f.called[2], {'foo'}) + f(nil) + ok(f.errors[3] ~= nil) + +## Reports + +Another useful feature is that you can customize test reports as you need. +But default tests are printed in color using ANSI escape sequences and use +UTF-8 symbols to indicate passed/failed state. If your environment doesn't +support it - you can easily override this behavior as well as add any other +information you need (number of passed/failed assertions, time the test took +etc): + + local passed = 0 + local failed = 0 + local clock = 0 + + test(function(event, testfunc, msg) + if event == 'begin' then + print('Started test', testfunc) + passed = 0 + failed = 0 + clock = os.clock() + elseif event == 'end' then + print('Finished test', testfunc, passed, failed, os.clock() - clock) + elseif event == 'pass' then + passed = passed + 1 + elseif event == 'fail' then + print('FAIL', testfunc, msg) + failed = failed + 1 + elseif event == 'except' then + print('ERROR', testfunc, msg) + end + end) + +Additionally, you can pass a different environment to keep `_G` unpolluted: + + test(function() ... end, myenv) + + test('Some test', function() + myenv.ok(myenv.eq(...)) + local f = myenv.spy() + end) + +## Appendix + +Library supports Lua 5.1 and Lua 5.2. It is distributed under the MIT license. +Enjoy! diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua new file mode 100644 index 0000000000..8b653faa32 --- /dev/null +++ b/lua_tests/gambiarra/gambiarra.lua @@ -0,0 +1,126 @@ +local function TERMINAL_HANDLER(e, test, msg) + if e == 'pass' then + print("✔ "..test..': '..msg) + elseif e == 'fail' then + print("✘ "..test..': '..msg) + elseif e == 'except' then + print("✘ "..test..': '..msg) + end +end + +local function deepeq(a, b) + -- Different types: false + if type(a) ~= type(b) then return false end + -- Functions + if type(a) == 'function' then + return string.dump(a) == string.dump(b) + 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 false end + -- Compare tables field by field + for k,v in pairs(a) do + if b[k] == nil or not deepeq(v, b[k]) then return false end + end + for k,v in pairs(b) do + if a[k] == nil or not deepeq(v, a[k]) then return false 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 s = {} + setmetatable(s, {__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 or table.unpack)(a, 1, a.n))) + if not r[1] then + s.errors = s.errors or {} + s.errors[#s.called] = r[2] + else + return (unpack or table.unpack)(r, 2, r.n) + end + end + end}) + return s +end + +local pendingtests = {} +local env = _G +local gambiarrahandler = TERMINAL_HANDLER + +local function runpending() + if pendingtests[1] ~= nil then pendingtests[1](runpending) end +end + +return function(name, f, async) + if type(name) == 'function' then + gambiarrahandler = name + env = f or _G + return + end + + local testfn = function(next) + + local prev = { + ok = env.ok, + spy = env.spy, + eq = env.eq + } + + local restore = function() + env.ok = prev.ok + env.spy = prev.spy + env.eq = prev.eq + gambiarrahandler('end', name) + table.remove(pendingtests, 1) + if next then next() end + end + + local handler = gambiarrahandler + + env.eq = deepeq + env.spy = spy + env.ok = function(cond, msg) + if not msg then + msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline + end + if cond then + handler('pass', name, msg) + else + handler('fail', name, msg) + end + end + + handler('begin', name); + local ok, err = pcall(f, restore) + if not ok then + handler('except', name, err) + end + + if not async then + handler('end', name); + env.ok = prev.ok; + env.spy = prev.spy; + env.eq = prev.eq; + end + end + + if not async then + testfn() + else + table.insert(pendingtests, testfn) + if #pendingtests == 1 then + runpending() + end + end +end diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua new file mode 100644 index 0000000000..5bf51784ee --- /dev/null +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -0,0 +1,234 @@ +local test = require('gambiarra') + +local actual = {} +local expected = {} + +-- Set meta test handler +test(function(e, test, msg) + if e == 'begin' then + currentTest = { + name = test, + pass = {}, + fail = {} + } + elseif e == 'end' then + table.insert(actual, currentTest) + elseif e == 'pass' then + table.insert(currentTest.pass, msg) + elseif e == 'fail' then + table.insert(currentTest.fail, msg) + elseif e == 'except' then + print('*** PANIC ***: ', test, msg) + end +end) + +-- Helper function to print arrays +local function stringify(t) + local s = '' + for i=1,#t do + s = s .. '"' .. t[i] .. '"' .. ' ' + end + return s:gsub('%s*$', '') +end + +-- Helper function to compare two tables +local function comparetables(t1, t2) + if #t1 ~= #t2 then return false end + for i=1,#t1 do + if t1[i] ~= t2[i] then return false end + end + return true +end + +local function metatest(name, f, expectedPassed, expectedFailed, async) + test(name, f, async) + table.insert(expected, { + name = name, + pass = expectedPassed, + fail = expectedFailed + }) +end + +-- +-- Basic tests +-- +metatest('simple assert passes', function() + ok(2 == 2, '2==2') +end, {'2==2'}, {}) + +metatest('simple assert fails', function() + ok(1 == 2, '1~=2') +end, {}, {'1~=2'}) + +metatest('ok without a message', function() + ok(1 == 1) + ok(1 == 2) +end, {'gambiarra_test.lua:64'}, {'gambiarra_test.lua:65'}) + +-- +-- Equality tests +-- +metatest('eq nil', function() + ok(eq(nil, nil), 'nil == nil') +end, {'nil == nil'}, {}) + +metatest('eq primitives', function() + ok(eq('foo', 'foo'), 'str == str') + ok(eq('foo', 'bar'), 'str != str') + ok(eq(123, 123), 'num == num') + ok(eq(123, 12), 'num != num') +end, {'str == str', 'num == num'}, {'str != str', 'num != num'}) + +metatest('eq arrays', function() + ok(eq({}, {}), 'empty') + ok(eq({1, 2}, {1, 2}), 'equal') + ok(eq({1, 2}, {2, 1}), 'swapped') + ok(eq({1, 2, 3}, {1, 2}), 'longer') + ok(eq({1, 2}, {1, 2, 3}), 'shorter') +end, {'empty', 'equal'}, {'swapped', 'longer', 'shorter'}) + +metatest('eq objects', function() + ok(eq({}, {}), 'empty') + ok(eq({a=1,b=2}, {a=1,b=2}), 'equal') + ok(eq({b=2,a=1}, {a=1,b=2}), 'swapped') + ok(eq({a=1,b=2}, {a=1,b=3}), 'not equal') +end, {'empty', 'equal', 'swapped'}, {'not equal'}) + +metatest('eq nested objects', function() + ok(eq({ + ['1'] = { name = 'mhc', age = 28 }, + ['2'] = { name = 'arb', age = 26 } + }, { + ['1'] = { name = 'mhc', age = 28 }, + ['2'] = { name = 'arb', age = 26 } + }), 'equal') + ok(eq({ + ['1'] = { name = 'mhc', age = 28 }, + ['2'] = { name = 'arb', age = 26 } + }, { + ['1'] = { name = 'mhc', age = 28 }, + ['2'] = { name = 'arb', age = 27 } + }), 'not equal') +end, {'equal'}, {'not equal'}) + +metatest('eq functions', function() + ok(eq(function(x) return x end, function(x) return x end), 'equal') + ok(eq(function(x) return x end, function(y) return y end), 'wrong variable') + ok(eq(function(x) return x end, function(x) return x+2 end), 'wrong code') +end, {'equal'}, {'wrong variable', 'wrong code'}) + +-- +-- Spies +-- +metatest('spy called', function() + local f = spy() + ok(not f.called or #f.called == 0, 'not called') + f() + ok(f.called, 'called') + ok(#f.called == 1, 'called once') + f() + ok(#f.called == 2, 'called twice') +end, {'not called', 'called', 'called once', 'called twice'}, {}) + +metatest('spy with arguments', function() + local x = 0 + function setX(n) x = n end + local f = spy(setX) + f(1) + ok(x == 1, 'x == 1') + ok(eq(f.called, {{1}}), 'called with 1') + f(42) + ok(x == 42, 'x == 42') + ok(eq(f.called, {{1}, {42}}), 'called with 42') +end, {'x == 1', 'called with 1', 'x == 42', 'called with 42'}, {}) + +metatest('spy with nils', function() + function nils(a, dummy, b) return a, nil, b, nil end + local f = spy(nils) + r1, r2, r3, r4 = f(1, nil, 2) + ok(eq(f.called, {{1, nil, 2}}), 'nil in args') + ok(r1 == 1 and r2 == nil and r3 == 2 and r4 == nil, 'nil in returns') +end, {'nil in args', 'nil in returns'}, {}) + +metatest('spy with exception', function() + function throwSomething(s) + if s ~= 'nopanic' then error('panic: '..s) end + end + local f = spy(throwSomething) + f('nopanic') + ok(f.errors == nil, 'no errors yet') + f('foo') + ok(eq(f.called, {{'nopanic'}, {'foo'}}), 'args ok') + ok(f.errors[1] == nil and f.errors[2] ~= nil, 'thrown ok') +end, {'no errors yet', 'args ok', 'thrown ok'}, {}) + +metatest('another spy with exception', function() + local f = spy(function() local a = unknownVariable + 1 end) + f() + ok(f.errors[1], 'exception thrown') +end, {'exception thrown'}, {}) + +metatest('spy should return a value', function() + local f = spy(function() return 5 end) + ok(f() == 5, 'spy returns a value') + local g = spy() + ok(g() == nil, 'default spy returns undefined') +end, {'spy returns a value', 'default spy returns undefined'}, {}) + +-- +-- Async tests +-- +local queue = {} +local function async(f) table.insert(queue, f) end +local function async_next() + local f = table.remove(queue, 1) + if f then f() end +end + +metatest('async test', function(next) + async(function() + ok(true, 'bar') + async(function() + ok(true, 'baz') + next() + end) + end) + ok(true, 'foo') +end, {'foo', 'bar', 'baz'}, {}, true) + +metatest('async test without actually async', function(next) + ok(true, 'bar') + next() +end, {'bar'}, {}, true) + +async_next() +async_next() + +metatest('another async test after async queue drained', function(next) + async(function() + ok(true, 'bar') + next() + end) + ok(true, 'foo') +end, {'foo', 'bar'}, {}, true) + +async_next() + +-- +-- Finalize: check test results +-- +for i = 1,#expected do + if actual[i] == nil then + print("✘ "..expected[i].name..' (pending)') + elseif not comparetables(expected[i].pass, actual[i].pass) then + print("✘ "..expected[i].name..' (passed): ['.. + stringify(expected[i].pass)..'] vs ['.. + stringify(actual[i].pass)..']') + elseif not comparetables(expected[i].fail, actual[i].fail) then + print("✘ "..expected[i].name..' (failed): ['.. + stringify(expected[i].fail)..'] vs ['.. + stringify(actual[i].fail)..']') + else + print("✔ "..expected[i].name) + end +end From c764c952e3d1afedc746903f869becdf59967b52 Mon Sep 17 00:00:00 2001 From: Gregor Date: Sun, 19 Jan 2020 08:37:17 +0100 Subject: [PATCH 03/36] Adapt gambiarra to NodeMCU --- lua_tests/gambiarra/gambiarra.lua | 11 +++++++---- lua_tests/gambiarra/gambiarra_test.lua | 10 +++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index 8b653faa32..2f8d9eb7bb 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -1,10 +1,10 @@ local function TERMINAL_HANDLER(e, test, msg) if e == 'pass' then - print("✔ "..test..': '..msg) + print(e.." "..test..': '..msg) elseif e == 'fail' then - print("✘ "..test..': '..msg) + print(e.." "..test..': '..msg) elseif e == 'except' then - print("✘ "..test..': '..msg) + print(e.." "..test..': '..msg) end end @@ -92,7 +92,10 @@ return function(name, f, async) env.spy = spy env.ok = function(cond, msg) if not msg then - msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline + -- debug.getinfo() does not exist in NodeMCU + -- msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline + msg = debug.traceback() + msg = msg:match("\n[^\n]*\n\t*([^\n]*): in") end if cond then handler('pass', name, msg) diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index 5bf51784ee..9f336802a4 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -113,7 +113,7 @@ end, {'equal'}, {'not equal'}) metatest('eq functions', function() ok(eq(function(x) return x end, function(x) return x end), 'equal') - ok(eq(function(x) return x end, function(y) return y end), 'wrong variable') + ok(eq(function(z) return x end, function(z) return y end), 'wrong variable') ok(eq(function(x) return x end, function(x) return x+2 end), 'wrong code') end, {'equal'}, {'wrong variable', 'wrong code'}) @@ -219,16 +219,16 @@ async_next() -- for i = 1,#expected do if actual[i] == nil then - print("✘ "..expected[i].name..' (pending)') + print("--- FAIL "..expected[i].name..' (pending)') elseif not comparetables(expected[i].pass, actual[i].pass) then - print("✘ "..expected[i].name..' (passed): ['.. + print("--- FAIL "..expected[i].name..' (passed): ['.. stringify(expected[i].pass)..'] vs ['.. stringify(actual[i].pass)..']') elseif not comparetables(expected[i].fail, actual[i].fail) then - print("✘ "..expected[i].name..' (failed): ['.. + print("--- FAIL "..expected[i].name..' (failed): ['.. stringify(expected[i].fail)..'] vs ['.. stringify(actual[i].fail)..']') else - print("✔ "..expected[i].name) + print("+++ Pass "..expected[i].name) end end From 586d8ce4248bc9e3d4796d87d078547559543137 Mon Sep 17 00:00:00 2001 From: Gregor Date: Sun, 19 Jan 2020 09:15:39 +0100 Subject: [PATCH 04/36] adapt to NodeMCU spacing and add nok functionality --- lua_tests/gambiarra/gambiarra.lua | 199 +++++++++---------- lua_tests/gambiarra/gambiarra_test.lua | 262 +++++++++++++------------ 2 files changed, 235 insertions(+), 226 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index 2f8d9eb7bb..e5827b89e1 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -1,57 +1,57 @@ local function TERMINAL_HANDLER(e, test, msg) - if e == 'pass' then - print(e.." "..test..': '..msg) - elseif e == 'fail' then - print(e.." "..test..': '..msg) - elseif e == 'except' then - print(e.." "..test..': '..msg) - end + if e == 'pass' then + print(e.." "..test..': '..msg) + elseif e == 'fail' then + print(e.." "..test..': '..msg) + elseif e == 'except' then + print(e.." "..test..': '..msg) + end end local function deepeq(a, b) - -- Different types: false - if type(a) ~= type(b) then return false end - -- Functions - if type(a) == 'function' then - return string.dump(a) == string.dump(b) - 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 false end - -- Compare tables field by field - for k,v in pairs(a) do - if b[k] == nil or not deepeq(v, b[k]) then return false end - end - for k,v in pairs(b) do - if a[k] == nil or not deepeq(v, a[k]) then return false end - end - return true + -- Different types: false + if type(a) ~= type(b) then return false end + -- Functions + if type(a) == 'function' then + return string.dump(a) == string.dump(b) + 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 false end + -- Compare tables field by field + for k,v in pairs(a) do + if b[k] == nil or not deepeq(v, b[k]) then return false end + end + for k,v in pairs(b) do + if a[k] == nil or not deepeq(v, a[k]) then return false end + end + return true end -- Compatibility for Lua 5.1 and Lua 5.2 local function args(...) - return {n=select('#', ...), ...} + return {n=select('#', ...), ...} end local function spy(f) - local s = {} - setmetatable(s, {__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 or table.unpack)(a, 1, a.n))) - if not r[1] then - s.errors = s.errors or {} - s.errors[#s.called] = r[2] - else - return (unpack or table.unpack)(r, 2, r.n) - end - end - end}) - return s + local s = {} + setmetatable(s, {__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 or table.unpack)(a, 1, a.n))) + if not r[1] then + s.errors = s.errors or {} + s.errors[#s.called] = r[2] + else + return (unpack or table.unpack)(r, 2, r.n) + end + end + end}) + return s end local pendingtests = {} @@ -59,71 +59,72 @@ local env = _G local gambiarrahandler = TERMINAL_HANDLER local function runpending() - if pendingtests[1] ~= nil then pendingtests[1](runpending) end + if pendingtests[1] ~= nil then pendingtests[1](runpending) end end return function(name, f, async) - if type(name) == 'function' then - gambiarrahandler = name - env = f or _G - return - end + if type(name) == 'function' then + gambiarrahandler = name + env = f or _G + return + end - local testfn = function(next) + local testfn = function(next) - local prev = { - ok = env.ok, - spy = env.spy, - eq = env.eq - } + local prev = { + ok = env.ok, + spy = env.spy, + eq = env.eq + } - local restore = function() - env.ok = prev.ok - env.spy = prev.spy - env.eq = prev.eq - gambiarrahandler('end', name) - table.remove(pendingtests, 1) - if next then next() end - end + local restore = function() + env.ok = prev.ok + env.spy = prev.spy + env.eq = prev.eq + gambiarrahandler('end', name) + table.remove(pendingtests, 1) + if next then next() end + end - local handler = gambiarrahandler + local handler = gambiarrahandler - env.eq = deepeq - env.spy = spy - env.ok = function(cond, msg) - if not msg then - -- debug.getinfo() does not exist in NodeMCU - -- msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline - msg = debug.traceback() - msg = msg:match("\n[^\n]*\n\t*([^\n]*): in") - end - if cond then - handler('pass', name, msg) - else - handler('fail', name, msg) - end - end + env.eq = deepeq + env.spy = spy + env.ok = function (cond, msg) + if not msg then + -- debug.getinfo() does not exist in NodeMCU + -- msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline + msg = debug.traceback() + msg = msg:match("\n[^\n]*\n\t*([^\n]*): in") + end + if cond then + handler('pass', name, msg) + else + handler('fail', name, msg) + end + end + env.nok = function(cond, msg) env.ok(not cond, msg) end - handler('begin', name); - local ok, err = pcall(f, restore) - if not ok then - handler('except', name, err) - end + handler('begin', name); + local ok, err = pcall(f, restore) + if not ok then + handler('except', name, err) + end - if not async then - handler('end', name); - env.ok = prev.ok; - env.spy = prev.spy; - env.eq = prev.eq; - end - end + if not async then + handler('end', name); + env.ok = prev.ok; + env.spy = prev.spy; + env.eq = prev.eq; + end + end - if not async then - testfn() - else - table.insert(pendingtests, testfn) - if #pendingtests == 1 then - runpending() - end - end + if not async then + testfn() + else + table.insert(pendingtests, testfn) + if #pendingtests == 1 then + runpending() + end + end end diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index 9f336802a4..f9d91fafdb 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -5,30 +5,30 @@ local expected = {} -- Set meta test handler test(function(e, test, msg) - if e == 'begin' then - currentTest = { - name = test, - pass = {}, - fail = {} - } - elseif e == 'end' then - table.insert(actual, currentTest) - elseif e == 'pass' then - table.insert(currentTest.pass, msg) - elseif e == 'fail' then - table.insert(currentTest.fail, msg) - elseif e == 'except' then - print('*** PANIC ***: ', test, msg) - end + if e == 'begin' then + currentTest = { + name = test, + pass = {}, + fail = {} + } + elseif e == 'end' then + table.insert(actual, currentTest) + elseif e == 'pass' then + table.insert(currentTest.pass, msg) + elseif e == 'fail' then + table.insert(currentTest.fail, msg) + elseif e == 'except' then + print('*** PANIC ***: ', test, msg) + end end) -- Helper function to print arrays local function stringify(t) - local s = '' + local s = '' for i=1,#t do - s = s .. '"' .. t[i] .. '"' .. ' ' - end - return s:gsub('%s*$', '') + s = s .. '"' .. t[i] .. '"' .. ' ' + end + return s:gsub('%s*$', '') end -- Helper function to compare two tables @@ -41,138 +41,146 @@ local function comparetables(t1, t2) end local function metatest(name, f, expectedPassed, expectedFailed, async) - test(name, f, async) - table.insert(expected, { - name = name, - pass = expectedPassed, - fail = expectedFailed - }) + test(name, f, async) + table.insert(expected, { + name = name, + pass = expectedPassed, + fail = expectedFailed + }) end -- -- Basic tests -- metatest('simple assert passes', function() - ok(2 == 2, '2==2') + ok(2 == 2, '2==2') end, {'2==2'}, {}) +metatest('simple negative assert fails', function() + ok(2 == 2, '2==2') +end, {}, {'2==2'}) + metatest('simple assert fails', function() - ok(1 == 2, '1~=2') + ok(1 == 2, '1~=2') end, {}, {'1~=2'}) +metatest('simple negative assert passe', function() + nok(1 == 2, '1~=2') +end, {'1~=2'}, {}) + metatest('ok without a message', function() - ok(1 == 1) - ok(1 == 2) + ok(1 == 1) + ok(1 == 2) end, {'gambiarra_test.lua:64'}, {'gambiarra_test.lua:65'}) -- -- Equality tests -- metatest('eq nil', function() - ok(eq(nil, nil), 'nil == nil') + ok(eq(nil, nil), 'nil == nil') end, {'nil == nil'}, {}) metatest('eq primitives', function() - ok(eq('foo', 'foo'), 'str == str') - ok(eq('foo', 'bar'), 'str != str') - ok(eq(123, 123), 'num == num') - ok(eq(123, 12), 'num != num') + ok(eq('foo', 'foo'), 'str == str') + ok(eq('foo', 'bar'), 'str != str') + ok(eq(123, 123), 'num == num') + ok(eq(123, 12), 'num != num') end, {'str == str', 'num == num'}, {'str != str', 'num != num'}) metatest('eq arrays', function() - ok(eq({}, {}), 'empty') - ok(eq({1, 2}, {1, 2}), 'equal') - ok(eq({1, 2}, {2, 1}), 'swapped') - ok(eq({1, 2, 3}, {1, 2}), 'longer') - ok(eq({1, 2}, {1, 2, 3}), 'shorter') + ok(eq({}, {}), 'empty') + ok(eq({1, 2}, {1, 2}), 'equal') + ok(eq({1, 2}, {2, 1}), 'swapped') + ok(eq({1, 2, 3}, {1, 2}), 'longer') + ok(eq({1, 2}, {1, 2, 3}), 'shorter') end, {'empty', 'equal'}, {'swapped', 'longer', 'shorter'}) metatest('eq objects', function() - ok(eq({}, {}), 'empty') - ok(eq({a=1,b=2}, {a=1,b=2}), 'equal') - ok(eq({b=2,a=1}, {a=1,b=2}), 'swapped') - ok(eq({a=1,b=2}, {a=1,b=3}), 'not equal') + ok(eq({}, {}), 'empty') + ok(eq({a=1,b=2}, {a=1,b=2}), 'equal') + ok(eq({b=2,a=1}, {a=1,b=2}), 'swapped') + ok(eq({a=1,b=2}, {a=1,b=3}), 'not equal') end, {'empty', 'equal', 'swapped'}, {'not equal'}) metatest('eq nested objects', function() - ok(eq({ - ['1'] = { name = 'mhc', age = 28 }, - ['2'] = { name = 'arb', age = 26 } - }, { - ['1'] = { name = 'mhc', age = 28 }, - ['2'] = { name = 'arb', age = 26 } - }), 'equal') - ok(eq({ - ['1'] = { name = 'mhc', age = 28 }, - ['2'] = { name = 'arb', age = 26 } - }, { - ['1'] = { name = 'mhc', age = 28 }, - ['2'] = { name = 'arb', age = 27 } - }), 'not equal') + ok(eq({ + ['1'] = { name = 'mhc', age = 28 }, + ['2'] = { name = 'arb', age = 26 } + }, { + ['1'] = { name = 'mhc', age = 28 }, + ['2'] = { name = 'arb', age = 26 } + }), 'equal') + ok(eq({ + ['1'] = { name = 'mhc', age = 28 }, + ['2'] = { name = 'arb', age = 26 } + }, { + ['1'] = { name = 'mhc', age = 28 }, + ['2'] = { name = 'arb', age = 27 } + }), 'not equal') end, {'equal'}, {'not equal'}) metatest('eq functions', function() - ok(eq(function(x) return x end, function(x) return x end), 'equal') - ok(eq(function(z) return x end, function(z) return y end), 'wrong variable') - ok(eq(function(x) return x end, function(x) return x+2 end), 'wrong code') + ok(eq(function(x) return x end, function(x) return x end), 'equal') + ok(eq(function(z) return x end, function(z) return y end), 'wrong variable') + ok(eq(function(x) return x end, function(x) return x+2 end), 'wrong code') end, {'equal'}, {'wrong variable', 'wrong code'}) -- -- Spies -- metatest('spy called', function() - local f = spy() - ok(not f.called or #f.called == 0, 'not called') - f() - ok(f.called, 'called') - ok(#f.called == 1, 'called once') - f() - ok(#f.called == 2, 'called twice') + local f = spy() + ok(not f.called or #f.called == 0, 'not called') + f() + ok(f.called, 'called') + ok(#f.called == 1, 'called once') + f() + ok(#f.called == 2, 'called twice') end, {'not called', 'called', 'called once', 'called twice'}, {}) metatest('spy with arguments', function() - local x = 0 - function setX(n) x = n end - local f = spy(setX) - f(1) - ok(x == 1, 'x == 1') - ok(eq(f.called, {{1}}), 'called with 1') - f(42) - ok(x == 42, 'x == 42') - ok(eq(f.called, {{1}, {42}}), 'called with 42') + local x = 0 + function setX(n) x = n end + local f = spy(setX) + f(1) + ok(x == 1, 'x == 1') + ok(eq(f.called, {{1}}), 'called with 1') + f(42) + ok(x == 42, 'x == 42') + ok(eq(f.called, {{1}, {42}}), 'called with 42') end, {'x == 1', 'called with 1', 'x == 42', 'called with 42'}, {}) metatest('spy with nils', function() - function nils(a, dummy, b) return a, nil, b, nil end - local f = spy(nils) - r1, r2, r3, r4 = f(1, nil, 2) - ok(eq(f.called, {{1, nil, 2}}), 'nil in args') - ok(r1 == 1 and r2 == nil and r3 == 2 and r4 == nil, 'nil in returns') + function nils(a, dummy, b) return a, nil, b, nil end + local f = spy(nils) + r1, r2, r3, r4 = f(1, nil, 2) + ok(eq(f.called, {{1, nil, 2}}), 'nil in args') + ok(r1 == 1 and r2 == nil and r3 == 2 and r4 == nil, 'nil in returns') end, {'nil in args', 'nil in returns'}, {}) metatest('spy with exception', function() - function throwSomething(s) - if s ~= 'nopanic' then error('panic: '..s) end - end - local f = spy(throwSomething) - f('nopanic') - ok(f.errors == nil, 'no errors yet') - f('foo') - ok(eq(f.called, {{'nopanic'}, {'foo'}}), 'args ok') - ok(f.errors[1] == nil and f.errors[2] ~= nil, 'thrown ok') + function throwSomething(s) + if s ~= 'nopanic' then error('panic: '..s) end + end + local f = spy(throwSomething) + f('nopanic') + ok(f.errors == nil, 'no errors yet') + f('foo') + ok(eq(f.called, {{'nopanic'}, {'foo'}}), 'args ok') + ok(f.errors[1] == nil and f.errors[2] ~= nil, 'thrown ok') end, {'no errors yet', 'args ok', 'thrown ok'}, {}) metatest('another spy with exception', function() - local f = spy(function() local a = unknownVariable + 1 end) - f() - ok(f.errors[1], 'exception thrown') + local f = spy(function() local a = unknownVariable + 1 end) + f() + ok(f.errors[1], 'exception thrown') end, {'exception thrown'}, {}) metatest('spy should return a value', function() - local f = spy(function() return 5 end) - ok(f() == 5, 'spy returns a value') - local g = spy() - ok(g() == nil, 'default spy returns undefined') + local f = spy(function() return 5 end) + ok(f() == 5, 'spy returns a value') + local g = spy() + ok(g() == nil, 'default spy returns undefined') end, {'spy returns a value', 'default spy returns undefined'}, {}) -- @@ -181,35 +189,35 @@ end, {'spy returns a value', 'default spy returns undefined'}, {}) local queue = {} local function async(f) table.insert(queue, f) end local function async_next() - local f = table.remove(queue, 1) - if f then f() end + local f = table.remove(queue, 1) + if f then f() end end metatest('async test', function(next) - async(function() - ok(true, 'bar') - async(function() - ok(true, 'baz') - next() - end) - end) - ok(true, 'foo') + async(function() + ok(true, 'bar') + async(function() + ok(true, 'baz') + next() + end) + end) + ok(true, 'foo') end, {'foo', 'bar', 'baz'}, {}, true) metatest('async test without actually async', function(next) - ok(true, 'bar') - next() + ok(true, 'bar') + next() end, {'bar'}, {}, true) async_next() async_next() metatest('another async test after async queue drained', function(next) - async(function() - ok(true, 'bar') - next() - end) - ok(true, 'foo') + async(function() + ok(true, 'bar') + next() + end) + ok(true, 'foo') end, {'foo', 'bar'}, {}, true) async_next() @@ -218,17 +226,17 @@ async_next() -- Finalize: check test results -- for i = 1,#expected do - if actual[i] == nil then - print("--- FAIL "..expected[i].name..' (pending)') - elseif not comparetables(expected[i].pass, actual[i].pass) then - print("--- FAIL "..expected[i].name..' (passed): ['.. - stringify(expected[i].pass)..'] vs ['.. - stringify(actual[i].pass)..']') - elseif not comparetables(expected[i].fail, actual[i].fail) then - print("--- FAIL "..expected[i].name..' (failed): ['.. - stringify(expected[i].fail)..'] vs ['.. - stringify(actual[i].fail)..']') - else - print("+++ Pass "..expected[i].name) - end + if actual[i] == nil then + print("--- FAIL "..expected[i].name..' (pending)') + elseif not comparetables(expected[i].pass, actual[i].pass) then + print("--- FAIL "..expected[i].name..' (passed): ['.. + stringify(expected[i].pass)..'] vs ['.. + stringify(actual[i].pass)..']') + elseif not comparetables(expected[i].fail, actual[i].fail) then + print("--- FAIL "..expected[i].name..' (failed): ['.. + stringify(expected[i].fail)..'] vs ['.. + stringify(actual[i].fail)..']') + else + print("+++ Pass "..expected[i].name) + end end From 5de4e2d506f9ce6cdd3b3c61ef02e68f35f0a08d Mon Sep 17 00:00:00 2001 From: Gregor Date: Sun, 19 Jan 2020 10:51:50 +0100 Subject: [PATCH 05/36] Some refactoring to make it easier to add new functionality --- lua_tests/gambiarra/gambiarra.lua | 22 +++++++++++----------- lua_tests/gambiarra/gambiarra_test.lua | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index e5827b89e1..da117d4675 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -62,6 +62,13 @@ local function runpending() if pendingtests[1] ~= nil then pendingtests[1](runpending) end end +local function copyenv(dest, src) + dest.eq = src.eq + dest.spy = src.spy + dest.ok = src.ok + dest.nok = src.nok +end + return function(name, f, async) if type(name) == 'function' then gambiarrahandler = name @@ -71,16 +78,11 @@ return function(name, f, async) local testfn = function(next) - local prev = { - ok = env.ok, - spy = env.spy, - eq = env.eq - } + local prev = {} + copyenv(prev, env) local restore = function() - env.ok = prev.ok - env.spy = prev.spy - env.eq = prev.eq + copyenv(env, prev) gambiarrahandler('end', name) table.remove(pendingtests, 1) if next then next() end @@ -113,9 +115,7 @@ return function(name, f, async) if not async then handler('end', name); - env.ok = prev.ok; - env.spy = prev.spy; - env.eq = prev.eq; + copyenv(env, prev) end end diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index f9d91fafdb..c64b57b8f6 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -57,7 +57,7 @@ metatest('simple assert passes', function() end, {'2==2'}, {}) metatest('simple negative assert fails', function() - ok(2 == 2, '2==2') + nok(2 == 2, '2==2') end, {}, {'2==2'}) metatest('simple assert fails', function() @@ -71,7 +71,7 @@ end, {'1~=2'}, {}) metatest('ok without a message', function() ok(1 == 1) ok(1 == 2) -end, {'gambiarra_test.lua:64'}, {'gambiarra_test.lua:65'}) +end, {'gambiarra_test.lua:72'}, {'gambiarra_test.lua:73'}) -- -- Equality tests From 7a52b5c602994041b8b62ada46133f3d155be4fe Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 20 Jan 2020 22:55:29 +0100 Subject: [PATCH 06/36] Add methode `fail` to check failing code and pass error messages to output - fail can be called with a function that should fail and a string which should be contained in the errormessage. - Pass failed check reasons to output. --- lua_tests/gambiarra/gambiarra.lua | 73 +++++++++++++++++++------- lua_tests/gambiarra/gambiarra_test.lua | 24 +++++++-- 2 files changed, 76 insertions(+), 21 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index da117d4675..ca529c6a96 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -1,16 +1,23 @@ -local function TERMINAL_HANDLER(e, test, msg) +local function TERMINAL_HANDLER(e, test, msg, errormsg) + if errormsg then + errormsg = ": "..errormsg + else + errormsg = "" + end if e == 'pass' then - print(e.." "..test..': '..msg) + print(" "..e.." "..test..': '..msg) elseif e == 'fail' then - print(e.." "..test..': '..msg) + print(" ==> "..e.." "..test..': '..msg..errormsg) elseif e == 'except' then - print(e.." "..test..': '..msg) + print(" ==> "..e.." "..test..': '..msg..errormsg) + else + print(e.." "..test) end end local function deepeq(a, b) -- Different types: false - if type(a) ~= type(b) then return false end + if type(a) ~= type(b) then return {false, "type 1 is "..type(a)..", type 2 is "..type(b)} end -- Functions if type(a) == 'function' then return string.dump(a) == string.dump(b) @@ -54,6 +61,43 @@ local function spy(f) return s end +local function assertok(handler, name, cond, msg) + if not msg then + -- debug.getinfo() does not exist in NodeMCU + -- msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline + msg = debug.traceback() + msg = msg:match("\n[^\n]*\n[^\n]*\n[^\n]*\n\t*([^\n]*): in") + end + local errormsg + + if type(cond) == "table" then + errormsg = cond[2] + cond = cond[1] + end + if cond then + handler('pass', name, msg, errormsg) + else + handler('fail', name, msg, errormsg) + end +end + +local function fail(handler, name, func, expected, msg) + local status, err = pcall(func) + if status then + local messagePart = "" + if expected then + messagePart = " containing \"" .. expected .. "\"" + end + handler('fail', name, msg, "Expected to fail with Error" .. messagePart) + return + end + if (expected and not string.find(err, expected)) then + handler('fail', name, msg, "expected errormessage \"" .. err .. "\" to contain \"" .. expected .. "\"") + return + end + handler('pass', name, msg) +end + local pendingtests = {} local env = _G local gambiarrahandler = TERMINAL_HANDLER @@ -67,6 +111,7 @@ local function copyenv(dest, src) dest.spy = src.spy dest.ok = src.ok dest.nok = src.nok + dest.fail = src.fail end return function(name, f, async) @@ -89,23 +134,15 @@ return function(name, f, async) end local handler = gambiarrahandler + local function wrap(f, ...) + f(handler, name, ...) + end env.eq = deepeq env.spy = spy - env.ok = function (cond, msg) - if not msg then - -- debug.getinfo() does not exist in NodeMCU - -- msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline - msg = debug.traceback() - msg = msg:match("\n[^\n]*\n\t*([^\n]*): in") - end - if cond then - handler('pass', name, msg) - else - handler('fail', name, msg) - end - end + env.ok = function (cond, msg) wrap(assertok, cond, msg) end env.nok = function(cond, msg) env.ok(not cond, msg) end + env.fail = function (func, expected, msg) wrap(fail, func, expected, msg) end handler('begin', name); local ok, err = pcall(f, restore) diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index c64b57b8f6..1062dbdcdb 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -4,7 +4,7 @@ local actual = {} local expected = {} -- Set meta test handler -test(function(e, test, msg) +test(function(e, test, msg, errormsg) if e == 'begin' then currentTest = { name = test, @@ -15,10 +15,12 @@ test(function(e, test, msg) table.insert(actual, currentTest) elseif e == 'pass' then table.insert(currentTest.pass, msg) + if errormsg then table.insert(currentTest.pass, errormsg) end elseif e == 'fail' then table.insert(currentTest.fail, msg) + if errormsg then table.insert(currentTest.fail, errormsg) end elseif e == 'except' then - print('*** PANIC ***: ', test, msg) + print('*** PANIC ***: ', test, msg, errormsg) end end) @@ -71,7 +73,7 @@ end, {'1~=2'}, {}) metatest('ok without a message', function() ok(1 == 1) ok(1 == 2) -end, {'gambiarra_test.lua:72'}, {'gambiarra_test.lua:73'}) +end, {'gambiarra_test.lua:74'}, {'gambiarra_test.lua:75'}) -- -- Equality tests @@ -125,6 +127,11 @@ metatest('eq functions', function() ok(eq(function(x) return x end, function(x) return x+2 end), 'wrong code') end, {'equal'}, {'wrong variable', 'wrong code'}) +metatest('eq different types', function() + ok(eq({a=1,b=2}, "text"), 'object/string') + ok(eq(function(x) return x end, 12), 'function/int') +end, {}, {"object/string", "type 1 is table, type 2 is string", "function/int", "type 1 is function, type 2 is number"}) + -- -- Spies -- @@ -183,6 +190,17 @@ metatest('spy should return a value', function() ok(g() == nil, 'default spy returns undefined') end, {'spy returns a value', 'default spy returns undefined'}, {}) +-- +-- fail tests +-- +metatest('fail should work', function() + fail(function() error("my error") end, "my error", "Failed with correct error") + fail(function() error("my error") end, "not my error", "Failed with incorrect error") + fail(function() end, "my error", "Failed without error") +end, {'Failed with correct error'}, {'Failed with incorrect error', + 'expected errormessage "gambiarra_test.lua:199: my error" to contain "not my error"', + "Failed without error", 'Expected to fail with Error containing "my error"'}) + -- -- Async tests -- From 80ef63c61527f51d1519ca2c45fc4c7a0f8b4999 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 20 Jan 2020 22:55:48 +0100 Subject: [PATCH 07/36] Create gambiarra_file.lua --- lua_tests/gambiarra/gambiarra_file.lua | 210 +++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 lua_tests/gambiarra/gambiarra_file.lua diff --git a/lua_tests/gambiarra/gambiarra_file.lua b/lua_tests/gambiarra/gambiarra_file.lua new file mode 100644 index 0000000000..308d219064 --- /dev/null +++ b/lua_tests/gambiarra/gambiarra_file.lua @@ -0,0 +1,210 @@ +local test = require('gambiarra') + +-- set the output handler +local function TERMINAL_HANDLER(e, test, msg, errormsg) + if errormsg then + errormsg = ": "..errormsg + else + errormsg = "" + end + if 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) + else + print(e.." "..test) + end +end +-- set the output handler +-- test(TERMINAL_HANDLER) + +local function cleanup() + file.remove("testfile") + file.remove("testfile2") + local testfiles = {"testfile1&", "testFILE2"} + for _, n in ipairs(testfiles) do + file.remove(n,n) + end +end + +local function buildfn(fn, ...) + params = {...} + local fnp = function() fn(unpack(params)) end + return fnp +end + +test('exist', function() + cleanup() + ok(not file.exists("non existing file"), "non existing file") + + file.putcontents("testfile", "testcontents") + ok(file.exists("testfile"), "existing file") +end) + + +test('fscfg', function() + cleanup() + local start, size = file.fscfg() + ok(start, "start") + ok(size, "size") +end) + +test('fsinfo', function() + cleanup() + local remaining, used, total = file.fsinfo() + ok(remaining, "remaining") + ok(used, "used") + ok(total, "total") + ok(eq(remaining+used, total), "size maths") +end) + +test('getcontents', function() + cleanup() + local testcontent = "some content \0 and more" + file.putcontents("testfile", testcontent) + local content = file.getcontents("testfile") + ok(testcontent, content) +end) + +test('getcontents non existent file', function() + cleanup() + nok(file.getcontents("non existing file"), "non existent file") +end) + +test('getcontents more than 1K', function() + cleanup() + local f = file.open("testfile", "w") + local i + for i = 1,100 do + f:write("some text to test") + end + f:close() + content = file.getcontents("testfile") + eq(#content, 1700, "partial read") +end) + +test('list', function() + cleanup() + local files + + local function count(files) + local filecount = 0 + for k,v in pairs(files) do filecount = filecount+1 end + return filecount + end + + local function testfile(name) + ok(eq(files[name],#name), "length matches name length") + end + + local testfiles = {"testfile1&", "testFILE2"} + for _, n in ipairs(testfiles) do + file.putcontents(n,n) + end + + files = file.list("testfile%.*") + ok(eq(count(files), 1), "found file") + testfile("testfile1&") + + files = file.list("^%l*%u+%d%.-") + ok(eq(count(files), 1), "found file regexp") + testfile("testFILE2") + + files = file.list() + ok(count(files) >= 2, "several files found") +end) + +test('open non existing', function() + cleanup() + local function testopen(test, filename, mode) + test(file.open(filename, mode), mode) + file.close() + file.remove(filename) + end + + testopen(nok, "testfile", "r") + testopen(ok, "testfile", "w") + testopen(ok, "testfile", "a") + testopen(nok, "testfile", "r+") + testopen(ok, "testfile", "w+") + testopen(ok, "testfile", "a+") + + fail(buildfn(file.open, "testfile"), "errormsg", "x") -- shouldn't this fail? +end) + +test('open existing', function() + cleanup() + local function testopen(mode, position) + file.putcontents("testfile", "testcontent") + ok(file.open("testfile", mode), mode) + file.write("") + ok(eq(file.seek(), position), "seek check") + file.close() + end + + testopen("r", 0) + testopen("w", 0) + testopen("a", 11) + testopen("r+", 0) + testopen("w+", 0) + testopen("a+", 11) + + fail(buildfn(file.open, "testfile"), "errormsg", "x") -- shouldn't this fail? +end) + +test('remove', function() + cleanup() + file.putcontents("testfile", "testfile") + + ok(file.remove("testfile") == nil, "existing file") + ok(file.remove("testfile") == nil, "non existing file") +end) + +test('rename', function() + cleanup() + file.putcontents("testfile", "testfile") + + ok(file.rename("testfile", "testfile2"), "rename existing") + nok(file.exists("testfile"), "old file removed") + ok(file.exists("testfile2"), "new file exists") + + nok(file.rename("testfile", "testfile3"), "rename non existing") + + file.putcontents("testfile", "testfile") + + nok(file.rename("testfile", "testfile2"), "rename to existing") + ok(file.exists("testfile"), "from file exists") + ok(file.exists("testfile2"), "to file exists") +end) + +test('stat existing file', function() + cleanup() + file.putcontents("testfile", "testfile") + + local stat = file.stat("testfile") + + nok(stat == nil, "stat existing") + ok(eq(stat.size, 8), "size") + ok(eq(stat.name, "testfile"), "name") + nok(stat.time == nil, "no time") + ok(eq(stat.time.year, 1970), "year") + ok(eq(stat.time.mon, 01), "mon") + ok(eq(stat.time.day, 01), "day") + ok(eq(stat.time.hour, 0), "hour") + ok(eq(stat.time.min, 0), "min") + ok(eq(stat.time.sec, 0), "sec") + nok(stat.is_dir, "is_dir") + nok(stat.is_rdonly, "is_rdonly") + nok(stat.is_hidden, "is_hidden") + nok(stat.is_sys, "is_sys") + nok(stat.is_arch, "is_arch") +end) + +test('stat non existing file', function() + cleanup() + local stat = file.stat("not existing file") + + ok(stat == nil, "stat empty") +end) From 12c8062df883b2c24bfc928b4044d89b661427a0 Mon Sep 17 00:00:00 2001 From: Gregor Date: Sun, 5 Jul 2020 16:32:53 +0200 Subject: [PATCH 08/36] Add reporting of tests that failed with Lua error --- lua_tests/gambiarra/gambiarra.lua | 16 +++++--- lua_tests/gambiarra/gambiarra_test.lua | 52 ++++++++++++++++++++------ 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index ca529c6a96..ac39a82525 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -67,6 +67,7 @@ local function assertok(handler, name, cond, msg) -- msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline msg = debug.traceback() msg = msg:match("\n[^\n]*\n[^\n]*\n[^\n]*\n\t*([^\n]*): in") + msg = msg:match(".-([^\\/]*)$") -- cut off path of filename end local errormsg @@ -78,6 +79,7 @@ local function assertok(handler, name, cond, msg) handler('pass', name, msg, errormsg) else handler('fail', name, msg, errormsg) + --error('okAbort') end end @@ -91,6 +93,7 @@ local function fail(handler, name, func, expected, msg) handler('fail', name, msg, "Expected to fail with Error" .. messagePart) return end + err:match(".-([^\\/]*)$") -- cut off path of filename if (expected and not string.find(err, expected)) then handler('fail', name, msg, "expected errormessage \"" .. err .. "\" to contain \"" .. expected .. "\"") return @@ -100,7 +103,7 @@ end local pendingtests = {} local env = _G -local gambiarrahandler = TERMINAL_HANDLER +local outputhandler = TERMINAL_HANDLER local function runpending() if pendingtests[1] ~= nil then pendingtests[1](runpending) end @@ -116,7 +119,7 @@ end return function(name, f, async) if type(name) == 'function' then - gambiarrahandler = name + outputhandler = name env = f or _G return end @@ -128,12 +131,13 @@ return function(name, f, async) local restore = function() copyenv(env, prev) - gambiarrahandler('end', name) + outputhandler('end', name) table.remove(pendingtests, 1) if next then next() end end - local handler = gambiarrahandler + local handler = outputhandler + local function wrap(f, ...) f(handler, name, ...) end @@ -147,7 +151,9 @@ return function(name, f, async) handler('begin', name); local ok, err = pcall(f, restore) if not ok then - handler('except', name, err) + if err ~= 'okAbort' then + handler('except', name, err) + end end if not async then diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index 1062dbdcdb..16c1e681dd 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -9,7 +9,8 @@ test(function(e, test, msg, errormsg) currentTest = { name = test, pass = {}, - fail = {} + fail = {}, + except = {} } elseif e == 'end' then table.insert(actual, currentTest) @@ -20,7 +21,8 @@ test(function(e, test, msg, errormsg) table.insert(currentTest.fail, msg) if errormsg then table.insert(currentTest.fail, errormsg) end elseif e == 'except' then - print('*** PANIC ***: ', test, msg, errormsg) + table.insert(currentTest.except, msg) + if errormsg then table.insert(currentTest.except, errormsg) end end end) @@ -42,12 +44,13 @@ local function comparetables(t1, t2) return true end -local function metatest(name, f, expectedPassed, expectedFailed, async) +local function metatest(name, f, expectedPassed, expectedFailed, expectedExcept, async) test(name, f, async) table.insert(expected, { name = name, pass = expectedPassed, - fail = expectedFailed + fail = expectedFailed, + except = expectedExcept or {} }) end @@ -73,7 +76,7 @@ end, {'1~=2'}, {}) metatest('ok without a message', function() ok(1 == 1) ok(1 == 2) -end, {'gambiarra_test.lua:74'}, {'gambiarra_test.lua:75'}) +end, {'gambiarra_test.lua:77'}, {'gambiarra_test.lua:78'}) -- -- Equality tests @@ -195,12 +198,20 @@ end, {'spy returns a value', 'default spy returns undefined'}, {}) -- metatest('fail should work', function() fail(function() error("my error") end, "my error", "Failed with correct error") - fail(function() error("my error") end, "not my error", "Failed with incorrect error") + fail(function() error("my error") end, "different error", "Failed with incorrect error") fail(function() end, "my error", "Failed without error") end, {'Failed with correct error'}, {'Failed with incorrect error', - 'expected errormessage "gambiarra_test.lua:199: my error" to contain "not my error"', + 'expected errormessage "gambiarra_test.lua:201: my error" to contain "different error"', "Failed without error", 'Expected to fail with Error containing "my error"'}) +-- +-- except tests +-- +metatest('error should panic', function() + error("lua error") + ok(true, 'unreachable code') +end, {}, {}, {'gambiarra_test.lua:211: lua error'}) + -- -- Async tests -- @@ -220,12 +231,12 @@ metatest('async test', function(next) end) end) ok(true, 'foo') -end, {'foo', 'bar', 'baz'}, {}, true) +end, {'foo', 'bar', 'baz'}, {}, {}, true) metatest('async test without actually async', function(next) ok(true, 'bar') next() -end, {'bar'}, {}, true) +end, {'bar'}, {}, {}, true) async_next() async_next() @@ -236,7 +247,20 @@ metatest('another async test after async queue drained', function(next) next() end) ok(true, 'foo') -end, {'foo', 'bar'}, {}, true) +end, {'foo', 'bar'}, {}, {}, true) + +async_next() + +-- +-- except tests async +-- +metatest('error should panic async', function(next) + async(function() + -- error("async Lua error") + next() + end) + ok(true, 'foo') +end, {'foo'}, {}, {'gambiarra_test.lua:259: async Lua error'}, true) async_next() @@ -247,13 +271,17 @@ for i = 1,#expected do if actual[i] == nil then print("--- FAIL "..expected[i].name..' (pending)') elseif not comparetables(expected[i].pass, actual[i].pass) then - print("--- FAIL "..expected[i].name..' (passed): ['.. + print("--- FAIL "..expected[i].name..' (passed): expected ['.. stringify(expected[i].pass)..'] vs ['.. stringify(actual[i].pass)..']') elseif not comparetables(expected[i].fail, actual[i].fail) then - print("--- FAIL "..expected[i].name..' (failed): ['.. + print("--- FAIL "..expected[i].name..' (failed): expected ['.. stringify(expected[i].fail)..'] vs ['.. stringify(actual[i].fail)..']') + elseif not comparetables(expected[i].except, actual[i].except) then + print("--- FAIL "..expected[i].name..' (failed): expected ['.. + stringify(expected[i].except)..'] vs ['.. + stringify(actual[i].except)..']') else print("+++ Pass "..expected[i].name) end From 30abc6097c8fdb5259c1efc03ae4651b287aae95 Mon Sep 17 00:00:00 2001 From: Gregor Date: Sun, 5 Jul 2020 20:15:25 +0200 Subject: [PATCH 09/36] ok, nok and fail will terminate the running test --- lua_tests/gambiarra/gambiarra.lua | 10 ++-- lua_tests/gambiarra/gambiarra_test.lua | 67 ++++++++++++++++++-------- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index ac39a82525..f84f76377b 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -66,7 +66,7 @@ local function assertok(handler, name, cond, msg) -- debug.getinfo() does not exist in NodeMCU -- msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline msg = debug.traceback() - msg = msg:match("\n[^\n]*\n[^\n]*\n[^\n]*\n\t*([^\n]*): in") + msg = msg:match(".*\n\t*([^\n]*): in.-\n\t*.*in function 'pcall'") msg = msg:match(".-([^\\/]*)$") -- cut off path of filename end local errormsg @@ -79,7 +79,7 @@ local function assertok(handler, name, cond, msg) handler('pass', name, msg, errormsg) else handler('fail', name, msg, errormsg) - --error('okAbort') + error('_*_TestAbort_*_') end end @@ -91,12 +91,12 @@ local function fail(handler, name, func, expected, msg) messagePart = " containing \"" .. expected .. "\"" end handler('fail', name, msg, "Expected to fail with Error" .. messagePart) - return + error('_*_TestAbort_*_') end err:match(".-([^\\/]*)$") -- cut off path of filename if (expected and not string.find(err, expected)) then handler('fail', name, msg, "expected errormessage \"" .. err .. "\" to contain \"" .. expected .. "\"") - return + error('_*_TestAbort_*_') end handler('pass', name, msg) end @@ -151,7 +151,7 @@ return function(name, f, async) handler('begin', name); local ok, err = pcall(f, restore) if not ok then - if err ~= 'okAbort' then + if not err:match('_*_TestAbort_*_') then handler('except', name, err) end end diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index 16c1e681dd..986ae90f77 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -63,10 +63,12 @@ end, {'2==2'}, {}) metatest('simple negative assert fails', function() nok(2 == 2, '2==2') + nok(false, 'unreachable code') end, {}, {'2==2'}) metatest('simple assert fails', function() ok(1 == 2, '1~=2') + ok(true, 'unreachable code') end, {}, {'1~=2'}) metatest('simple negative assert passe', function() @@ -76,7 +78,12 @@ end, {'1~=2'}, {}) metatest('ok without a message', function() ok(1 == 1) ok(1 == 2) -end, {'gambiarra_test.lua:77'}, {'gambiarra_test.lua:78'}) +end, {'gambiarra_test.lua:79'}, {'gambiarra_test.lua:80'}) + +metatest('nok without a message', function() + nok(1 == 2) + nok(1 == 1) +end, {'gambiarra_test.lua:84'}, {'gambiarra_test.lua:85'}) -- -- Equality tests @@ -87,25 +94,25 @@ end, {'nil == nil'}, {}) metatest('eq primitives', function() ok(eq('foo', 'foo'), 'str == str') - ok(eq('foo', 'bar'), 'str != str') + nok(eq('foo', 'bar'), 'str != str') ok(eq(123, 123), 'num == num') - ok(eq(123, 12), 'num != num') -end, {'str == str', 'num == num'}, {'str != str', 'num != num'}) + nok(eq(123, 12), 'num != num') +end, {'str == str', 'str != str', 'num == num', 'num != num'}, {}) metatest('eq arrays', function() ok(eq({}, {}), 'empty') ok(eq({1, 2}, {1, 2}), 'equal') - ok(eq({1, 2}, {2, 1}), 'swapped') - ok(eq({1, 2, 3}, {1, 2}), 'longer') - ok(eq({1, 2}, {1, 2, 3}), 'shorter') -end, {'empty', 'equal'}, {'swapped', 'longer', 'shorter'}) + nok(eq({1, 2}, {2, 1}), 'swapped') + nok(eq({1, 2, 3}, {1, 2}), 'longer') + nok(eq({1, 2}, {1, 2, 3}), 'shorter') +end, {'empty', 'equal', 'swapped', 'longer', 'shorter'}, {}) metatest('eq objects', function() ok(eq({}, {}), 'empty') ok(eq({a=1,b=2}, {a=1,b=2}), 'equal') ok(eq({b=2,a=1}, {a=1,b=2}), 'swapped') - ok(eq({a=1,b=2}, {a=1,b=3}), 'not equal') -end, {'empty', 'equal', 'swapped'}, {'not equal'}) + nok(eq({a=1,b=2}, {a=1,b=3}), 'not equal') +end, {'empty', 'equal', 'swapped', 'not equal'}, {}) metatest('eq nested objects', function() ok(eq({ @@ -126,14 +133,16 @@ end, {'equal'}, {'not equal'}) metatest('eq functions', function() ok(eq(function(x) return x end, function(x) return x end), 'equal') - ok(eq(function(z) return x end, function(z) return y end), 'wrong variable') - ok(eq(function(x) return x end, function(x) return x+2 end), 'wrong code') -end, {'equal'}, {'wrong variable', 'wrong code'}) + nok(eq(function(z) return x end, function(z) return y end), 'wrong variable') + nok(eq(function(x) return x end, function(x) return x+2 end), 'wrong code') +end, {'equal', 'wrong variable', 'wrong code'}, {}) metatest('eq different types', function() - ok(eq({a=1,b=2}, "text"), 'object/string') - ok(eq(function(x) return x end, 12), 'function/int') -end, {}, {"object/string", "type 1 is table, type 2 is string", "function/int", "type 1 is function, type 2 is number"}) + local eqos = eq({a=1,b=2}, "text") + ok(eq(eqos[2], "type 1 is table, type 2 is string"), 'object/string') + local eqfn = eq(function(x) return x end, 12) + ok(eq(eqfn[2], "type 1 is function, type 2 is number"), 'function/int') +end, {"object/string", "function/int"}, {}) -- -- Spies @@ -196,13 +205,21 @@ end, {'spy returns a value', 'default spy returns undefined'}, {}) -- -- fail tests -- -metatest('fail should work', function() +metatest('fail with correct errormessage', function() fail(function() error("my error") end, "my error", "Failed with correct error") + ok(true, 'reachable code') +end, {'Failed with correct error', 'reachable code'}, {}) + +metatest('fail with incorrect errormessage', function() fail(function() error("my error") end, "different error", "Failed with incorrect error") + ok(true, 'unreachable code') +end, {}, {'Failed with incorrect error', + 'expected errormessage "gambiarra_test.lua:214: my error" to contain "different error"'}) + +metatest('fail with not failing code', function() fail(function() end, "my error", "Failed without error") -end, {'Failed with correct error'}, {'Failed with incorrect error', - 'expected errormessage "gambiarra_test.lua:201: my error" to contain "different error"', - "Failed without error", 'Expected to fail with Error containing "my error"'}) + ok(true, 'unreachable code') +end, {}, {"Failed without error", 'Expected to fail with Error containing "my error"'}) -- -- except tests @@ -210,7 +227,7 @@ end, {'Failed with correct error'}, {'Failed with incorrect error', metatest('error should panic', function() error("lua error") ok(true, 'unreachable code') -end, {}, {}, {'gambiarra_test.lua:211: lua error'}) +end, {}, {}, {'gambiarra_test.lua:228: lua error'}) -- -- Async tests @@ -267,22 +284,30 @@ async_next() -- -- Finalize: check test results -- +local failed, passed = 0,0 for i = 1,#expected do if actual[i] == nil then print("--- FAIL "..expected[i].name..' (pending)') + failed = failed + 1 elseif not comparetables(expected[i].pass, actual[i].pass) then print("--- FAIL "..expected[i].name..' (passed): expected ['.. stringify(expected[i].pass)..'] vs ['.. stringify(actual[i].pass)..']') + failed = failed + 1 elseif not comparetables(expected[i].fail, actual[i].fail) then print("--- FAIL "..expected[i].name..' (failed): expected ['.. stringify(expected[i].fail)..'] vs ['.. stringify(actual[i].fail)..']') + failed = failed + 1 elseif not comparetables(expected[i].except, actual[i].except) then print("--- FAIL "..expected[i].name..' (failed): expected ['.. stringify(expected[i].except)..'] vs ['.. stringify(actual[i].except)..']') + failed = failed + 1 else print("+++ Pass "..expected[i].name) + passed = passed + 1 end end + +print("failed: "..failed, "passed: "..passed) \ No newline at end of file From a7558c22b0c1291637ae90c567736bec1ff47bdd Mon Sep 17 00:00:00 2001 From: Gregor Date: Sun, 5 Jul 2020 21:17:17 +0200 Subject: [PATCH 10/36] Add capability to run sync and async tests in mixed order and have a task.post inbetween them --- lua_tests/gambiarra/gambiarra.lua | 21 +++++---- lua_tests/gambiarra/gambiarra_test.lua | 62 +++++++++++++++++++------- 2 files changed, 57 insertions(+), 26 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index f84f76377b..3c3cb25321 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -106,7 +106,11 @@ local env = _G local outputhandler = TERMINAL_HANDLER local function runpending() - if pendingtests[1] ~= nil then pendingtests[1](runpending) end + if pendingtests[1] ~= nil then + node.task.post(node.task.LOW_PRIORITY, function() + pendingtests[1](runpending) + end) + end end local function copyenv(dest, src) @@ -149,7 +153,7 @@ return function(name, f, async) env.fail = function (func, expected, msg) wrap(fail, func, expected, msg) end handler('begin', name); - local ok, err = pcall(f, restore) + local ok, err = pcall(f, async and restore) if not ok then if not err:match('_*_TestAbort_*_') then handler('except', name, err) @@ -157,17 +161,12 @@ return function(name, f, async) end if not async then - handler('end', name); - copyenv(env, prev) + restore() end end - if not async then - testfn() - else - table.insert(pendingtests, testfn) - if #pendingtests == 1 then - runpending() - end + table.insert(pendingtests, testfn) + if #pendingtests == 1 then + runpending() end end diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index 986ae90f77..cb803d2f23 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -3,6 +3,8 @@ local test = require('gambiarra') local actual = {} local expected = {} +node = {task = {}} + -- Set meta test handler test(function(e, test, msg, errormsg) if e == 'begin' then @@ -44,6 +46,22 @@ local function comparetables(t1, t2) return true end +local queue = {} +local function async(f) table.insert(queue, f) end +local post_queue = {} +node.task.post = function (p, f) table.insert(post_queue, f) end +local function async_next() + local f = table.remove(queue, 1) + if f then + f() + else + f = table.remove(post_queue, 1) + if f then + f() + end + end +end + local function metatest(name, f, expectedPassed, expectedFailed, expectedExcept, async) test(name, f, async) table.insert(expected, { @@ -78,12 +96,12 @@ end, {'1~=2'}, {}) metatest('ok without a message', function() ok(1 == 1) ok(1 == 2) -end, {'gambiarra_test.lua:79'}, {'gambiarra_test.lua:80'}) +end, {'gambiarra_test.lua:97'}, {'gambiarra_test.lua:98'}) metatest('nok without a message', function() nok(1 == 2) nok(1 == 1) -end, {'gambiarra_test.lua:84'}, {'gambiarra_test.lua:85'}) +end, {'gambiarra_test.lua:102'}, {'gambiarra_test.lua:103'}) -- -- Equality tests @@ -214,7 +232,7 @@ metatest('fail with incorrect errormessage', function() fail(function() error("my error") end, "different error", "Failed with incorrect error") ok(true, 'unreachable code') end, {}, {'Failed with incorrect error', - 'expected errormessage "gambiarra_test.lua:214: my error" to contain "different error"'}) + 'expected errormessage "gambiarra_test.lua:232: my error" to contain "different error"'}) metatest('fail with not failing code', function() fail(function() end, "my error", "Failed without error") @@ -227,18 +245,15 @@ end, {}, {"Failed without error", 'Expected to fail with Error containing "my er metatest('error should panic', function() error("lua error") ok(true, 'unreachable code') -end, {}, {}, {'gambiarra_test.lua:228: lua error'}) +end, {}, {}, {'gambiarra_test.lua:246: lua error'}) + +while #post_queue > 0 do + async_next() +end -- -- Async tests -- -local queue = {} -local function async(f) table.insert(queue, f) end -local function async_next() - local f = table.remove(queue, 1) - if f then f() end -end - metatest('async test', function(next) async(function() ok(true, 'bar') @@ -266,8 +281,6 @@ metatest('another async test after async queue drained', function(next) ok(true, 'foo') end, {'foo', 'bar'}, {}, {}, true) -async_next() - -- -- except tests async -- @@ -277,9 +290,28 @@ metatest('error should panic async', function(next) next() end) ok(true, 'foo') -end, {'foo'}, {}, {'gambiarra_test.lua:259: async Lua error'}, true) +end, {'foo'}, {}, {'gambiarra_test.lua:289: async Lua error'}, true) -async_next() +-- +-- sync after async test +-- +local marker = 0 +metatest('set marker async', function(next) + async(function() + marker = "marked" + ok(true, 'async bar') + next() + end) + ok(true, 'foo') +end, {'foo', 'async bar'}, {}, {}, true) + +metatest('check marker async', function() + ok(eq(marker, "marked"), "marker check") +end, {"marker check"}, {}) + +while #post_queue + #queue > 0 do + async_next() +end -- -- Finalize: check test results From e9994818a2a80175e452cc2813a2f48cc7183ae3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 7 Jul 2020 07:11:59 +0200 Subject: [PATCH 11/36] fix gambiarra self test to also run on device (not only host) Use less ram in checking tests directly after they ran. Use nateie task.post to tame watchdog. --- lua_tests/gambiarra/gambiarra.lua | 19 +-- lua_tests/gambiarra/gambiarra_test.lua | 159 ++++++++++++++----------- 2 files changed, 99 insertions(+), 79 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index 3c3cb25321..537d884088 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -86,17 +86,17 @@ end local function fail(handler, name, func, expected, msg) local status, err = pcall(func) if status then - local messagePart = "" - if expected then - messagePart = " containing \"" .. expected .. "\"" - end - handler('fail', name, msg, "Expected to fail with Error" .. messagePart) - error('_*_TestAbort_*_') + local messagePart = "" + if expected then + messagePart = " containing \"" .. expected .. "\"" + end + handler('fail', name, msg, "Expected to fail with Error" .. messagePart) + error('_*_TestAbort_*_') end - err:match(".-([^\\/]*)$") -- cut off path of filename if (expected and not string.find(err, expected)) then - handler('fail', name, msg, "expected errormessage \"" .. err .. "\" to contain \"" .. expected .. "\"") - error('_*_TestAbort_*_') + 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 @@ -155,6 +155,7 @@ return function(name, f, async) handler('begin', name); 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 diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index cb803d2f23..de4d652f84 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -1,9 +1,45 @@ local test = require('gambiarra') -local actual = {} local expected = {} +local failed, passed = 0,0 + +local orig_node = node + +-- implement pseudo task handling for on host testing +local post_queue = {} +local drain_post_queue = function() end +if not node then + node = {task = {}} + node.task.post = function (p, f) + table.insert(post_queue, f) + end + drain_post_queue = function() + while #post_queue > 0 do + local f = table.remove(post_queue, 1) + if f then + f() + end + end + end +end + +-- Helper function to print arrays +local function stringify(t) + local s = '' + for i=1,#t do + s = s .. '"' .. t[i] .. '"' .. ' ' + end + return s:gsub('%s*$', '') +end -node = {task = {}} +-- Helper function to compare two tables +local function comparetables(t1, t2) + if #t1 ~= #t2 then return false end + for i=1,#t1 do + if t1[i] ~= t2[i] then return false end + end + return true +end -- Set meta test handler test(function(e, test, msg, errormsg) @@ -15,7 +51,26 @@ test(function(e, test, msg, errormsg) except = {} } elseif e == 'end' then - table.insert(actual, currentTest) + if not comparetables(expected[1].pass, currentTest.pass) then + print("--- FAIL "..expected[1].name..' (passed): expected ['.. + stringify(expected[1].pass)..'] vs ['.. + stringify(currentTest.pass)..']') + failed = failed + 1 + elseif not comparetables(expected[1].fail, currentTest.fail) then + print("--- FAIL "..expected[1].name..' (failed): expected ['.. + stringify(expected[1].fail)..'] vs ['.. + stringify(currentTest.fail)..']') + failed = failed + 1 + elseif not comparetables(expected[1].except, currentTest.except) then + print("--- FAIL "..expected[1].name..' (failed): expected ['.. + stringify(expected[1].except)..'] vs ['.. + stringify(currentTest.except)..']') + failed = failed + 1 + else + print("+++ Pass "..expected[1].name) + passed = passed + 1 + end + table.remove(expected, 1) elseif e == 'pass' then table.insert(currentTest.pass, msg) if errormsg then table.insert(currentTest.pass, errormsg) end @@ -28,42 +83,30 @@ test(function(e, test, msg, errormsg) end end) --- Helper function to print arrays -local function stringify(t) - local s = '' - for i=1,#t do - s = s .. '"' .. t[i] .. '"' .. ' ' +local async_queue = {} +local function async(f) table.insert(async_queue, f) end +local function async_next() + local f = table.remove(async_queue, 1) + if f then + f() end - return s:gsub('%s*$', '') end --- Helper function to compare two tables -local function comparetables(t1, t2) - if #t1 ~= #t2 then return false end - for i=1,#t1 do - if t1[i] ~= t2[i] then return false end +local function drain_async_queue() + while #async_queue > 0 do + async_next() end - return true end -local queue = {} -local function async(f) table.insert(queue, f) end -local post_queue = {} -node.task.post = function (p, f) table.insert(post_queue, f) end -local function async_next() - local f = table.remove(queue, 1) - if f then - f() - else - f = table.remove(post_queue, 1) - if f then - f() +local function metatest(name, f, expectedPassed, expectedFailed, expectedExcept, async) + local ff = f + if async then + ff = function(...) + f(...) + drain_async_queue() end end -end - -local function metatest(name, f, expectedPassed, expectedFailed, expectedExcept, async) - test(name, f, async) + test(name, ff, async) table.insert(expected, { name = name, pass = expectedPassed, @@ -96,12 +139,12 @@ end, {'1~=2'}, {}) metatest('ok without a message', function() ok(1 == 1) ok(1 == 2) -end, {'gambiarra_test.lua:97'}, {'gambiarra_test.lua:98'}) +end, {'gambiarra_test.lua:140'}, {'gambiarra_test.lua:141'}) metatest('nok without a message', function() nok(1 == 2) nok(1 == 1) -end, {'gambiarra_test.lua:102'}, {'gambiarra_test.lua:103'}) +end, {'gambiarra_test.lua:145'}, {'gambiarra_test.lua:146'}) -- -- Equality tests @@ -232,12 +275,12 @@ metatest('fail with incorrect errormessage', function() fail(function() error("my error") end, "different error", "Failed with incorrect error") ok(true, 'unreachable code') end, {}, {'Failed with incorrect error', - 'expected errormessage "gambiarra_test.lua:232: my error" to contain "different error"'}) + 'expected errormessage "gambiarra_test.lua:275: my error" to contain "different error"'}) metatest('fail with not failing code', function() - fail(function() end, "my error", "Failed without error") + fail(function() end, "my error", "did not fail") ok(true, 'unreachable code') -end, {}, {"Failed without error", 'Expected to fail with Error containing "my error"'}) +end, {}, {"did not fail", 'Expected to fail with Error containing "my error"'}) -- -- except tests @@ -245,11 +288,9 @@ end, {}, {"Failed without error", 'Expected to fail with Error containing "my er metatest('error should panic', function() error("lua error") ok(true, 'unreachable code') -end, {}, {}, {'gambiarra_test.lua:246: lua error'}) +end, {}, {}, {'gambiarra_test.lua:289: lua error'}) -while #post_queue > 0 do - async_next() -end +drain_post_queue() -- -- Async tests @@ -270,8 +311,7 @@ metatest('async test without actually async', function(next) next() end, {'bar'}, {}, {}, true) -async_next() -async_next() +drain_post_queue() metatest('another async test after async queue drained', function(next) async(function() @@ -290,7 +330,7 @@ metatest('error should panic async', function(next) next() end) ok(true, 'foo') -end, {'foo'}, {}, {'gambiarra_test.lua:289: async Lua error'}, true) +end, {'foo'}, {}, {'gambiarra_test.lua:329: async Lua error'}, true) -- -- sync after async test @@ -309,37 +349,16 @@ metatest('check marker async', function() ok(eq(marker, "marked"), "marker check") end, {"marker check"}, {}) -while #post_queue + #queue > 0 do - async_next() -end - -- -- Finalize: check test results -- -local failed, passed = 0,0 -for i = 1,#expected do - if actual[i] == nil then +metatest("finishing up pending tests", function() + for i = 1,#expected -1 do print("--- FAIL "..expected[i].name..' (pending)') failed = failed + 1 - elseif not comparetables(expected[i].pass, actual[i].pass) then - print("--- FAIL "..expected[i].name..' (passed): expected ['.. - stringify(expected[i].pass)..'] vs ['.. - stringify(actual[i].pass)..']') - failed = failed + 1 - elseif not comparetables(expected[i].fail, actual[i].fail) then - print("--- FAIL "..expected[i].name..' (failed): expected ['.. - stringify(expected[i].fail)..'] vs ['.. - stringify(actual[i].fail)..']') - failed = failed + 1 - elseif not comparetables(expected[i].except, actual[i].except) then - print("--- FAIL "..expected[i].name..' (failed): expected ['.. - stringify(expected[i].except)..'] vs ['.. - stringify(actual[i].except)..']') - failed = failed + 1 - else - print("+++ Pass "..expected[i].name) - passed = passed + 1 end -end + print("failed: "..failed, "passed: "..passed) + node = orig_node +end, {}, {}) -print("failed: "..failed, "passed: "..passed) \ No newline at end of file +drain_post_queue() From 0340d426d6874c52ba74a0cd82dacaa54f1d3e7d Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 7 Jul 2020 16:37:57 +0200 Subject: [PATCH 12/36] Update file tests + add async tmr tests --- lua_tests/gambiarra/gambiarra_file.lua | 31 ++---------- lua_tests/gambiarra/gambiarra_tmr.lua | 66 ++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 26 deletions(-) create mode 100644 lua_tests/gambiarra/gambiarra_tmr.lua diff --git a/lua_tests/gambiarra/gambiarra_file.lua b/lua_tests/gambiarra/gambiarra_file.lua index 308d219064..1d08d07253 100644 --- a/lua_tests/gambiarra/gambiarra_file.lua +++ b/lua_tests/gambiarra/gambiarra_file.lua @@ -1,25 +1,5 @@ local test = require('gambiarra') --- set the output handler -local function TERMINAL_HANDLER(e, test, msg, errormsg) - if errormsg then - errormsg = ": "..errormsg - else - errormsg = "" - end - if 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) - else - print(e.." "..test) - end -end --- set the output handler --- test(TERMINAL_HANDLER) - local function cleanup() file.remove("testfile") file.remove("testfile2") @@ -37,7 +17,7 @@ end test('exist', function() cleanup() - ok(not file.exists("non existing file"), "non existing file") + nok(file.exists("non existing file"), "non existing file") file.putcontents("testfile", "testcontents") ok(file.exists("testfile"), "existing file") @@ -65,7 +45,7 @@ test('getcontents', function() local testcontent = "some content \0 and more" file.putcontents("testfile", testcontent) local content = file.getcontents("testfile") - ok(testcontent, content) + ok(eq(testcontent, content),"contents") end) test('getcontents non existent file', function() @@ -82,7 +62,7 @@ test('getcontents more than 1K', function() end f:close() content = file.getcontents("testfile") - eq(#content, 1700, "partial read") + ok(eq(#content, 1700), "long contents") end) test('list', function() @@ -184,11 +164,10 @@ test('stat existing file', function() file.putcontents("testfile", "testfile") local stat = file.stat("testfile") - - nok(stat == nil, "stat existing") + ok(stat != nil, "stat existing") ok(eq(stat.size, 8), "size") ok(eq(stat.name, "testfile"), "name") - nok(stat.time == nil, "no time") + ok(stat.time, "no time") ok(eq(stat.time.year, 1970), "year") ok(eq(stat.time.mon, 01), "mon") ok(eq(stat.time.day, 01), "day") diff --git a/lua_tests/gambiarra/gambiarra_tmr.lua b/lua_tests/gambiarra/gambiarra_tmr.lua new file mode 100644 index 0000000000..418e227679 --- /dev/null +++ b/lua_tests/gambiarra/gambiarra_tmr.lua @@ -0,0 +1,66 @@ +local test = require('gambiarra') + +-- set the output handler +local function TERMINAL_HANDLER(e, test, msg, errormsg) + if errormsg then + errormsg = ": "..errormsg + else + errormsg = "" + end + if 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) + else + print(e.." "..test) + end +end +-- set the output handler +test(TERMINAL_HANDLER) + +test('SINGLE alarm', function(next) + local t = tmr.create(); + local count = 0 + t:alarm(200, tmr.ALARM_SINGLE, + function(t) + count = count + 1 + ok(count <= 1, "only 1 invocation") + next() + end) + + ok(true, "sync end") +end, true) + +test('SEMI alarm', function(next) + local t = tmr.create(); + local count = 0 + t:alarm(200, tmr.ALARM_SEMI, + function(tp) + count = count + 1 + if count <= 1 then + tp:start() + return + end + ok(eq(count, 2), "only 2 invocations") + next() + end) + ok(true, "sync end") +end, true) + +test('AUTO alarm', function(next) + local t = tmr.create(); + local count = 0 + t:alarm(200, tmr.ALARM_AUTO, + function(tp) + count = count + 1 + if count == 2 then + tp:stop() + return next() + end + ok(count < 2, "only 2 invocations") + end) + ok(true, "sync end") +end, true) + From 50acd3f683b93f24539622cec4a2633cf073f32c Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 14 Jul 2020 18:18:59 +0200 Subject: [PATCH 13/36] Another fix in executing async test --- lua_tests/gambiarra/gambiarra.lua | 3 +++ lua_tests/gambiarra/gambiarra_file.lua | 2 +- lua_tests/gambiarra/gambiarra_test.lua | 12 ++++++++++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index 537d884088..28ad0fa4c6 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -159,6 +159,9 @@ return function(name, f, async) if not err:match('_*_TestAbort_*_') then handler('except', name, err) end + if async then + restore() + end end if not async then diff --git a/lua_tests/gambiarra/gambiarra_file.lua b/lua_tests/gambiarra/gambiarra_file.lua index 1d08d07253..e91efabccd 100644 --- a/lua_tests/gambiarra/gambiarra_file.lua +++ b/lua_tests/gambiarra/gambiarra_file.lua @@ -164,7 +164,7 @@ test('stat existing file', function() file.putcontents("testfile", "testfile") local stat = file.stat("testfile") - ok(stat != nil, "stat existing") + ok(stat ~= nil, "stat existing") ok(eq(stat.size, 8), "size") ok(eq(stat.name, "testfile"), "name") ok(stat.time, "no time") diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index de4d652f84..7518698717 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -311,6 +311,10 @@ metatest('async test without actually async', function(next) next() end, {'bar'}, {}, {}, true) +metatest('async fail in main', function(next) + ok(false, "async fail") +end, {}, {'async fail'}, {}, true) + drain_post_queue() metatest('another async test after async queue drained', function(next) @@ -324,13 +328,17 @@ end, {'foo', 'bar'}, {}, {}, true) -- -- except tests async -- -metatest('error should panic async', function(next) +metatest('async except in main', function(next) + error("async except") +end, {}, {}, {'gambiarra_test.lua:332: async except'}, true) + +metatest('async except in callback', function(next) async(function() -- error("async Lua error") next() end) ok(true, 'foo') -end, {'foo'}, {}, {'gambiarra_test.lua:329: async Lua error'}, true) +end, {'foo'}, {}, {'gambiarra_test.lua:337: async Lua error'}, true) -- -- sync after async test From 55965c6a92e63efe2cce78dde4593d70c0945ef4 Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 18 Jul 2020 17:39:52 +0200 Subject: [PATCH 14/36] Catch errors in callbacks using node.setonerror --- lua_tests/gambiarra/gambiarra.lua | 12 ++++++- lua_tests/gambiarra/gambiarra_test.lua | 43 +++++++++++++++++++++----- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index 28ad0fa4c6..0cbbc792b4 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -106,7 +106,7 @@ local env = _G local outputhandler = TERMINAL_HANDLER local function runpending() - if pendingtests[1] ~= nil then + if pendingtests[1] ~= nil then node.task.post(node.task.LOW_PRIORITY, function() pendingtests[1](runpending) end) @@ -134,6 +134,7 @@ return function(name, f, async) copyenv(prev, env) local restore = function() + if node then node.setonerror() end copyenv(env, prev) outputhandler('end', name) table.remove(pendingtests, 1) @@ -146,6 +147,14 @@ return function(name, f, async) f(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, msg) wrap(assertok, cond, msg) end @@ -153,6 +162,7 @@ return function(name, f, async) 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 diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index 7518698717..0671c5b148 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -5,6 +5,8 @@ local failed, passed = 0,0 local orig_node = node +local cbWrap = function(cb) return cb end + -- implement pseudo task handling for on host testing local post_queue = {} local drain_post_queue = function() end @@ -21,6 +23,23 @@ if not node then end end end + + local errorfunc + node.setonerror = function(fn) errorfunc = fn end + cbWrap = function(cb) + return function(...) + local ok, p1,p2,p3,p4 = pcall(cb, ...) + if not ok then + if errorfunc then + errorfunc(p1) + else + print(p1, "::::::::::::: reboot :::::::::::::") + end + else + return p1,p2,p3,p4 + end + end + end end -- Helper function to print arrays @@ -84,7 +103,7 @@ test(function(e, test, msg, errormsg) end) local async_queue = {} -local function async(f) table.insert(async_queue, f) end +local function async(f) table.insert(async_queue, cbWrap(f)) end local function async_next() local f = table.remove(async_queue, 1) if f then @@ -139,12 +158,12 @@ end, {'1~=2'}, {}) metatest('ok without a message', function() ok(1 == 1) ok(1 == 2) -end, {'gambiarra_test.lua:140'}, {'gambiarra_test.lua:141'}) +end, {'gambiarra_test.lua:159'}, {'gambiarra_test.lua:160'}) metatest('nok without a message', function() nok(1 == 2) nok(1 == 1) -end, {'gambiarra_test.lua:145'}, {'gambiarra_test.lua:146'}) +end, {'gambiarra_test.lua:164'}, {'gambiarra_test.lua:165'}) -- -- Equality tests @@ -275,7 +294,7 @@ metatest('fail with incorrect errormessage', function() fail(function() error("my error") end, "different error", "Failed with incorrect error") ok(true, 'unreachable code') end, {}, {'Failed with incorrect error', - 'expected errormessage "gambiarra_test.lua:275: my error" to contain "different error"'}) + 'expected errormessage "gambiarra_test.lua:294: my error" to contain "different error"'}) metatest('fail with not failing code', function() fail(function() end, "my error", "did not fail") @@ -288,7 +307,7 @@ end, {}, {"did not fail", 'Expected to fail with Error containing "my error"'}) metatest('error should panic', function() error("lua error") ok(true, 'unreachable code') -end, {}, {}, {'gambiarra_test.lua:289: lua error'}) +end, {}, {}, {'gambiarra_test.lua:308: lua error'}) drain_post_queue() @@ -330,15 +349,23 @@ end, {'foo', 'bar'}, {}, {}, true) -- metatest('async except in main', function(next) error("async except") -end, {}, {}, {'gambiarra_test.lua:332: async except'}, true) +end, {}, {}, {'gambiarra_test.lua:351: async except'}, true) + +metatest('async fail in callback', function(next) + async(function() + ok(false, "async fail") + next() + end) + ok(true, 'foo') +end, {'foo'}, {'async fail'}, {}, true) metatest('async except in callback', function(next) async(function() - -- error("async Lua error") + error("async Lua error") next() end) ok(true, 'foo') -end, {'foo'}, {}, {'gambiarra_test.lua:337: async Lua error'}, true) +end, {'foo'}, {}, {'gambiarra_test.lua:364: async Lua error'}, true) -- -- sync after async test From dc6445ed84408295cb527cb468215849ce6ea1eb Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 1 Aug 2020 14:59:07 +0200 Subject: [PATCH 15/36] change interface to return an object with several test methods --- lua_tests/gambiarra/gambiarra.lua | 34 ++++++++++++---- lua_tests/gambiarra/gambiarra_test.lua | 55 +++++++++++++++++++++----- 2 files changed, 72 insertions(+), 17 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index 0cbbc792b4..625a302ede 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -84,6 +84,13 @@ local function assertok(handler, name, cond, msg) end local function fail(handler, name, func, expected, msg) + if not msg then + -- debug.getinfo() does not exist in NodeMCU + -- msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline + msg = debug.traceback() + msg = msg:match(".*\n\t*([^\n]*): in.-\n\t*.*in function 'pcall'") + msg = msg:match(".-([^\\/]*)$") -- cut off path of filename + end local status, err = pcall(func) if status then local messagePart = "" @@ -121,13 +128,7 @@ local function copyenv(dest, src) dest.fail = src.fail end -return function(name, f, async) - if type(name) == 'function' then - outputhandler = name - env = f or _G - return - end - +local function testimpl(name, f, async) local testfn = function(next) local prev = {} @@ -161,7 +162,7 @@ return function(name, f, async) env.nok = function(cond, msg) env.ok(not cond, msg) end env.fail = function (func, expected, msg) wrap(fail, func, expected, msg) end - handler('begin', name); + handler('begin', name) node.setonerror(cbError) local ok, err = pcall(f, async and restore) if not ok then @@ -184,3 +185,20 @@ return function(name, f, async) 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 + +return {test = test, testasync = testasync, report = report} \ No newline at end of file diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index 0671c5b148..2f738a2f8f 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -1,4 +1,4 @@ -local test = require('gambiarra') +local N = require('gambiarra') local expected = {} local failed, passed = 0,0 @@ -61,7 +61,7 @@ local function comparetables(t1, t2) end -- Set meta test handler -test(function(e, test, msg, errormsg) +N.report(function(e, test, msg, errormsg) if e == 'begin' then currentTest = { name = test, @@ -124,8 +124,10 @@ local function metatest(name, f, expectedPassed, expectedFailed, expectedExcept, f(...) drain_async_queue() end + N.testasync(name, ff) + else + N.test(name, ff) end - test(name, ff, async) table.insert(expected, { name = name, pass = expectedPassed, @@ -158,12 +160,12 @@ end, {'1~=2'}, {}) metatest('ok without a message', function() ok(1 == 1) ok(1 == 2) -end, {'gambiarra_test.lua:159'}, {'gambiarra_test.lua:160'}) +end, {'gambiarra_test.lua:161'}, {'gambiarra_test.lua:162'}) metatest('nok without a message', function() nok(1 == 2) nok(1 == 1) -end, {'gambiarra_test.lua:164'}, {'gambiarra_test.lua:165'}) +end, {'gambiarra_test.lua:166'}, {'gambiarra_test.lua:167'}) -- -- Equality tests @@ -294,20 +296,54 @@ metatest('fail with incorrect errormessage', function() fail(function() error("my error") end, "different error", "Failed with incorrect error") ok(true, 'unreachable code') end, {}, {'Failed with incorrect error', - 'expected errormessage "gambiarra_test.lua:294: my error" to contain "different error"'}) + 'expected errormessage "gambiarra_test.lua:296: my error" to contain "different error"'}) + +metatest('fail with incorrect errormessage default message', function() + fail(function() error("my error") end, "different error") + ok(true, 'unreachable code') +end, {}, {'gambiarra_test.lua:302', + 'expected errormessage "gambiarra_test.lua:302: my error" to contain "different error"'}) metatest('fail with not failing code', function() fail(function() end, "my error", "did not fail") ok(true, 'unreachable code') end, {}, {"did not fail", 'Expected to fail with Error containing "my error"'}) +metatest('fail with failing code', function() + fail(function() error("my error") end, nil, "Failed as expected") + ok(true, 'reachable code') +end, {'Failed as expected', 'reachable code'}, {}) + +metatest('fail with not failing code', function() + fail(function() end, nil , "did not fail") + ok(true, 'unreachable code') +end, {}, {"did not fail", 'Expected to fail with Error'}) + +metatest('fail with not failing code default message', function() + fail(function() end) + ok(true, 'unreachable code') +end, {}, {"gambiarra_test.lua:323", 'Expected to fail with Error'}) + -- -- except tests -- metatest('error should panic', function() error("lua error") ok(true, 'unreachable code') -end, {}, {}, {'gambiarra_test.lua:308: lua error'}) +end, {}, {}, {'gambiarra_test.lua:331: lua error'}) + +-- +-- called function except +-- + +local function subfunc() + error("lua error") +end + +metatest('subroutine error should panic', function() + subfunc() + ok(true, 'unreachable code') +end, {}, {}, {'gambiarra_test.lua:340: lua error'}) drain_post_queue() @@ -349,7 +385,8 @@ end, {'foo', 'bar'}, {}, {}, true) -- metatest('async except in main', function(next) error("async except") -end, {}, {}, {'gambiarra_test.lua:351: async except'}, true) + ok(true, 'foo') +end, {}, {}, {'gambiarra_test.lua:387: async except'}, true) metatest('async fail in callback', function(next) async(function() @@ -365,7 +402,7 @@ metatest('async except in callback', function(next) next() end) ok(true, 'foo') -end, {'foo'}, {}, {'gambiarra_test.lua:364: async Lua error'}, true) +end, {'foo'}, {}, {'gambiarra_test.lua:401: async Lua error'}, true) -- -- sync after async test From 14c9b8df13be9b8e3a3a064047296e7f4648c796 Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 1 Aug 2020 14:59:32 +0200 Subject: [PATCH 16/36] Update README.md --- lua_tests/gambiarra/README.md | 83 ++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 26 deletions(-) diff --git a/lua_tests/gambiarra/README.md b/lua_tests/gambiarra/README.md index f42965b570..e3c3fcdc86 100644 --- a/lua_tests/gambiarra/README.md +++ b/lua_tests/gambiarra/README.md @@ -16,52 +16,72 @@ Or get only `gambiarra.lua` and start writing tests: ## Example -- Simple synchronous test - test('Check dogma', function() + tests = require("gambiarra.lua") + + tests.test('Check dogma', function() ok(2+2 == 5, 'two plus two equals five') end) -- A more advanced asyncrhonous test - test('Do it later', function(done) + tests.testasync('Do it later', function(done) someAsyncFn(function(result) ok(result == expected) done() -- this starts the next async test end) - end, true) -- 'true' means 'async test' here + end) ## API -`require('gambiarra')` returns a test function which can also be used to -customize test reports: +`require('gambiarra')` returns an object with the following functions: - local test = require('gambiarra') + local tests = require('gambiarra') `test(name:string, f:function, [async:bool])` allows you to define a new test: - test('My sync test', function() + tests.test('My sync test', function() end) - test('My async test', function(done) + tests.testasync('My async test', function(done) done() - end, true) + end) + +All test functions also define some helper functions that are added when the test is +executed - `ok`, `nok`, `fail`, `eq` and `spy`. + +`ok`, `nok`, `fail` are assert functions which will break the test if the condition is not met. + +`ok(cond:bool, [msg:string])`. It takes any +boolean condition and an optional assertion message. If no message is defined - +current filename and line will be used. If the condition evaluetes to thuthy nothing happens. +If it is falsy the message is printed and the test is aborted and the next test is executed. + + ok(1 == 2) -- prints 'foo.lua:42' + ok(1 == 2, 'one equals one') -- prints 'one equals one' + ok(1 == 1) -- prints nothing -`test()` defines also three helper functions that are added when test is -executed - `ok`, `eq` and `spy`. +`nok(cond:bool, [msg:string])` is a shorthand for `ok(not cond, 'msg')`. -`ok(cond:bool, [msg:string])` is a simple assertion helper. It takes any -boolean condition and an optional assertion message. If no message is define - -current filename and line will be used. + `nok(1 == 1)` -- prints 'foo.lua:42' + `nok(1 == 1, 'one equals one')` -- prints 'one equals one' - ok(1 == 1) -- prints 'foo.lua:42' - ok(1 == 1, 'one equals one') -- prints 'one equals one' +`fail(func:function, [expected:string], [msg:string])` tests a function for failure. If expected is given the errormessage poduced by the function must contain the given string else `fail` will fail the test. If no message is defined - +current filename and line will be used. + + `local function f() error("my error") end` + `nok(1 == 1)` -- prints 'foo.lua:42' + `fail(f, "different error", "Failed with incorrect error")` -- prints 'Failed with incorrect error' and + 'expected errormessage "gambiarra_test.lua:294: my error" to contain "different error"' + `eq(a, b)` is a helper to deeply compare lua variables. It supports numbers, -strings, booleans, nils, functions and tables. It's mostly useful within ok(): +strings, booleans, nils, functions and tables. It's mostly useful within ok() and nok(): ok(eq(1, 1)) ok(eq('foo', 'bar')) ok(eq({a='b',c='d'}, {c='d',a='b'}) -Finally, `spy([f])` creates function wrappers that remember each their call + +`spy([f])` creates function wrappers that remember each call (arguments, errors) but behaves much like the real function. Real function is optional, in this case spy will return nil, but will still record its calls. Spies are most helpful when passing them as callbacks and testing that they @@ -79,22 +99,30 @@ were called with correct values. ## Reports Another useful feature is that you can customize test reports as you need. -But default tests are printed in color using ANSI escape sequences and use +The default reports are printed in color using ANSI escape sequences and use UTF-8 symbols to indicate passed/failed state. If your environment doesn't support it - you can easily override this behavior as well as add any other information you need (number of passed/failed assertions, time the test took etc): +Events are: + +`begin` Will be called before each test +`end` Will be called after each test +`pass` Test has passed +`fail` Test has failed with not fulfilled assert (ok, nok, fail) +`except` Test has failed with unexpected error + + + local passed = 0 local failed = 0 - local clock = 0 - test(function(event, testfunc, msg) + tests.report(function(event, testfunc, msg) if event == 'begin' then print('Started test', testfunc) passed = 0 failed = 0 - clock = os.clock() elseif event == 'end' then print('Finished test', testfunc, passed, failed, os.clock() - clock) elseif event == 'pass' then @@ -109,14 +137,17 @@ etc): Additionally, you can pass a different environment to keep `_G` unpolluted: - test(function() ... end, myenv) + local myenv = {} + + tests.report(function() ... end, myenv) - test('Some test', function() + tests.test('Some test', function() myenv.ok(myenv.eq(...)) local f = myenv.spy() end) ## Appendix -Library supports Lua 5.1 and Lua 5.2. It is distributed under the MIT license. -Enjoy! +This Library is for NodeMCU versions Lua 5.1 and Lua 5.3. + +It is based on https://bitbucket.org/zserge/gambiarra and includes bugfixes, extensions of functionality and adaptions to NodeMCU requirements. From 6ea36654070d5c02dd5c1625999602e0763fa4cf Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 19 Sep 2020 08:05:01 +0200 Subject: [PATCH 17/36] Change interface of Gambiarra + add reason for failed eq --- lua_tests/gambiarra/README.md | 174 +++++++++++--------- lua_tests/gambiarra/gambiarra.lua | 219 +++++++++++++++---------- lua_tests/gambiarra/gambiarra_file.lua | 92 +++++++++-- lua_tests/gambiarra/gambiarra_test.lua | 75 +++++---- lua_tests/gambiarra/gambiarra_tmr.lua | 35 +--- 5 files changed, 361 insertions(+), 234 deletions(-) diff --git a/lua_tests/gambiarra/README.md b/lua_tests/gambiarra/README.md index e3c3fcdc86..e86e09b61b 100644 --- a/lua_tests/gambiarra/README.md +++ b/lua_tests/gambiarra/README.md @@ -7,28 +7,28 @@ minimal unit testing. Get the sources: -`hg clone https://bitbucket.org/zserge/gambiarra` +https://github.com/nodemcu/nodemcu-firmware/blob/release/lua_tests/gambiarra/gambiarra.lua -Or get only `gambiarra.lua` and start writing tests: - -`wget https://bitbucket.org/zserge/gambiarra/raw/tip/gambiarra.lua` +NOTE: you will have to use LFS to run this as it is too big to fit in memory. ## Example - -- Simple synchronous test - tests = require("gambiarra.lua") +``` Lua +-- Simple synchronous test +local tests = require("gambiarra.lua") - tests.test('Check dogma', function() - ok(2+2 == 5, 'two plus two equals five') - end) +tests.test('Check dogma', function() + ok(2+2 == 5, 'two plus two equals five') +end) - -- A more advanced asyncrhonous test - tests.testasync('Do it later', function(done) - someAsyncFn(function(result) - ok(result == expected) - done() -- this starts the next async test - end) - end) +-- A more advanced asynchronous test +tests.testasync('Do it later', function(done) + someAsyncFn(function(result) + ok(result == expected) + done() -- this starts the next async test + end) +end) +``` ## API @@ -36,14 +36,23 @@ Or get only `gambiarra.lua` and start writing tests: local tests = require('gambiarra') -`test(name:string, f:function, [async:bool])` allows you to define a new test: +`test(name:string, f:function)` allows you to define a new test: + +``` Lua +tests.test('My sync test', function() +end) +``` - tests.test('My sync test', function() - end) +`testasync(name:string, f:function(done:function))` allows you to define a new asynchronous test: +To tell gambuarra that the test is finished you need to call the function `done` which gets passed in. +In async scenarios the test function will usually terminate quickly but the test is still waiting for +some callback to be called before it is finished. - tests.testasync('My async test', function(done) - done() - end) +``` Lua +tests.testasync('My async test', function(done) + done() +end) +``` All test functions also define some helper functions that are added when the test is executed - `ok`, `nok`, `fail`, `eq` and `spy`. @@ -55,31 +64,39 @@ boolean condition and an optional assertion message. If no message is defined - current filename and line will be used. If the condition evaluetes to thuthy nothing happens. If it is falsy the message is printed and the test is aborted and the next test is executed. - ok(1 == 2) -- prints 'foo.lua:42' - ok(1 == 2, 'one equals one') -- prints 'one equals one' - ok(1 == 1) -- prints nothing +``` Lua +ok(1 == 2) -- prints 'foo.lua:42' +ok(1 == 2, 'one equals one') -- prints 'one equals one' +ok(1 == 1) -- prints nothing +``` `nok(cond:bool, [msg:string])` is a shorthand for `ok(not cond, 'msg')`. - `nok(1 == 1)` -- prints 'foo.lua:42' - `nok(1 == 1, 'one equals one')` -- prints 'one equals one' +``` Lua +nok(1 == 1) -- prints 'foo.lua:42' +nok(1 == 1, 'one equals one') -- prints 'one equals one' +``` `fail(func:function, [expected:string], [msg:string])` tests a function for failure. If expected is given the errormessage poduced by the function must contain the given string else `fail` will fail the test. If no message is defined - current filename and line will be used. - `local function f() error("my error") end` - `nok(1 == 1)` -- prints 'foo.lua:42' - `fail(f, "different error", "Failed with incorrect error")` -- prints 'Failed with incorrect error' and - 'expected errormessage "gambiarra_test.lua:294: my error" to contain "different error"' - +``` Lua +local function f() error("my error") end +fail(f, "expected error", "Failed with incorrect error") + -- fails with 'Failed with incorrect error' and + -- 'expected errormessage "foo.lua:2: my error" to contain "expected error"' +``` `eq(a, b)` is a helper to deeply compare lua variables. It supports numbers, strings, booleans, nils, functions and tables. It's mostly useful within ok() and nok(): +If the variables are equal it returns `true` +else it returns `{msg=''}` This is spechially handled by `ok` and `nok` - ok(eq(1, 1)) - ok(eq('foo', 'bar')) - ok(eq({a='b',c='d'}, {c='d',a='b'}) - +``` Lua +ok(eq(1, 1)) +ok(eq({a='b',c='d'}, {c='d',a='b'}) +ok(eq('foo', 'bar')) -- will fail +``` `spy([f])` creates function wrappers that remember each call (arguments, errors) but behaves much like the real function. Real function is @@ -87,26 +104,28 @@ optional, in this case spy will return nil, but will still record its calls. Spies are most helpful when passing them as callbacks and testing that they were called with correct values. - local f = spy(function(s) return #s end) - ok(f('hello') == 5) - ok(f('foo') == 3) - ok(#f.called == 2) - ok(eq(f.called[1], {'hello'}) - ok(eq(f.called[2], {'foo'}) - f(nil) - ok(f.errors[3] ~= nil) +``` Lua +local f = spy(function(s) return #s end) +ok(f('hello') == 5) +ok(f('foo') == 3) +ok(#f.called == 2) +ok(eq(f.called[1], {'hello'}) +ok(eq(f.called[2], {'foo'}) +f(nil) +ok(f.errors[3] ~= nil) +``` ## Reports Another useful feature is that you can customize test reports as you need. -The default reports are printed in color using ANSI escape sequences and use -UTF-8 symbols to indicate passed/failed state. If your environment doesn't -support it - you can easily override this behavior as well as add any other +The default reports just more or less prints out a basic report. +You can easily override this behavior as well as add any other information you need (number of passed/failed assertions, time the test took etc): Events are: - +`start` when testing starts +`finish` when all tests have finished `begin` Will be called before each test `end` Will be called after each test `pass` Test has passed @@ -114,37 +133,42 @@ Events are: `except` Test has failed with unexpected error - - local passed = 0 - local failed = 0 - - tests.report(function(event, testfunc, msg) - if event == 'begin' then - print('Started test', testfunc) - passed = 0 - failed = 0 - elseif event == 'end' then - print('Finished test', testfunc, passed, failed, os.clock() - clock) - elseif event == 'pass' then - passed = passed + 1 - elseif event == 'fail' then - print('FAIL', testfunc, msg) - failed = failed + 1 - elseif event == 'except' then - print('ERROR', testfunc, msg) - end - end) +``` Lua +local passed = 0 +local failed = 0 + +tests.report(function(event, testfunc, msg) + if event == 'begin' then + print('Started test', testfunc) + passed = 0 + failed = 0 + elseif event == 'end' then + print('Finished test', testfunc, passed, failed) + elseif event == 'pass' then + passed = passed + 1 + elseif event == 'fail' then + print('FAIL', testfunc, msg) + failed = failed + 1 + elseif event == 'except' then + print('ERROR', testfunc, msg) + end +end) +``` Additionally, you can pass a different environment to keep `_G` unpolluted: +You need to set it, so the helper functions mentioned above can be added before calling +the test function. + +``` Lua +local myenv = {} - local myenv = {} - - tests.report(function() ... end, myenv) +tests.report(function() ... end, myenv) - tests.test('Some test', function() - myenv.ok(myenv.eq(...)) - local f = myenv.spy() - end) +tests.test('Some test', function() + myenv.ok(myenv.eq(...)) + local f = myenv.spy() +end) +``` ## Appendix diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index 625a302ede..9812cb414b 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -4,34 +4,55 @@ local function TERMINAL_HANDLER(e, test, msg, errormsg) else errormsg = "" end - if e == 'pass' then + 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 = ""} + 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 {false, "type 1 is "..type(a)..", type 2 is "..type(b)} end + 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 - return string.dump(a) == string.dump(b) + 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 false end + 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 or not deepeq(v, b[k]) then return false end + 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 or not deepeq(v, a[k]) then return false end + 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 @@ -61,7 +82,12 @@ local function spy(f) return s end -local function assertok(handler, name, cond, msg) +local function assertok(handler, name, invert, cond, msg) + local errormsg + if type(cond) == 'table' and cond.msg then + errormsg = cond.msg + cond = false + end if not msg then -- debug.getinfo() does not exist in NodeMCU -- msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline @@ -69,14 +95,12 @@ local function assertok(handler, name, cond, msg) msg = msg:match(".*\n\t*([^\n]*): in.-\n\t*.*in function 'pcall'") msg = msg:match(".-([^\\/]*)$") -- cut off path of filename end - local errormsg - if type(cond) == "table" then - errormsg = cond[2] - cond = cond[1] + if invert then + cond = not cond end if cond then - handler('pass', name, msg, errormsg) + handler('pass', name, msg) else handler('fail', name, msg, errormsg) error('_*_TestAbort_*_') @@ -108,97 +132,122 @@ local function fail(handler, name, func, expected, msg) handler('pass', name, msg) end -local pendingtests = {} -local env = _G -local outputhandler = TERMINAL_HANDLER +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 runpending() - if pendingtests[1] ~= nil then - node.task.post(node.task.LOW_PRIORITY, function() - pendingtests[1](runpending) - 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 -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 + print("failed with", err) + 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) + if next then next() end + end + + local function wrap(f, ...) + f(handler, name, ...) + end -local function testimpl(name, f, async) - local testfn = function(next) + 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 - local prev = {} - copyenv(prev, env) + 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 - local restore = function() - if node then node.setonerror() end - copyenv(env, prev) - outputhandler('end', name) - table.remove(pendingtests, 1) - if next then next() end + if not async then + restore() + end end - local handler = outputhandler - - local function wrap(f, ...) - f(handler, name, ...) + if not started then + outputhandler('start', testrunname) + started = true 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, msg) wrap(assertok, cond, msg) end - env.nok = function(cond, msg) env.ok(not cond, msg) 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 + table.insert(pendingtests, testfn) + if #pendingtests == 1 then + runpending() end + end - if not async then - restore() - end + local function test(name, f) + testimpl(name, f) end - table.insert(pendingtests, testfn) - if #pendingtests == 1 then - runpending() + local function testasync(name, f) + testimpl(name, f, true) end -end -local function test(name, f) - testimpl(name, f) -end + local function report(f, envP) + if type(f) == 'function' then + outputhandler = f + env = envP or _G + end + 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 + return {test = test, testasync = testasync, report = report} end -return {test = test, testasync = testasync, report = report} \ No newline at end of file +return NTest + diff --git a/lua_tests/gambiarra/gambiarra_file.lua b/lua_tests/gambiarra/gambiarra_file.lua index e91efabccd..fbd2e17e97 100644 --- a/lua_tests/gambiarra/gambiarra_file.lua +++ b/lua_tests/gambiarra/gambiarra_file.lua @@ -1,4 +1,4 @@ -local test = require('gambiarra') +local N = require('gambiarra')("file") local function cleanup() file.remove("testfile") @@ -15,7 +15,7 @@ local function buildfn(fn, ...) return fnp end -test('exist', function() +N.test('exist', function() cleanup() nok(file.exists("non existing file"), "non existing file") @@ -24,14 +24,14 @@ test('exist', function() end) -test('fscfg', function() +N.test('fscfg', function() cleanup() local start, size = file.fscfg() ok(start, "start") ok(size, "size") end) -test('fsinfo', function() +N.test('fsinfo', function() cleanup() local remaining, used, total = file.fsinfo() ok(remaining, "remaining") @@ -40,7 +40,7 @@ test('fsinfo', function() ok(eq(remaining+used, total), "size maths") end) -test('getcontents', function() +N.test('getcontents', function() cleanup() local testcontent = "some content \0 and more" file.putcontents("testfile", testcontent) @@ -48,12 +48,12 @@ test('getcontents', function() ok(eq(testcontent, content),"contents") end) -test('getcontents non existent file', function() +N.test('getcontents non existent file', function() cleanup() nok(file.getcontents("non existing file"), "non existent file") end) -test('getcontents more than 1K', function() +N.test('getcontents more than 1K', function() cleanup() local f = file.open("testfile", "w") local i @@ -65,7 +65,69 @@ test('getcontents more than 1K', function() ok(eq(#content, 1700), "long contents") end) -test('list', function() +N.test('read more than 1K', function() + cleanup() + local f = file.open("testfile", "w") + local i + for i = 1,100 do + f:write("some text to test") + end + f:close() + f = file.open("testfile","r") + local buffer = f:read() + ok(eq(#buffer, 1024), "first block") + buffer = f:read() + f:close() + ok(eq(#buffer, 1700-1024), "second block") +end) + +function makefile(name, num128) + local s128 = "16 bytes written" + s128 = s128..s128..s128..s128 + s128 = s128..s128 + local f = file.open(name, "w") + for i = 1, num128 do + f:write(s128) + end + f:close() +end + +N.test('read 7*128 bytes', function() + cleanup() + makefile("testfile", 7) + f = file.open("testfile","r") + local buffer = f:read() + f:close() + nok(eq(buffer, nil), "nil first block") + ok(eq(#buffer, 128*7), "length first block") +end) + +N.test('read 8*128 bytes long file', function() + cleanup() + makefile("testfile", 8) + local f = file.open("testfile","r") + local buffer = f:read() + nok(eq(buffer, nil), "nil first block") + ok(eq(#buffer, 128*8), "size first block") + buffer = f:read() + f:close() + ok(eq(buffer, nil), "nil second block") +end) + +N.test('read 9*128 bytes', function() + cleanup() + makefile("testfile", 9) + f = file.open("testfile","r") + local buffer = f:read() + nok(eq(buffer, nil), "nil first block") + ok(eq(#buffer, 1024), "size first block") + buffer = f:read() + f:close() + nok(eq(buffer, nil), "nil second block") + ok(eq(#buffer, 128*8-1024), "size second block") +end) + +N.test('list', function() cleanup() local files @@ -96,7 +158,7 @@ test('list', function() ok(count(files) >= 2, "several files found") end) -test('open non existing', function() +N.test('open non existing', function() cleanup() local function testopen(test, filename, mode) test(file.open(filename, mode), mode) @@ -114,7 +176,7 @@ test('open non existing', function() fail(buildfn(file.open, "testfile"), "errormsg", "x") -- shouldn't this fail? end) -test('open existing', function() +N.test('open existing', function() cleanup() local function testopen(mode, position) file.putcontents("testfile", "testcontent") @@ -134,7 +196,7 @@ test('open existing', function() fail(buildfn(file.open, "testfile"), "errormsg", "x") -- shouldn't this fail? end) -test('remove', function() +N.test('remove', function() cleanup() file.putcontents("testfile", "testfile") @@ -142,7 +204,7 @@ test('remove', function() ok(file.remove("testfile") == nil, "non existing file") end) -test('rename', function() +N.test('rename', function() cleanup() file.putcontents("testfile", "testfile") @@ -159,12 +221,12 @@ test('rename', function() ok(file.exists("testfile2"), "to file exists") end) -test('stat existing file', function() +N.test('stat existing file', function() cleanup() file.putcontents("testfile", "testfile") local stat = file.stat("testfile") - ok(stat ~= nil, "stat existing") + ok(stat, "stat existing") ok(eq(stat.size, 8), "size") ok(eq(stat.name, "testfile"), "name") ok(stat.time, "no time") @@ -181,7 +243,7 @@ test('stat existing file', function() nok(stat.is_arch, "is_arch") end) -test('stat non existing file', function() +N.test('stat non existing file', function() cleanup() local stat = file.stat("not existing file") diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index 2f738a2f8f..2e23289f06 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -1,6 +1,6 @@ -local N = require('gambiarra') +local N = require('gambiarra')("selftest") -local expected = {} +local expected, currentTest = {} local failed, passed = 0,0 local orig_node = node @@ -70,24 +70,30 @@ N.report(function(e, test, msg, errormsg) except = {} } elseif e == 'end' then + local pass = true if not comparetables(expected[1].pass, currentTest.pass) then print("--- FAIL "..expected[1].name..' (passed): expected ['.. stringify(expected[1].pass)..'] vs ['.. stringify(currentTest.pass)..']') - failed = failed + 1 - elseif not comparetables(expected[1].fail, currentTest.fail) then + pass = false + end + if not comparetables(expected[1].fail, currentTest.fail) then print("--- FAIL "..expected[1].name..' (failed): expected ['.. stringify(expected[1].fail)..'] vs ['.. stringify(currentTest.fail)..']') - failed = failed + 1 - elseif not comparetables(expected[1].except, currentTest.except) then + pass = false + end + if not comparetables(expected[1].except, currentTest.except) then print("--- FAIL "..expected[1].name..' (failed): expected ['.. stringify(expected[1].except)..'] vs ['.. stringify(currentTest.except)..']') - failed = failed + 1 - else + pass = false + end + if pass then print("+++ Pass "..expected[1].name) passed = passed + 1 + else + failed = failed + 1 end table.remove(expected, 1) elseif e == 'pass' then @@ -99,6 +105,10 @@ N.report(function(e, test, msg, errormsg) elseif e == 'except' then table.insert(currentTest.except, msg) if errormsg then table.insert(currentTest.except, errormsg) end + elseif e == 'start' or e == 'finish' then + -- ignore + else + print("Extra output: ", e, test, msg, errormsg) end end) @@ -160,19 +170,21 @@ end, {'1~=2'}, {}) metatest('ok without a message', function() ok(1 == 1) ok(1 == 2) -end, {'gambiarra_test.lua:161'}, {'gambiarra_test.lua:162'}) +end, {'gambiarra_test.lua:175'}, {'gambiarra_test.lua:176'}) metatest('nok without a message', function() - nok(1 == 2) + nok(1 == "") nok(1 == 1) -end, {'gambiarra_test.lua:166'}, {'gambiarra_test.lua:167'}) +end, {'gambiarra_test.lua:180'}, {'gambiarra_test.lua:181'}) -- -- Equality tests -- metatest('eq nil', function() ok(eq(nil, nil), 'nil == nil') -end, {'nil == nil'}, {}) + nok(eq("", nil), '"" != nil') + nok(eq(nil, ""), 'nil != ""') +end, {'nil == nil', '"" != nil', 'nil != ""'}, {}) metatest('eq primitives', function() ok(eq('foo', 'foo'), 'str == str') @@ -211,7 +223,7 @@ metatest('eq nested objects', function() ['1'] = { name = 'mhc', age = 28 }, ['2'] = { name = 'arb', age = 27 } }), 'not equal') -end, {'equal'}, {'not equal'}) +end, {'equal'}, {'not equal', 'different numbers expected 26 vs. 27'}) metatest('eq functions', function() ok(eq(function(x) return x end, function(x) return x end), 'equal') @@ -221,10 +233,11 @@ end, {'equal', 'wrong variable', 'wrong code'}, {}) metatest('eq different types', function() local eqos = eq({a=1,b=2}, "text") - ok(eq(eqos[2], "type 1 is table, type 2 is string"), 'object/string') + ok(eq(eqos.msg, "type 1 is table, type 2 is string"), 'object/string') local eqfn = eq(function(x) return x end, 12) - ok(eq(eqfn[2], "type 1 is function, type 2 is number"), 'function/int') -end, {"object/string", "function/int"}, {}) + ok(eq(eqfn.msg, "type 1 is function, type 2 is number"), 'function/int') + nok(eq(12, "Hallo"), 'int/string') +end, {"object/string", "function/int", 'int/string'}, {}) -- -- Spies @@ -241,7 +254,7 @@ end, {'not called', 'called', 'called once', 'called twice'}, {}) metatest('spy with arguments', function() local x = 0 - function setX(n) x = n end + local function setX(n) x = n end local f = spy(setX) f(1) ok(x == 1, 'x == 1') @@ -252,15 +265,15 @@ metatest('spy with arguments', function() end, {'x == 1', 'called with 1', 'x == 42', 'called with 42'}, {}) metatest('spy with nils', function() - function nils(a, dummy, b) return a, nil, b, nil end + local function nils(a, dummy, b) return a, nil, b, nil end local f = spy(nils) - r1, r2, r3, r4 = f(1, nil, 2) + local r1, r2, r3, r4 = f(1, nil, 2) ok(eq(f.called, {{1, nil, 2}}), 'nil in args') ok(r1 == 1 and r2 == nil and r3 == 2 and r4 == nil, 'nil in returns') end, {'nil in args', 'nil in returns'}, {}) metatest('spy with exception', function() - function throwSomething(s) + local function throwSomething(s) if s ~= 'nopanic' then error('panic: '..s) end end local f = spy(throwSomething) @@ -296,13 +309,13 @@ metatest('fail with incorrect errormessage', function() fail(function() error("my error") end, "different error", "Failed with incorrect error") ok(true, 'unreachable code') end, {}, {'Failed with incorrect error', - 'expected errormessage "gambiarra_test.lua:296: my error" to contain "different error"'}) + 'expected errormessage "gambiarra_test.lua:313: my error" to contain "different error"'}) metatest('fail with incorrect errormessage default message', function() fail(function() error("my error") end, "different error") ok(true, 'unreachable code') -end, {}, {'gambiarra_test.lua:302', - 'expected errormessage "gambiarra_test.lua:302: my error" to contain "different error"'}) +end, {}, {'gambiarra_test.lua:319', + 'expected errormessage "gambiarra_test.lua:319: my error" to contain "different error"'}) metatest('fail with not failing code', function() fail(function() end, "my error", "did not fail") @@ -322,7 +335,7 @@ end, {}, {"did not fail", 'Expected to fail with Error'}) metatest('fail with not failing code default message', function() fail(function() end) ok(true, 'unreachable code') -end, {}, {"gambiarra_test.lua:323", 'Expected to fail with Error'}) +end, {}, {"gambiarra_test.lua:340", 'Expected to fail with Error'}) -- -- except tests @@ -330,7 +343,7 @@ end, {}, {"gambiarra_test.lua:323", 'Expected to fail with Error'}) metatest('error should panic', function() error("lua error") ok(true, 'unreachable code') -end, {}, {}, {'gambiarra_test.lua:331: lua error'}) +end, {}, {}, {'gambiarra_test.lua:348: lua error'}) -- -- called function except @@ -343,9 +356,9 @@ end metatest('subroutine error should panic', function() subfunc() ok(true, 'unreachable code') -end, {}, {}, {'gambiarra_test.lua:340: lua error'}) +end, {}, {}, {'gambiarra_test.lua:357: lua error'}) -drain_post_queue() +--drain_post_queue() -- -- Async tests @@ -370,7 +383,7 @@ metatest('async fail in main', function(next) ok(false, "async fail") end, {}, {'async fail'}, {}, true) -drain_post_queue() +--drain_post_queue() metatest('another async test after async queue drained', function(next) async(function() @@ -385,8 +398,8 @@ end, {'foo', 'bar'}, {}, {}, true) -- metatest('async except in main', function(next) error("async except") - ok(true, 'foo') -end, {}, {}, {'gambiarra_test.lua:387: async except'}, true) + ok(true, 'unreachable code') +end, {}, {}, {'gambiarra_test.lua:404: async except'}, true) metatest('async fail in callback', function(next) async(function() @@ -402,7 +415,7 @@ metatest('async except in callback', function(next) next() end) ok(true, 'foo') -end, {'foo'}, {}, {'gambiarra_test.lua:401: async Lua error'}, true) +end, {'foo'}, {}, {'gambiarra_test.lua:418: async Lua error'}, true) -- -- sync after async test diff --git a/lua_tests/gambiarra/gambiarra_tmr.lua b/lua_tests/gambiarra/gambiarra_tmr.lua index 418e227679..4b341654a6 100644 --- a/lua_tests/gambiarra/gambiarra_tmr.lua +++ b/lua_tests/gambiarra/gambiarra_tmr.lua @@ -1,26 +1,6 @@ -local test = require('gambiarra') +local N = require('gambiarra')("tmr") --- set the output handler -local function TERMINAL_HANDLER(e, test, msg, errormsg) - if errormsg then - errormsg = ": "..errormsg - else - errormsg = "" - end - if 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) - else - print(e.." "..test) - end -end --- set the output handler -test(TERMINAL_HANDLER) - -test('SINGLE alarm', function(next) +N.testasync('SINGLE alarm', function(next) local t = tmr.create(); local count = 0 t:alarm(200, tmr.ALARM_SINGLE, @@ -31,9 +11,9 @@ test('SINGLE alarm', function(next) end) ok(true, "sync end") -end, true) +end) -test('SEMI alarm', function(next) +N.testasync('SEMI alarm', function(next) local t = tmr.create(); local count = 0 t:alarm(200, tmr.ALARM_SEMI, @@ -47,9 +27,9 @@ test('SEMI alarm', function(next) next() end) ok(true, "sync end") -end, true) +end) -test('AUTO alarm', function(next) +N.testasync('AUTO alarm', function(next) local t = tmr.create(); local count = 0 t:alarm(200, tmr.ALARM_AUTO, @@ -62,5 +42,4 @@ test('AUTO alarm', function(next) ok(count < 2, "only 2 invocations") end) ok(true, "sync end") -end, true) - +end) From 23a9dab815d2785122a7361d471c4d0fb3b6b90e Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 19 Sep 2020 08:42:14 +0200 Subject: [PATCH 18/36] Update gambiarra documentation --- lua_tests/gambiarra/README.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lua_tests/gambiarra/README.md b/lua_tests/gambiarra/README.md index e86e09b61b..c0080a0079 100644 --- a/lua_tests/gambiarra/README.md +++ b/lua_tests/gambiarra/README.md @@ -15,7 +15,7 @@ NOTE: you will have to use LFS to run this as it is too big to fit in memory. ``` Lua -- Simple synchronous test -local tests = require("gambiarra.lua") +local tests = require("gambiarra.lua")("first testrun") tests.test('Check dogma', function() ok(2+2 == 5, 'two plus two equals five') @@ -32,9 +32,17 @@ end) ## API -`require('gambiarra')` returns an object with the following functions: +`require('gambiarra')` returns an new function which must be called with a string. - local tests = require('gambiarra') +``` Lua + local new = require('gambiarra') +``` + +`new(testrunname:string)` returns an object with the following functions: + +``` Lua + local tests = new("first testrun") +``` `test(name:string, f:function)` allows you to define a new test: From 59c49625b3512b5f0d1fab27f000207c9dc59a6f Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 5 Oct 2020 21:51:25 +0200 Subject: [PATCH 19/36] Add coroutine testcases to gambiarra --- lua_tests/gambiarra/README.md | 34 +++++- lua_tests/gambiarra/gambiarra.lua | 51 +++++++- lua_tests/gambiarra/gambiarra_test.lua | 156 ++++++++++++++++++++----- lua_tests/gambiarra/gambiarra_tmr.lua | 41 +++++++ 4 files changed, 245 insertions(+), 37 deletions(-) diff --git a/lua_tests/gambiarra/README.md b/lua_tests/gambiarra/README.md index c0080a0079..d04d275c76 100644 --- a/lua_tests/gambiarra/README.md +++ b/lua_tests/gambiarra/README.md @@ -15,7 +15,7 @@ NOTE: you will have to use LFS to run this as it is too big to fit in memory. ``` Lua -- Simple synchronous test -local tests = require("gambiarra.lua")("first testrun") +local tests = require("gambiarra")("first testrun") tests.test('Check dogma', function() ok(2+2 == 5, 'two plus two equals five') @@ -28,6 +28,13 @@ tests.testasync('Do it later', function(done) done() -- this starts the next async test end) end) + +-- An asynchronous test using a coroutine +tests.testco('Do it in place', function(getCB, waitCB) + someAsyncFn(getCB("callback")) + local CBName = waitCB() + ok(CBName, "callback") +end) ``` ## API @@ -62,6 +69,31 @@ tests.testasync('My async test', function(done) end) ``` +`testco(name:string, f:function(getCB:function, waitCB:function))` allows you to define a new asynchronous +test which runs in a coroutine: +This allows handling callbacks in the test in a linear way. simply use getCB to create a new callback stub. +waitCB blocks the test until the callback is called and returns the parameters. + +``` Lua +tests.testco('My coroutine test', function(getCB, waitCB) +end) + +tests.testco('My coroutine test with callback', function(getCB, waitCB) + local t = tmr.create(); + t:alarm(200, tmr.ALARM_AUTO, getCB("timer")) + local name, tCB = waitCB() + ok(eq(name, "timer"), "CB name matches") + + name, tCB = waitCB() + ok(eq("timer", name), "CB name matches") + + tCB:stop() + + ok(true, "coroutine end") +end) + +``` + All test functions also define some helper functions that are added when the test is executed - `ok`, `nok`, `fail`, `eq` and `spy`. diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index 9812cb414b..f3261dacd3 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -92,7 +92,7 @@ local function assertok(handler, name, invert, cond, msg) -- debug.getinfo() does not exist in NodeMCU -- msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline msg = debug.traceback() - msg = msg:match(".*\n\t*([^\n]*): in.-\n\t*.*in function 'pcall'") + msg = msg:match(".*\n\t*([^\n]*): in.-\n\t*.*in function 'pcall'") or msg:match(".*: in.-\n\t*.*in function '[n]?ok'\n\t*([^\n]*)\n") msg = msg:match(".-([^\\/]*)$") -- cut off path of filename end @@ -112,7 +112,7 @@ local function fail(handler, name, func, expected, msg) -- debug.getinfo() does not exist in NodeMCU -- msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline msg = debug.traceback() - msg = msg:match(".*\n\t*([^\n]*): in.-\n\t*.*in function 'pcall'") + msg = msg:match(".*\n\t*([^\n]*): in.-\n\t*.*in function 'pcall'") or msg:match(".*: in.-\n\t*.*in function 'fail'\n\t*([^\n]*)\n") msg = msg:match(".-([^\\/]*)$") -- cut off path of filename end local status, err = pcall(func) @@ -169,7 +169,6 @@ local function NTest(testrunname, failoldinterface) local restore = function(err) if err then - print("failed with", err) err = err:match(".-([^\\/]*)$") -- cut off path of filename if not err:match('_*_TestAbort_*_') then handler('except', name, err) @@ -245,8 +244,52 @@ local function NTest(testrunname, failoldinterface) end end + local currentCoName - return {test = test, testasync = testasync, report = report} + 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 diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index 2e23289f06..5155dd8dd1 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -1,6 +1,8 @@ local N = require('gambiarra')("selftest") -local expected, currentTest = {} +local expected = {} +local currentTest = {} + local failed, passed = 0,0 local orig_node = node @@ -8,21 +10,30 @@ local orig_node = node local cbWrap = function(cb) return cb end -- implement pseudo task handling for on host testing -local post_queue = {} local drain_post_queue = function() end -if not node then - node = {task = {}} - node.task.post = function (p, f) - table.insert(post_queue, f) - end + +if not node then + local post_queue = {{},{},{}} + drain_post_queue = function() - while #post_queue > 0 do - local f = table.remove(post_queue, 1) - if f then - f() + while #post_queue[1] + #post_queue[2] + #post_queue[3] > 0 do + for i = 3, 1, -1 do + if #post_queue[i] > 0 then + local f = table.remove(post_queue[i], 1) + if f then + f() + end + break end end end + end + + node = {} + node.task = {LOW_PRIORITY = 1, MEDIUM_PRIORITY = 2, HIGH_PRIORITY = 3} + node.task.post = function (p, f) + table.insert(post_queue[p], f) + end local errorfunc node.setonerror = function(fn) errorfunc = fn end @@ -45,15 +56,15 @@ end -- Helper function to print arrays local function stringify(t) local s = '' - for i=1,#t do - s = s .. '"' .. t[i] .. '"' .. ' ' + for i=1,#(t or {}) do + s = s .. '"' .. t[i] .. '"' .. ', ' end - return s:gsub('%s*$', '') + return s:gsub('..$', '') end -- Helper function to compare two tables local function comparetables(t1, t2) - if #t1 ~= #t2 then return false end + if #t1 ~= (t2 and #t2 or 0) then return false end for i=1,#t1 do if t1[i] ~= t2[i] then return false end end @@ -63,12 +74,7 @@ end -- Set meta test handler N.report(function(e, test, msg, errormsg) if e == 'begin' then - currentTest = { - name = test, - pass = {}, - fail = {}, - except = {} - } + currentTest.name = test elseif e == 'end' then local pass = true if not comparetables(expected[1].pass, currentTest.pass) then @@ -95,14 +101,18 @@ N.report(function(e, test, msg, errormsg) else failed = failed + 1 end + currentTest = {} table.remove(expected, 1) elseif e == 'pass' then + currentTest.pass = currentTest.pass or {} table.insert(currentTest.pass, msg) if errormsg then table.insert(currentTest.pass, errormsg) end elseif e == 'fail' then + currentTest.fail = currentTest.fail or {} table.insert(currentTest.fail, msg) if errormsg then table.insert(currentTest.fail, errormsg) end elseif e == 'except' then + currentTest.except = currentTest.except or {} table.insert(currentTest.except, msg) if errormsg then table.insert(currentTest.except, errormsg) end elseif e == 'start' or e == 'finish' then @@ -134,7 +144,11 @@ local function metatest(name, f, expectedPassed, expectedFailed, expectedExcept, f(...) drain_async_queue() end - N.testasync(name, ff) + if (async == "co") then + N.testco(name,ff) + else + N.testasync(name, ff) + end else N.test(name, ff) end @@ -170,12 +184,12 @@ end, {'1~=2'}, {}) metatest('ok without a message', function() ok(1 == 1) ok(1 == 2) -end, {'gambiarra_test.lua:175'}, {'gambiarra_test.lua:176'}) +end, {'gambiarra_test.lua:185'}, {'gambiarra_test.lua:186'}) metatest('nok without a message', function() nok(1 == "") nok(1 == 1) -end, {'gambiarra_test.lua:180'}, {'gambiarra_test.lua:181'}) +end, {'gambiarra_test.lua:190'}, {'gambiarra_test.lua:191'}) -- -- Equality tests @@ -309,13 +323,13 @@ metatest('fail with incorrect errormessage', function() fail(function() error("my error") end, "different error", "Failed with incorrect error") ok(true, 'unreachable code') end, {}, {'Failed with incorrect error', - 'expected errormessage "gambiarra_test.lua:313: my error" to contain "different error"'}) + 'expected errormessage "gambiarra_test.lua:323: my error" to contain "different error"'}) metatest('fail with incorrect errormessage default message', function() fail(function() error("my error") end, "different error") ok(true, 'unreachable code') -end, {}, {'gambiarra_test.lua:319', - 'expected errormessage "gambiarra_test.lua:319: my error" to contain "different error"'}) +end, {}, {'gambiarra_test.lua:329', + 'expected errormessage "gambiarra_test.lua:329: my error" to contain "different error"'}) metatest('fail with not failing code', function() fail(function() end, "my error", "did not fail") @@ -335,7 +349,7 @@ end, {}, {"did not fail", 'Expected to fail with Error'}) metatest('fail with not failing code default message', function() fail(function() end) ok(true, 'unreachable code') -end, {}, {"gambiarra_test.lua:340", 'Expected to fail with Error'}) +end, {}, {"gambiarra_test.lua:350", 'Expected to fail with Error'}) -- -- except tests @@ -343,7 +357,7 @@ end, {}, {"gambiarra_test.lua:340", 'Expected to fail with Error'}) metatest('error should panic', function() error("lua error") ok(true, 'unreachable code') -end, {}, {}, {'gambiarra_test.lua:348: lua error'}) +end, {}, {}, {'gambiarra_test.lua:358: lua error'}) -- -- called function except @@ -356,7 +370,7 @@ end metatest('subroutine error should panic', function() subfunc() ok(true, 'unreachable code') -end, {}, {}, {'gambiarra_test.lua:357: lua error'}) +end, {}, {}, {'gambiarra_test.lua:367: lua error'}) --drain_post_queue() @@ -381,6 +395,7 @@ end, {'bar'}, {}, {}, true) metatest('async fail in main', function(next) ok(false, "async fail") + ok(true, 'unreachable code') end, {}, {'async fail'}, {}, true) --drain_post_queue() @@ -399,7 +414,7 @@ end, {'foo', 'bar'}, {}, {}, true) metatest('async except in main', function(next) error("async except") ok(true, 'unreachable code') -end, {}, {}, {'gambiarra_test.lua:404: async except'}, true) +end, {}, {}, {'gambiarra_test.lua:415: async except'}, true) metatest('async fail in callback', function(next) async(function() @@ -415,7 +430,7 @@ metatest('async except in callback', function(next) next() end) ok(true, 'foo') -end, {'foo'}, {}, {'gambiarra_test.lua:418: async Lua error'}, true) +end, {'foo'}, {}, {'gambiarra_test.lua:429: async Lua error'}, true) -- -- sync after async test @@ -434,6 +449,83 @@ metatest('check marker async', function() ok(eq(marker, "marked"), "marker check") end, {"marker check"}, {}) +-- +-- coroutine async tests +-- +metatest('coroutine pass', function(getCB, waitCb) + ok(true, "passing") +end, {"passing"}, {}, {}, "co") + +metatest('coroutine fail in main', function(getCB, waitCb) + ok(false, "coroutine fail") + ok(true, 'unreachable code') +end, {}, {'coroutine fail'}, {}, "co") + +metatest('coroutine fail in main', function(getCB, waitCb) + nok(true, "coroutine fail") + nok(false, 'unreachable code') +end, {}, {'coroutine fail'}, {}, "co") + +metatest('coroutine fail error', function(getCB, waitCb) + fail(function() error("my error") end, "my error", "Failed with correct error") + fail(function() error("my error") end, "other error", "Failed with other error") + ok(true, 'unreachable code') +end, {'Failed with correct error'}, {'Failed with other error', 'expected errormessage "gambiarra_test.lua:471: my error" to contain "other error"'}, {}, "co") + +metatest('coroutine except in main', function(getCB, waitCb) + error("coroutine except") + ok(true, 'unreachable code') +end, {}, {}, {'gambiarra_test.lua:476: coroutine except'}, "co") + +--local function coasync(f) table.insert(post_queue, 1, f) end +local function coasync(f, p1, p2) node.task.post(node.task.MEDIUM_PRIORITY, function() f(p1,p2) end) end + +metatest('coroutine with callback', function(getCB, waitCb) + coasync(getCB("testCb")) + name = waitCb() + ok(eq(name, "testCb"), "cb") +end, {"cb"}, {}, {}, "co") + +metatest('coroutine with callback with values', function(getCB, waitCb) + coasync(getCB("testCb"), "p1", 2) + name, p1, p2 = waitCb() + ok(eq(name, "testCb"), "cb") + ok(eq(p1, "p1"), "p1") + ok(eq(p2, 2), "p2") +end, {"cb", "p1", "p2"}, {}, {}, "co") + +metatest('coroutine fail after callback', function(getCB, waitCb) + coasync(getCB("testCb")) + name = waitCb() + ok(eq(name, "testCb"), "cb") + ok(false, "fail") + ok(true, 'unreachable code') +end, {"cb"}, {"fail"}, {}, "co") + +metatest('coroutine except after callback', function(getCB, waitCb) + coasync(getCB("testCb")) + name = waitCb() + ok(eq(name, "testCb"), "cb") + error("error") + ok(true, 'unreachable code') +end, {"cb"}, {}, {"gambiarra_test.lua:509: error"}, "co") + +--- detect stray callbacks after the test has finished +local strayCb +local function rewrap() + coasync(strayCb) +end + +metatest('leave stray callback', function(getCB, waitCb) + strayCb = getCB("testa") + coasync(rewrap) +end, {}, {}, {}, "co") + +metatest('test after stray cb', function(getCB, waitCb) +end, {}, {"Found stray Callback 'testa' from test 'leave stray callback'"}, {}, "co") + + + -- -- Finalize: check test results -- diff --git a/lua_tests/gambiarra/gambiarra_tmr.lua b/lua_tests/gambiarra/gambiarra_tmr.lua index 4b341654a6..748d0903e1 100644 --- a/lua_tests/gambiarra/gambiarra_tmr.lua +++ b/lua_tests/gambiarra/gambiarra_tmr.lua @@ -43,3 +43,44 @@ N.testasync('AUTO alarm', function(next) end) ok(true, "sync end") end) + +N.testco('SINGLE alarm coroutine', function(getCB, waitCB) + local t = tmr.create(); + t:alarm(200, tmr.ALARM_SINGLE, getCB("timer")) + + local name, timer = waitCB() + ok(eq("timer", name), "CB name matches") + + ok(true, "coroutine end") +end) + +N.testco('SEMI alarm coroutine', function(getCB, waitCB) + local t = tmr.create(); + t:alarm(200, tmr.ALARM_SEMI, getCB("timer")) + + local name, timer = waitCB() + ok(eq("timer", name), "CB name matches") + + timer:start() + + name, timer = waitCB() + ok(eq("timer", name), "CB name matches again") + + ok(true, "coroutine end") +end) + +N.testco('AUTO alarm coroutine', function(getCB, waitCB) + local t = tmr.create(); + t:alarm(200, tmr.ALARM_AUTO, getCB("timer")) + + local name, timer = waitCB() + ok(eq("timer", name), "CB name matches") + + name, timer = waitCB() + ok(eq("timer", name), "CB name matches again") + + timer:stop() + + ok(true, "coroutine end") +end) + From e313395664287f47b187adbb44fbb8658a7ef73b Mon Sep 17 00:00:00 2001 From: Gregor Hartmann Date: Tue, 6 Oct 2020 20:28:27 +0200 Subject: [PATCH 20/36] Delete mispec_file.lua as it is superseeded by gambiarra_file.lua --- lua_tests/mispec_file.lua | 177 -------------------------------------- 1 file changed, 177 deletions(-) delete mode 100644 lua_tests/mispec_file.lua diff --git a/lua_tests/mispec_file.lua b/lua_tests/mispec_file.lua deleted file mode 100644 index 651c1137c9..0000000000 --- a/lua_tests/mispec_file.lua +++ /dev/null @@ -1,177 +0,0 @@ -require 'mispec' - -describe('file', function(it) - --- it:initialize(function() end) - - it:cleanup(function() - file.remove("testfile") - file.remove("testfile2") - local testfiles = {"testfile1&", "testFILE2"} - for _, n in ipairs(testfiles) do - file.remove(n,n) - end - end) - - it:should('exist', function() - ko(file.exists("non existant file"), "non existant file") - - file.putcontents("testfile", "testcontents") - ok(file.exists("testfile")) - end) - - it:should('fscfg', function() - local start, size = file.fscfg() - ok(start, "start") - ok(size, "size") - end) - - it:should('fsinfo', function() - local remaining, used, total = file.fsinfo() - ok(remaining, "remaining") - ok(used, "used") - ok(total, "total") - ok(eq(remaining+used, total), "size maths") - end) - - it:should('getcontents', function() - local testcontent = "some content \0 and more" - file.putcontents("testfile", testcontent) - local content = file.getcontents("testfile") - ok(testcontent, content) - end) - - it:should('getcontents non existent file', function() - ko(file.getcontents("non existant file"), "non existent file") - end) - - it:should('getcontents more than 1K', function() - local f = file.open("testfile", "w") - local i - for i = 1,100 do - f:write("some text to test") - end - f:close() - content = file.getcontents("testfile") - eq(#content, 1700, "partial read") - end) - - it:should('list', function() - local files - - local function count(files) - local filecount = 0 - for k,v in pairs(files) do filecount = filecount+1 end - return filecount - end - - local function testfile(name) - ok(eq(files[name],#name)) - end - - local testfiles = {"testfile1&", "testFILE2"} - for _, n in ipairs(testfiles) do - file.putcontents(n,n) - end - - files = file.list("testfile%.*") - ok(eq(count(files), 1)) - testfile("testfile1&") - - files = file.list("^%l*%u+%d%.-") - ok(eq(count(files), 1)) - testfile("testFILE2") - - files = file.list() - ok(count(files) >= 2) - end) - - it:should('open non existing', function() - local function testopen(outcome, filename, mode) - outcome(file.open(filename, mode), mode) - file.close() - file.remove(filename) - end - - testopen(ko, "testfile", "r") - testopen(ok, "testfile", "w") - testopen(ok, "testfile", "a") - testopen(ko, "testfile", "r+") - testopen(ok, "testfile", "w+") - testopen(ok, "testfile", "a+") - - fail(file.open, "testfile", "x") -- shouldn't this fail? - end) - - it:should('open existing', function() - local function testopen(mode, position) - file.putcontents("testfile", "testfile") - ok(file.open("testfile", mode), mode) - file.write("") - ok(eq(file.seek(), position)) - file.close() - end - - testopen("r", 0) - testopen("w", 0) - testopen("a", 8) - testopen("r+", 0) - testopen("w+", 0) - testopen("a+", 8) - - fail(file.open, "testfile", "x") -- shouldn't this fail? - end) - - it:should('remove', function() - file.putcontents("testfile", "testfile") - - ok(file.remove("testfile") == nil, "existing file") - ok(file.remove("testfile") == nil, "non existing file") - end) - - it:should('rename', function() - file.putcontents("testfile", "testfile") - - ok(file.rename("testfile", "testfile2"), "rename existing") - ko(file.exists("testfile"), "old file removed") - ok(file.exists("testfile2"), "new file exists") - - ko(file.rename("testfile", "testfile3"), "rename non existing") - - file.putcontents("testfile", "testfile") - - ko(file.rename("testfile", "testfile2"), "rename to existing") - ok(file.exists("testfile"), "from file exists") - ok(file.exists("testfile2"), "to file exists") - end) - - it:should('stat existing file', function() - file.putcontents("testfile", "testfile") - - local stat = file.stat("testfile") - - ko(stat == nil, "stat existing") - ok(eq(stat.size, 8), "size") - ok(eq(stat.name, "testfile"), "name") - ko(stat.time == nil, "no time") - ok(eq(stat.time.year, 1970), "year") - ok(eq(stat.time.mon, 01), "mon") - ok(eq(stat.time.day, 01), "day") - ok(eq(stat.time.hour, 0), "hour") - ok(eq(stat.time.min, 0), "min") - ok(eq(stat.time.sec, 0), "sec") - ko(stat.is_dir, "is_dir") - ko(stat.is_rdonly, "is_rdonly") - ko(stat.is_hidden, "is_hidden") - ko(stat.is_sys, "is_sys") - ko(stat.is_arch, "is_arch") - end) - - it:should('stat non existing file', function() - local stat = file.stat("not existing file") - - ok(stat == nil, "stat empty") - end) -end) - -mispec.run() From 5c4cefda1e8aaf5aaffd80e976c798e0a9e9ccd5 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 26 Oct 2020 16:38:07 +0100 Subject: [PATCH 21/36] improve regexp for stack frame extraction --- lua_tests/gambiarra/gambiarra.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index f3261dacd3..711cac73ec 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -92,7 +92,7 @@ local function assertok(handler, name, invert, cond, msg) -- debug.getinfo() does not exist in NodeMCU -- msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline msg = debug.traceback() - msg = msg:match(".*\n\t*([^\n]*): in.-\n\t*.*in function 'pcall'") or msg:match(".*: in.-\n\t*.*in function '[n]?ok'\n\t*([^\n]*)\n") + msg = msg:match("([^\t]*): in[^\n]*\n\t[^\n]*in function '[ROM.]*pcall'") msg = msg:match(".-([^\\/]*)$") -- cut off path of filename end @@ -112,7 +112,7 @@ local function fail(handler, name, func, expected, msg) -- debug.getinfo() does not exist in NodeMCU -- msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline msg = debug.traceback() - msg = msg:match(".*\n\t*([^\n]*): in.-\n\t*.*in function 'pcall'") or msg:match(".*: in.-\n\t*.*in function 'fail'\n\t*([^\n]*)\n") + msg = msg:match("([^\t]*): in[^\n]*\n\t[^\n]*in function '[ROM.]*pcall'") msg = msg:match(".-([^\\/]*)$") -- cut off path of filename end local status, err = pcall(func) From 6b616d86e871a0a3aa781f55015674c0261323a3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 31 Oct 2020 05:14:40 +0100 Subject: [PATCH 22/36] Use Lua 53 debug capabilities --- lua_tests/gambiarra/gambiarra.lua | 34 ++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index 711cac73ec..406288acdc 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -82,6 +82,28 @@ local function spy(f) return s 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 + +node.heap = function() return 123 end + print(node.heap()) + collectgarbage() + print(node.heap()) + collectgarbage() + print("before debug.traceback()") + msg = debug.traceback() +-- file.putcontents("stack", msg) + print(node.heap(), "stack: ", msg) + msg = msg:match("([^\t]*): in[^\n]*\n\t[^\n]*in function '[ROM.]*pcall'") + print(node.heap(), "stack: ", msg) + msg = msg:match(".-([^\\/]*)$") -- cut off path of filename + print(node.heap(), "stack: ", msg) + return msg +end + local function assertok(handler, name, invert, cond, msg) local errormsg if type(cond) == 'table' and cond.msg then @@ -89,11 +111,7 @@ local function assertok(handler, name, invert, cond, msg) cond = false end if not msg then - -- debug.getinfo() does not exist in NodeMCU - -- msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline - msg = debug.traceback() - msg = msg:match("([^\t]*): in[^\n]*\n\t[^\n]*in function '[ROM.]*pcall'") - msg = msg:match(".-([^\\/]*)$") -- cut off path of filename + msg = getstackframe() end if invert then @@ -109,11 +127,7 @@ end local function fail(handler, name, func, expected, msg) if not msg then - -- debug.getinfo() does not exist in NodeMCU - -- msg = debug.getinfo(2, 'S').short_src..":"..debug.getinfo(2, 'l').currentline - msg = debug.traceback() - msg = msg:match("([^\t]*): in[^\n]*\n\t[^\n]*in function '[ROM.]*pcall'") - msg = msg:match(".-([^\\/]*)$") -- cut off path of filename + msg = getstackframe() end local status, err = pcall(func) if status then From 2248316d7ac1e631e36713ac74d47a6eff44ab16 Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 31 Oct 2020 05:15:45 +0100 Subject: [PATCH 23/36] move actual tests upfront --- lua_tests/gambiarra/gambiarra_test.lua | 781 +++++++++++++------------ 1 file changed, 399 insertions(+), 382 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index 5155dd8dd1..77d104db36 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -1,12 +1,403 @@ local N = require('gambiarra')("selftest") +local metatest +local async local expected = {} -local currentTest = {} - local failed, passed = 0,0 -local orig_node = node +local function load_tests() + -- + -- Basic tests + -- + metatest('simple assert passes', function() + ok(2 == 2, '2==2') + end, {'2==2'}, {}) + + metatest('simple negative assert fails', function() + nok(2 == 2, '2==2') + nok(false, 'unreachable code') + end, {}, {'2==2'}) + + metatest('simple assert fails', function() + ok(1 == 2, '1~=2') + ok(true, 'unreachable code') + end, {}, {'1~=2'}) + + metatest('simple negative assert passes', function() + nok(1 == 2, '1~=2') + end, {'1~=2'}, {}) + + metatest('ok without a message', function() + ok(1 == 1) + ok(1 == 2) + end, {'gambiarra_test.lua:31'}, {'gambiarra_test.lua:32'}) + + metatest('nok without a message', function() + nok(1 == "") + nok(1 == 1) + end, {'gambiarra_test.lua:36'}, {'gambiarra_test.lua:37'}) + + -- + -- Equality tests + -- + metatest('eq nil', function() + ok(eq(nil, nil), 'nil == nil') + nok(eq("", nil), '"" != nil') + nok(eq(nil, ""), 'nil != ""') + end, {'nil == nil', '"" != nil', 'nil != ""'}, {}) + + metatest('eq primitives', function() + ok(eq('foo', 'foo'), 'str == str') + nok(eq('foo', 'bar'), 'str != str') + ok(eq(123, 123), 'num == num') + nok(eq(123, 12), 'num != num') + end, {'str == str', 'str != str', 'num == num', 'num != num'}, {}) + + metatest('eq arrays', function() + ok(eq({}, {}), 'empty') + ok(eq({1, 2}, {1, 2}), 'equal') + nok(eq({1, 2}, {2, 1}), 'swapped') + nok(eq({1, 2, 3}, {1, 2}), 'longer') + nok(eq({1, 2}, {1, 2, 3}), 'shorter') + end, {'empty', 'equal', 'swapped', 'longer', 'shorter'}, {}) + + metatest('eq objects', function() + ok(eq({}, {}), 'empty') + ok(eq({a=1,b=2}, {a=1,b=2}), 'equal') + ok(eq({b=2,a=1}, {a=1,b=2}), 'swapped') + nok(eq({a=1,b=2}, {a=1,b=3}), 'not equal') + end, {'empty', 'equal', 'swapped', 'not equal'}, {}) + + metatest('eq nested objects', function() + ok(eq({ + ['1'] = { name = 'mhc', age = 28 }, + ['2'] = { name = 'arb', age = 26 } + }, { + ['1'] = { name = 'mhc', age = 28 }, + ['2'] = { name = 'arb', age = 26 } + }), 'equal') + ok(eq({ + ['1'] = { name = 'mhc', age = 28 }, + ['2'] = { name = 'arb', age = 26 } + }, { + ['1'] = { name = 'mhc', age = 28 }, + ['2'] = { name = 'arb', age = 27 } + }), 'not equal') + end, {'equal'}, {'not equal', 'different numbers expected 26 vs. 27'}) + + metatest('eq functions', function() + ok(eq(function(x) return x end, function(x) return x end), 'equal') + nok(eq(function(z) return x end, function(z) return y end), 'wrong variable') + nok(eq(function(x) return x end, function(x) return x+2 end), 'wrong code') + end, {'equal', 'wrong variable', 'wrong code'}, {}) + + metatest('eq different types', function() + local eqos = eq({a=1,b=2}, "text") + ok(eq(eqos.msg, "type 1 is table, type 2 is string"), 'object/string') + local eqfn = eq(function(x) return x end, 12) + ok(eq(eqfn.msg, "type 1 is function, type 2 is number"), 'function/int') + nok(eq(12, "Hallo"), 'int/string') + end, {"object/string", "function/int", 'int/string'}, {}) + + -- + -- Spies + -- + metatest('spy called', function() + local f = spy() + ok(not f.called or #f.called == 0, 'not called') + f() + ok(f.called, 'called') + ok(#f.called == 1, 'called once') + f() + ok(#f.called == 2, 'called twice') + end, {'not called', 'called', 'called once', 'called twice'}, {}) + + metatest('spy with arguments', function() + local x = 0 + local function setX(n) x = n end + local f = spy(setX) + f(1) + ok(x == 1, 'x == 1') + ok(eq(f.called, {{1}}), 'called with 1') + f(42) + ok(x == 42, 'x == 42') + ok(eq(f.called, {{1}, {42}}), 'called with 42') + end, {'x == 1', 'called with 1', 'x == 42', 'called with 42'}, {}) + + metatest('spy with nils', function() + local function nils(a, dummy, b) return a, nil, b, nil end + local f = spy(nils) + local r1, r2, r3, r4 = f(1, nil, 2) + ok(eq(f.called, {{1, nil, 2}}), 'nil in args') + ok(r1 == 1 and r2 == nil and r3 == 2 and r4 == nil, 'nil in returns') + end, {'nil in args', 'nil in returns'}, {}) + + metatest('spy with exception', function() + local function throwSomething(s) + if s ~= 'nopanic' then error('panic: '..s) end + end + local f = spy(throwSomething) + f('nopanic') + ok(f.errors == nil, 'no errors yet') + f('foo') + ok(eq(f.called, {{'nopanic'}, {'foo'}}), 'args ok') + ok(f.errors[1] == nil and f.errors[2] ~= nil, 'thrown ok') + end, {'no errors yet', 'args ok', 'thrown ok'}, {}) + + metatest('another spy with exception', function() + local f = spy(function() local a = unknownVariable + 1 end) + f() + ok(f.errors[1], 'exception thrown') + end, {'exception thrown'}, {}) + + metatest('spy should return a value', function() + local f = spy(function() return 5 end) + ok(f() == 5, 'spy returns a value') + local g = spy() + ok(g() == nil, 'default spy returns undefined') + end, {'spy returns a value', 'default spy returns undefined'}, {}) + + -- + -- fail tests + -- + metatest('fail with correct errormessage', function() + fail(function() error("my error") end, "my error", "Failed with correct error") + ok(true, 'reachable code') + end, {'Failed with correct error', 'reachable code'}, {}) + + metatest('fail with incorrect errormessage', function() + fail(function() error("my error") end, "different error", "Failed with incorrect error") + ok(true, 'unreachable code') + end, {}, {'Failed with incorrect error', + 'expected errormessage "gambiarra_test.lua:169: my error" to contain "different error"'}) + + metatest('fail with incorrect errormessage default message', function() + fail(function() error("my error") end, "different error") + ok(true, 'unreachable code') + end, {}, {'gambiarra_test.lua:175', + 'expected errormessage "gambiarra_test.lua:175: my error" to contain "different error"'}) + + metatest('fail with not failing code', function() + fail(function() end, "my error", "did not fail") + ok(true, 'unreachable code') + end, {}, {"did not fail", 'Expected to fail with Error containing "my error"'}) + + metatest('fail with failing code', function() + fail(function() error("my error") end, nil, "Failed as expected") + ok(true, 'reachable code') + end, {'Failed as expected', 'reachable code'}, {}) + + metatest('fail with not failing code', function() + fail(function() end, nil , "did not fail") + ok(true, 'unreachable code') + end, {}, {"did not fail", 'Expected to fail with Error'}) + + metatest('fail with not failing code default message', function() + fail(function() end) + ok(true, 'unreachable code') + end, {}, {"gambiarra_test.lua:196", 'Expected to fail with Error'}) + + -- + -- except tests + -- + metatest('error should panic', function() + error("lua error") + ok(true, 'unreachable code') + end, {}, {}, {'gambiarra_test.lua:204: lua error'}) + + -- + -- called function except + -- + + local function subfuncerror() + error("lua error") + end + + metatest('subroutine error should panic', function() + subfuncerror() + ok(true, 'unreachable code') + end, {}, {}, {'gambiarra_test.lua:213: lua error'}) + + local function subfuncok() + ok(false) + end + + metatest('subroutine ok should fail', function() + subfuncok() + ok(true, 'unreachable code') + end, {}, {'gambiarra_test.lua:222'}) + + --drain_post_queue() + + -- + -- Async tests + -- + metatest('async test', function(next) + async(function() + ok(true, 'bar') + async(function() + ok(true, 'baz') + next() + end) + end) + ok(true, 'foo') + end, {'foo', 'bar', 'baz'}, {}, {}, true) + + metatest('async test without actually async', function(next) + ok(true, 'bar') + next() + end, {'bar'}, {}, {}, true) + + metatest('async fail in main', function(next) + ok(false, "async fail") + ok(true, 'unreachable code') + end, {}, {'async fail'}, {}, true) + + --drain_post_queue() + + metatest('another async test after async queue drained', function(next) + async(function() + ok(true, 'bar') + next() + end) + ok(true, 'foo') + end, {'foo', 'bar'}, {}, {}, true) + + -- + -- except tests async + -- + metatest('async except in main', function(next) + error("async except") + ok(true, 'unreachable code') + end, {}, {}, {'gambiarra_test.lua:270: async except'}, true) + + metatest('async fail in callback', function(next) + async(function() + ok(false, "async fail") + next() + end) + ok(true, 'foo') + end, {'foo'}, {'async fail'}, {}, true) + + metatest('async except in callback', function(next) + async(function() + error("async Lua error") + next() + end) + ok(true, 'foo') + end, {'foo'}, {}, {'gambiarra_test.lua:284: async Lua error'}, true) + + -- + -- sync after async test + -- + local marker = 0 + metatest('set marker async', function(next) + async(function() + marker = "marked" + ok(true, 'async bar') + next() + end) + ok(true, 'foo') + end, {'foo', 'async bar'}, {}, {}, true) + + metatest('check marker async', function() + ok(eq(marker, "marked"), "marker check") + end, {"marker check"}, {}) + + -- + -- coroutine async tests + -- + metatest('coroutine pass', function(getCB, waitCb) + ok(true, "passing") + end, {"passing"}, {}, {}, "co") + + metatest('coroutine fail in main', function(getCB, waitCb) + ok(false, "coroutine fail") + ok(true, 'unreachable code') + end, {}, {'coroutine fail'}, {}, "co") + + metatest('coroutine fail in main', function(getCB, waitCb) + nok(true, "coroutine fail") + nok(false, 'unreachable code') + end, {}, {'coroutine fail'}, {}, "co") + + metatest('coroutine fail error', function(getCB, waitCb) + fail(function() error("my error") end, "my error", "Failed with correct error") + fail(function() error("my error") end, "other error", "Failed with other error") + ok(true, 'unreachable code') + end, {'Failed with correct error'}, {'Failed with other error', 'expected errormessage "gambiarra_test.lua:326: my error" to contain "other error"'}, {}, "co") + + metatest('coroutine except in main', function(getCB, waitCb) + error("coroutine except") + ok(true, 'unreachable code') + end, {}, {}, {'gambiarra_test.lua:331: coroutine except'}, "co") + + --local function coasync(f) table.insert(post_queue, 1, f) end + local function coasync(f, p1, p2) node.task.post(node.task.MEDIUM_PRIORITY, function() f(p1,p2) end) end + + metatest('coroutine with callback', function(getCB, waitCb) + coasync(getCB("testCb")) + name = waitCb() + ok(eq(name, "testCb"), "cb") + end, {"cb"}, {}, {}, "co") + + metatest('coroutine with callback with values', function(getCB, waitCb) + coasync(getCB("testCb"), "p1", 2) + name, p1, p2 = waitCb() + ok(eq(name, "testCb"), "cb") + ok(eq(p1, "p1"), "p1") + ok(eq(p2, 2), "p2") + end, {"cb", "p1", "p2"}, {}, {}, "co") + + metatest('coroutine fail after callback', function(getCB, waitCb) + coasync(getCB("testCb")) + name = waitCb() + ok(eq(name, "testCb"), "cb") + ok(false, "fail") + ok(true, 'unreachable code') + end, {"cb"}, {"fail"}, {}, "co") + + metatest('coroutine except after callback', function(getCB, waitCb) + coasync(getCB("testCb")) + name = waitCb() + ok(eq(name, "testCb"), "cb") + error("error") + ok(true, 'unreachable code') + end, {"cb"}, {}, {"gambiarra_test.lua:364: error"}, "co") + + --- detect stray callbacks after the test has finished + local strayCb + local function rewrap() + coasync(strayCb) + end + + metatest('leave stray callback', function(getCB, waitCb) + strayCb = getCB("testa") + coasync(rewrap) + end, {}, {}, {}, "co") + + metatest('test after stray cb', function(getCB, waitCb) + end, {}, {"Found stray Callback 'testa' from test 'leave stray callback'"}, {}, "co") + + + -- + -- Finalize: check test results + -- + metatest("finishing up pending tests", function() + for i = 1,#expected -1 do + print("--- FAIL "..expected[i].name..' (pending)') + failed = failed + 1 + end + print("failed: "..failed, "passed: "..passed) + node = orig_node + end, {}, {}) + +end -- load_tests() + + +local currentTest = {} +local orig_node = node local cbWrap = function(cb) return cb end -- implement pseudo task handling for on host testing @@ -73,10 +464,11 @@ end -- Set meta test handler N.report(function(e, test, msg, errormsg) + local pass = true if e == 'begin' then currentTest.name = test + pass = true elseif e == 'end' then - local pass = true if not comparetables(expected[1].pass, currentTest.pass) then print("--- FAIL "..expected[1].name..' (passed): expected ['.. stringify(expected[1].pass)..'] vs ['.. @@ -123,7 +515,7 @@ N.report(function(e, test, msg, errormsg) end) local async_queue = {} -local function async(f) table.insert(async_queue, cbWrap(f)) end +async = function(f) table.insert(async_queue, cbWrap(f)) end local function async_next() local f = table.remove(async_queue, 1) if f then @@ -137,7 +529,7 @@ local function drain_async_queue() end end -local function metatest(name, f, expectedPassed, expectedFailed, expectedExcept, async) +metatest = function(name, f, expectedPassed, expectedFailed, expectedExcept, async) local ff = f if async then ff = function(...) @@ -160,382 +552,7 @@ local function metatest(name, f, expectedPassed, expectedFailed, expectedExcept, }) end --- --- Basic tests --- -metatest('simple assert passes', function() - ok(2 == 2, '2==2') -end, {'2==2'}, {}) - -metatest('simple negative assert fails', function() - nok(2 == 2, '2==2') - nok(false, 'unreachable code') -end, {}, {'2==2'}) - -metatest('simple assert fails', function() - ok(1 == 2, '1~=2') - ok(true, 'unreachable code') -end, {}, {'1~=2'}) - -metatest('simple negative assert passe', function() - nok(1 == 2, '1~=2') -end, {'1~=2'}, {}) - -metatest('ok without a message', function() - ok(1 == 1) - ok(1 == 2) -end, {'gambiarra_test.lua:185'}, {'gambiarra_test.lua:186'}) - -metatest('nok without a message', function() - nok(1 == "") - nok(1 == 1) -end, {'gambiarra_test.lua:190'}, {'gambiarra_test.lua:191'}) - --- --- Equality tests --- -metatest('eq nil', function() - ok(eq(nil, nil), 'nil == nil') - nok(eq("", nil), '"" != nil') - nok(eq(nil, ""), 'nil != ""') -end, {'nil == nil', '"" != nil', 'nil != ""'}, {}) - -metatest('eq primitives', function() - ok(eq('foo', 'foo'), 'str == str') - nok(eq('foo', 'bar'), 'str != str') - ok(eq(123, 123), 'num == num') - nok(eq(123, 12), 'num != num') -end, {'str == str', 'str != str', 'num == num', 'num != num'}, {}) - -metatest('eq arrays', function() - ok(eq({}, {}), 'empty') - ok(eq({1, 2}, {1, 2}), 'equal') - nok(eq({1, 2}, {2, 1}), 'swapped') - nok(eq({1, 2, 3}, {1, 2}), 'longer') - nok(eq({1, 2}, {1, 2, 3}), 'shorter') -end, {'empty', 'equal', 'swapped', 'longer', 'shorter'}, {}) - -metatest('eq objects', function() - ok(eq({}, {}), 'empty') - ok(eq({a=1,b=2}, {a=1,b=2}), 'equal') - ok(eq({b=2,a=1}, {a=1,b=2}), 'swapped') - nok(eq({a=1,b=2}, {a=1,b=3}), 'not equal') -end, {'empty', 'equal', 'swapped', 'not equal'}, {}) - -metatest('eq nested objects', function() - ok(eq({ - ['1'] = { name = 'mhc', age = 28 }, - ['2'] = { name = 'arb', age = 26 } - }, { - ['1'] = { name = 'mhc', age = 28 }, - ['2'] = { name = 'arb', age = 26 } - }), 'equal') - ok(eq({ - ['1'] = { name = 'mhc', age = 28 }, - ['2'] = { name = 'arb', age = 26 } - }, { - ['1'] = { name = 'mhc', age = 28 }, - ['2'] = { name = 'arb', age = 27 } - }), 'not equal') -end, {'equal'}, {'not equal', 'different numbers expected 26 vs. 27'}) - -metatest('eq functions', function() - ok(eq(function(x) return x end, function(x) return x end), 'equal') - nok(eq(function(z) return x end, function(z) return y end), 'wrong variable') - nok(eq(function(x) return x end, function(x) return x+2 end), 'wrong code') -end, {'equal', 'wrong variable', 'wrong code'}, {}) - -metatest('eq different types', function() - local eqos = eq({a=1,b=2}, "text") - ok(eq(eqos.msg, "type 1 is table, type 2 is string"), 'object/string') - local eqfn = eq(function(x) return x end, 12) - ok(eq(eqfn.msg, "type 1 is function, type 2 is number"), 'function/int') - nok(eq(12, "Hallo"), 'int/string') -end, {"object/string", "function/int", 'int/string'}, {}) - --- --- Spies --- -metatest('spy called', function() - local f = spy() - ok(not f.called or #f.called == 0, 'not called') - f() - ok(f.called, 'called') - ok(#f.called == 1, 'called once') - f() - ok(#f.called == 2, 'called twice') -end, {'not called', 'called', 'called once', 'called twice'}, {}) - -metatest('spy with arguments', function() - local x = 0 - local function setX(n) x = n end - local f = spy(setX) - f(1) - ok(x == 1, 'x == 1') - ok(eq(f.called, {{1}}), 'called with 1') - f(42) - ok(x == 42, 'x == 42') - ok(eq(f.called, {{1}, {42}}), 'called with 42') -end, {'x == 1', 'called with 1', 'x == 42', 'called with 42'}, {}) - -metatest('spy with nils', function() - local function nils(a, dummy, b) return a, nil, b, nil end - local f = spy(nils) - local r1, r2, r3, r4 = f(1, nil, 2) - ok(eq(f.called, {{1, nil, 2}}), 'nil in args') - ok(r1 == 1 and r2 == nil and r3 == 2 and r4 == nil, 'nil in returns') -end, {'nil in args', 'nil in returns'}, {}) - -metatest('spy with exception', function() - local function throwSomething(s) - if s ~= 'nopanic' then error('panic: '..s) end - end - local f = spy(throwSomething) - f('nopanic') - ok(f.errors == nil, 'no errors yet') - f('foo') - ok(eq(f.called, {{'nopanic'}, {'foo'}}), 'args ok') - ok(f.errors[1] == nil and f.errors[2] ~= nil, 'thrown ok') -end, {'no errors yet', 'args ok', 'thrown ok'}, {}) - -metatest('another spy with exception', function() - local f = spy(function() local a = unknownVariable + 1 end) - f() - ok(f.errors[1], 'exception thrown') -end, {'exception thrown'}, {}) - -metatest('spy should return a value', function() - local f = spy(function() return 5 end) - ok(f() == 5, 'spy returns a value') - local g = spy() - ok(g() == nil, 'default spy returns undefined') -end, {'spy returns a value', 'default spy returns undefined'}, {}) - --- --- fail tests --- -metatest('fail with correct errormessage', function() - fail(function() error("my error") end, "my error", "Failed with correct error") - ok(true, 'reachable code') -end, {'Failed with correct error', 'reachable code'}, {}) - -metatest('fail with incorrect errormessage', function() - fail(function() error("my error") end, "different error", "Failed with incorrect error") - ok(true, 'unreachable code') -end, {}, {'Failed with incorrect error', - 'expected errormessage "gambiarra_test.lua:323: my error" to contain "different error"'}) - -metatest('fail with incorrect errormessage default message', function() - fail(function() error("my error") end, "different error") - ok(true, 'unreachable code') -end, {}, {'gambiarra_test.lua:329', - 'expected errormessage "gambiarra_test.lua:329: my error" to contain "different error"'}) - -metatest('fail with not failing code', function() - fail(function() end, "my error", "did not fail") - ok(true, 'unreachable code') -end, {}, {"did not fail", 'Expected to fail with Error containing "my error"'}) - -metatest('fail with failing code', function() - fail(function() error("my error") end, nil, "Failed as expected") - ok(true, 'reachable code') -end, {'Failed as expected', 'reachable code'}, {}) - -metatest('fail with not failing code', function() - fail(function() end, nil , "did not fail") - ok(true, 'unreachable code') -end, {}, {"did not fail", 'Expected to fail with Error'}) - -metatest('fail with not failing code default message', function() - fail(function() end) - ok(true, 'unreachable code') -end, {}, {"gambiarra_test.lua:350", 'Expected to fail with Error'}) - --- --- except tests --- -metatest('error should panic', function() - error("lua error") - ok(true, 'unreachable code') -end, {}, {}, {'gambiarra_test.lua:358: lua error'}) - --- --- called function except --- - -local function subfunc() - error("lua error") -end - -metatest('subroutine error should panic', function() - subfunc() - ok(true, 'unreachable code') -end, {}, {}, {'gambiarra_test.lua:367: lua error'}) - ---drain_post_queue() - --- --- Async tests --- -metatest('async test', function(next) - async(function() - ok(true, 'bar') - async(function() - ok(true, 'baz') - next() - end) - end) - ok(true, 'foo') -end, {'foo', 'bar', 'baz'}, {}, {}, true) -metatest('async test without actually async', function(next) - ok(true, 'bar') - next() -end, {'bar'}, {}, {}, true) - -metatest('async fail in main', function(next) - ok(false, "async fail") - ok(true, 'unreachable code') -end, {}, {'async fail'}, {}, true) - ---drain_post_queue() - -metatest('another async test after async queue drained', function(next) - async(function() - ok(true, 'bar') - next() - end) - ok(true, 'foo') -end, {'foo', 'bar'}, {}, {}, true) - --- --- except tests async --- -metatest('async except in main', function(next) - error("async except") - ok(true, 'unreachable code') -end, {}, {}, {'gambiarra_test.lua:415: async except'}, true) - -metatest('async fail in callback', function(next) - async(function() - ok(false, "async fail") - next() - end) - ok(true, 'foo') -end, {'foo'}, {'async fail'}, {}, true) - -metatest('async except in callback', function(next) - async(function() - error("async Lua error") - next() - end) - ok(true, 'foo') -end, {'foo'}, {}, {'gambiarra_test.lua:429: async Lua error'}, true) - --- --- sync after async test --- -local marker = 0 -metatest('set marker async', function(next) - async(function() - marker = "marked" - ok(true, 'async bar') - next() - end) - ok(true, 'foo') -end, {'foo', 'async bar'}, {}, {}, true) - -metatest('check marker async', function() - ok(eq(marker, "marked"), "marker check") -end, {"marker check"}, {}) - --- --- coroutine async tests --- -metatest('coroutine pass', function(getCB, waitCb) - ok(true, "passing") -end, {"passing"}, {}, {}, "co") - -metatest('coroutine fail in main', function(getCB, waitCb) - ok(false, "coroutine fail") - ok(true, 'unreachable code') -end, {}, {'coroutine fail'}, {}, "co") - -metatest('coroutine fail in main', function(getCB, waitCb) - nok(true, "coroutine fail") - nok(false, 'unreachable code') -end, {}, {'coroutine fail'}, {}, "co") - -metatest('coroutine fail error', function(getCB, waitCb) - fail(function() error("my error") end, "my error", "Failed with correct error") - fail(function() error("my error") end, "other error", "Failed with other error") - ok(true, 'unreachable code') -end, {'Failed with correct error'}, {'Failed with other error', 'expected errormessage "gambiarra_test.lua:471: my error" to contain "other error"'}, {}, "co") - -metatest('coroutine except in main', function(getCB, waitCb) - error("coroutine except") - ok(true, 'unreachable code') -end, {}, {}, {'gambiarra_test.lua:476: coroutine except'}, "co") - ---local function coasync(f) table.insert(post_queue, 1, f) end -local function coasync(f, p1, p2) node.task.post(node.task.MEDIUM_PRIORITY, function() f(p1,p2) end) end - -metatest('coroutine with callback', function(getCB, waitCb) - coasync(getCB("testCb")) - name = waitCb() - ok(eq(name, "testCb"), "cb") -end, {"cb"}, {}, {}, "co") - -metatest('coroutine with callback with values', function(getCB, waitCb) - coasync(getCB("testCb"), "p1", 2) - name, p1, p2 = waitCb() - ok(eq(name, "testCb"), "cb") - ok(eq(p1, "p1"), "p1") - ok(eq(p2, 2), "p2") -end, {"cb", "p1", "p2"}, {}, {}, "co") - -metatest('coroutine fail after callback', function(getCB, waitCb) - coasync(getCB("testCb")) - name = waitCb() - ok(eq(name, "testCb"), "cb") - ok(false, "fail") - ok(true, 'unreachable code') -end, {"cb"}, {"fail"}, {}, "co") - -metatest('coroutine except after callback', function(getCB, waitCb) - coasync(getCB("testCb")) - name = waitCb() - ok(eq(name, "testCb"), "cb") - error("error") - ok(true, 'unreachable code') -end, {"cb"}, {}, {"gambiarra_test.lua:509: error"}, "co") - ---- detect stray callbacks after the test has finished -local strayCb -local function rewrap() - coasync(strayCb) -end - -metatest('leave stray callback', function(getCB, waitCb) - strayCb = getCB("testa") - coasync(rewrap) -end, {}, {}, {}, "co") - -metatest('test after stray cb', function(getCB, waitCb) -end, {}, {"Found stray Callback 'testa' from test 'leave stray callback'"}, {}, "co") - - - --- --- Finalize: check test results --- -metatest("finishing up pending tests", function() - for i = 1,#expected -1 do - print("--- FAIL "..expected[i].name..' (pending)') - failed = failed + 1 - end - print("failed: "..failed, "passed: "..passed) - node = orig_node -end, {}, {}) +load_tests() drain_post_queue() From d3fc6e1126106fb09b02e2fdd6f33609a16f03af Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 31 Oct 2020 19:16:06 +0100 Subject: [PATCH 24/36] remove debug code + optimization --- lua_tests/gambiarra/gambiarra.lua | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index 406288acdc..0583b35699 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -88,24 +88,15 @@ local function getstackframe() return debug.getinfo(5, 'S').short_src:match("([^\\/]*)$")..":"..debug.getinfo(5, 'l').currentline end -node.heap = function() return 123 end - print(node.heap()) - collectgarbage() - print(node.heap()) - collectgarbage() - print("before debug.traceback()") msg = debug.traceback() --- file.putcontents("stack", msg) - print(node.heap(), "stack: ", msg) - msg = msg:match("([^\t]*): in[^\n]*\n\t[^\n]*in function '[ROM.]*pcall'") - print(node.heap(), "stack: ", msg) + 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 - print(node.heap(), "stack: ", msg) 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 @@ -126,16 +117,16 @@ local function assertok(handler, name, invert, cond, msg) end local function fail(handler, name, func, expected, msg) + local status, err = pcall(func) if not msg then msg = getstackframe() end - local status, err = pcall(func) if status then - local messagePart = "" + local messageParts = {"Expected to fail with Error"} if expected then - messagePart = " containing \"" .. expected .. "\"" + messageParts[2] = " containing \"" .. expected .. "\"" end - handler('fail', name, msg, "Expected to fail with Error" .. messagePart) + handler('fail', name, msg, table.concat(messageParts, "")) error('_*_TestAbort_*_') end if (expected and not string.find(err, expected)) then From 42af25437960c2a48f854871fbafdf3fa2ccbea1 Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 31 Oct 2020 19:17:34 +0100 Subject: [PATCH 25/36] Show errors immediately instead of at the end of the test, freeing memory earlier --- lua_tests/gambiarra/gambiarra_test.lua | 59 ++++++++++++++------------ 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index 77d104db36..12f0d6e488 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -462,31 +462,41 @@ local function comparetables(t1, t2) return true end +local pass -- Set meta test handler N.report(function(e, test, msg, errormsg) - local pass = true - if e == 'begin' then - currentTest.name = test - pass = true - elseif e == 'end' then - if not comparetables(expected[1].pass, currentTest.pass) then - print("--- FAIL "..expected[1].name..' (passed): expected ['.. - stringify(expected[1].pass)..'] vs ['.. - stringify(currentTest.pass)..']') + local function consumemsg(msg, area) + if not expected[1][area][1] then + print("--- FAIL "..expected[1].name..' ('..area..'ed): unexpected "'.. + msg..'"') pass = false + return end - if not comparetables(expected[1].fail, currentTest.fail) then - print("--- FAIL "..expected[1].name..' (failed): expected ['.. - stringify(expected[1].fail)..'] vs ['.. - stringify(currentTest.fail)..']') + + if msg ~= expected[1][area][1] then + print("--- FAIL "..expected[1].name..' ('..area..'ed): expected "'.. + expected[1][area][1]..'" vs "'.. + msg..'"') pass = false end - if not comparetables(expected[1].except, currentTest.except) then - print("--- FAIL "..expected[1].name..' (failed): expected ['.. - stringify(expected[1].except)..'] vs ['.. - stringify(currentTest.except)..']') + table.remove(expected[1][area], 1) + end + + local function consumeremainder(area) + if #expected[1][area] > 0 then + print("--- FAIL "..expected[1].name..' ('..area..'ed): expected ['.. + stringify(expected[1][area])..']') pass = false end + end + + if e == 'begin' then + currentTest.name = test + pass = true + elseif e == 'end' then + consumeremainder("pass") + consumeremainder("fail") + consumeremainder("except") if pass then print("+++ Pass "..expected[1].name) passed = passed + 1 @@ -496,17 +506,14 @@ N.report(function(e, test, msg, errormsg) currentTest = {} table.remove(expected, 1) elseif e == 'pass' then - currentTest.pass = currentTest.pass or {} - table.insert(currentTest.pass, msg) - if errormsg then table.insert(currentTest.pass, errormsg) end + consumemsg(msg, "pass") + if errormsg then consumemsg(errormsg, "pass") end elseif e == 'fail' then - currentTest.fail = currentTest.fail or {} - table.insert(currentTest.fail, msg) - if errormsg then table.insert(currentTest.fail, errormsg) end + consumemsg(msg, "fail") + if errormsg then consumemsg(errormsg, "fail") end elseif e == 'except' then - currentTest.except = currentTest.except or {} - table.insert(currentTest.except, msg) - if errormsg then table.insert(currentTest.except, errormsg) end + consumemsg(msg, "except") + if errormsg then consumemsg(errormsg, "except") end elseif e == 'start' or e == 'finish' then -- ignore else From 832fc86e71fc60b84f0604b4c4ab0c3813381434 Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 31 Oct 2020 19:39:55 +0100 Subject: [PATCH 26/36] Split tests to be run in 2 tranches --- lua_tests/gambiarra/gambiarra.lua | 1 + lua_tests/gambiarra/gambiarra_test.lua | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/lua_tests/gambiarra/gambiarra.lua b/lua_tests/gambiarra/gambiarra.lua index 0583b35699..d458c71dc3 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/lua_tests/gambiarra/gambiarra.lua @@ -183,6 +183,7 @@ local function NTest(testrunname, failoldinterface) copyenv(env, prev) outputhandler('end', name) table.remove(pendingtests, 1) + if collectgarbage then collectgarbage() end if next then next() end end diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/lua_tests/gambiarra/gambiarra_test.lua index 12f0d6e488..c830bd0087 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/lua_tests/gambiarra/gambiarra_test.lua @@ -4,7 +4,7 @@ local metatest local async local expected = {} local failed, passed = 0,0 - +local load_tests2 local function load_tests() -- -- Basic tests @@ -197,13 +197,20 @@ local function load_tests() ok(true, 'unreachable code') end, {}, {"gambiarra_test.lua:196", 'Expected to fail with Error'}) + metatest('=== load more tests ===', function() + load_tests2() + end, {}, {}) + +end + +load_tests2 = function() -- -- except tests -- metatest('error should panic', function() error("lua error") ok(true, 'unreachable code') - end, {}, {}, {'gambiarra_test.lua:204: lua error'}) + end, {}, {}, {'gambiarra_test.lua:211: lua error'}) -- -- called function except @@ -216,7 +223,7 @@ local function load_tests() metatest('subroutine error should panic', function() subfuncerror() ok(true, 'unreachable code') - end, {}, {}, {'gambiarra_test.lua:213: lua error'}) + end, {}, {}, {'gambiarra_test.lua:220: lua error'}) local function subfuncok() ok(false) @@ -225,7 +232,7 @@ local function load_tests() metatest('subroutine ok should fail', function() subfuncok() ok(true, 'unreachable code') - end, {}, {'gambiarra_test.lua:222'}) + end, {}, {'gambiarra_test.lua:229'}) --drain_post_queue() @@ -269,7 +276,7 @@ local function load_tests() metatest('async except in main', function(next) error("async except") ok(true, 'unreachable code') - end, {}, {}, {'gambiarra_test.lua:270: async except'}, true) + end, {}, {}, {'gambiarra_test.lua:277: async except'}, true) metatest('async fail in callback', function(next) async(function() @@ -285,7 +292,7 @@ local function load_tests() next() end) ok(true, 'foo') - end, {'foo'}, {}, {'gambiarra_test.lua:284: async Lua error'}, true) + end, {'foo'}, {}, {'gambiarra_test.lua:291: async Lua error'}, true) -- -- sync after async test @@ -325,12 +332,12 @@ local function load_tests() fail(function() error("my error") end, "my error", "Failed with correct error") fail(function() error("my error") end, "other error", "Failed with other error") ok(true, 'unreachable code') - end, {'Failed with correct error'}, {'Failed with other error', 'expected errormessage "gambiarra_test.lua:326: my error" to contain "other error"'}, {}, "co") + end, {'Failed with correct error'}, {'Failed with other error', 'expected errormessage "gambiarra_test.lua:333: my error" to contain "other error"'}, {}, "co") metatest('coroutine except in main', function(getCB, waitCb) error("coroutine except") ok(true, 'unreachable code') - end, {}, {}, {'gambiarra_test.lua:331: coroutine except'}, "co") + end, {}, {}, {'gambiarra_test.lua:338: coroutine except'}, "co") --local function coasync(f) table.insert(post_queue, 1, f) end local function coasync(f, p1, p2) node.task.post(node.task.MEDIUM_PRIORITY, function() f(p1,p2) end) end @@ -363,7 +370,7 @@ local function load_tests() ok(eq(name, "testCb"), "cb") error("error") ok(true, 'unreachable code') - end, {"cb"}, {}, {"gambiarra_test.lua:364: error"}, "co") + end, {"cb"}, {}, {"gambiarra_test.lua:371: error"}, "co") --- detect stray callbacks after the test has finished local strayCb From fdbfe105dd03e4ee3d94e0524b2829de5648b546 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 2 Nov 2020 16:43:52 +0100 Subject: [PATCH 27/36] rename to NTest and move to new location --- .../gambiarra.lua => tests/NTest/NTest.lua | 2 +- .../README.md => tests/NTest/NTest.md | 65 +++++++------------ .../NTest/NTest_test.lua | 30 ++++----- .../NTest/tests/file.lua | 2 +- .../NTest/tests/tmr.lua | 2 +- 5 files changed, 42 insertions(+), 59 deletions(-) rename lua_tests/gambiarra/gambiarra.lua => tests/NTest/NTest.lua (99%) rename lua_tests/gambiarra/README.md => tests/NTest/NTest.md (66%) rename lua_tests/gambiarra/gambiarra_test.lua => tests/NTest/NTest_test.lua (93%) rename lua_tests/gambiarra/gambiarra_file.lua => tests/NTest/tests/file.lua (99%) rename lua_tests/gambiarra/gambiarra_tmr.lua => tests/NTest/tests/tmr.lua (97%) diff --git a/lua_tests/gambiarra/gambiarra.lua b/tests/NTest/NTest.lua similarity index 99% rename from lua_tests/gambiarra/gambiarra.lua rename to tests/NTest/NTest.lua index d458c71dc3..bfe6cbf6e3 100644 --- a/lua_tests/gambiarra/gambiarra.lua +++ b/tests/NTest/NTest.lua @@ -183,7 +183,7 @@ local function NTest(testrunname, failoldinterface) copyenv(env, prev) outputhandler('end', name) table.remove(pendingtests, 1) - if collectgarbage then collectgarbage() end + collectgarbage() if next then next() end end diff --git a/lua_tests/gambiarra/README.md b/tests/NTest/NTest.md similarity index 66% rename from lua_tests/gambiarra/README.md rename to tests/NTest/NTest.md index d04d275c76..98b11fed9a 100644 --- a/lua_tests/gambiarra/README.md +++ b/tests/NTest/NTest.md @@ -1,21 +1,18 @@ -# Gambiarra +# NTest +| Since | Origin / Contributor | Maintainer | Source | +| :----- | :-------------------- | :---------- | :------ | +| 2020-11-01 | [Gregor Hartmann](https://github.com/HHHartmann) | [Gregor Hartmann](https://github.com/HHHartmann) | [NTest.lua](NTest.lua) | -Gambiarra is a Lua version of Kludjs, and follows the idea of ultimately -minimal unit testing. +NTest is a test system for NodeMCU which is originally based on gambiarra. It is designed to run on chip but also runs on the host using luac.cross. -## Install - -Get the sources: - -https://github.com/nodemcu/nodemcu-firmware/blob/release/lua_tests/gambiarra/gambiarra.lua - -NOTE: you will have to use LFS to run this as it is too big to fit in memory. +!!! attention + You will have to use LFS to run this as it is too big to fit in memory. ## Example ``` Lua -- Simple synchronous test -local tests = require("gambiarra")("first testrun") +local tests = require("NTest")("first testrun") tests.test('Check dogma', function() ok(2+2 == 5, 'two plus two equals five') @@ -39,16 +36,16 @@ end) ## API -`require('gambiarra')` returns an new function which must be called with a string. +`require('NTest')` returns an new function which must be called with a string. ``` Lua - local new = require('gambiarra') +local new = require('NTest') ``` `new(testrunname:string)` returns an object with the following functions: ``` Lua - local tests = new("first testrun") +local tests = new("first testrun") ``` `test(name:string, f:function)` allows you to define a new test: @@ -59,7 +56,7 @@ end) ``` `testasync(name:string, f:function(done:function))` allows you to define a new asynchronous test: -To tell gambuarra that the test is finished you need to call the function `done` which gets passed in. +To tell NTest that the test is finished you need to call the function `done` which gets passed in. In async scenarios the test function will usually terminate quickly but the test is still waiting for some callback to be called before it is finished. @@ -85,7 +82,7 @@ tests.testco('My coroutine test with callback', function(getCB, waitCB) ok(eq(name, "timer"), "CB name matches") name, tCB = waitCB() - ok(eq("timer", name), "CB name matches") + ok(eq(name, "timer"), "CB name matches again") tCB:stop() @@ -94,15 +91,12 @@ end) ``` -All test functions also define some helper functions that are added when the test is -executed - `ok`, `nok`, `fail`, `eq` and `spy`. +All test functions also define some helper functions that are added when the test is executed - `ok`, `nok`, `fail`, `eq` and `spy`. `ok`, `nok`, `fail` are assert functions which will break the test if the condition is not met. -`ok(cond:bool, [msg:string])`. It takes any -boolean condition and an optional assertion message. If no message is defined - -current filename and line will be used. If the condition evaluetes to thuthy nothing happens. -If it is falsy the message is printed and the test is aborted and the next test is executed. +`ok(cond:bool, [msg:string])`. It takes any boolean condition and an optional assertion message. If no message is defined - current filename and line will be used. If the condition evaluetes to thuthy nothing happens. +If it is falsy the message is printed and the test is aborted. The next test will then be executed. ``` Lua ok(1 == 2) -- prints 'foo.lua:42' @@ -117,8 +111,7 @@ nok(1 == 1) -- prints 'foo.lua:42' nok(1 == 1, 'one equals one') -- prints 'one equals one' ``` -`fail(func:function, [expected:string], [msg:string])` tests a function for failure. If expected is given the errormessage poduced by the function must contain the given string else `fail` will fail the test. If no message is defined - -current filename and line will be used. +`fail(func:function, [expected:string], [msg:string])` tests a function for failure. If expected is given the errormessage poduced by the function must also contain the given string else `fail` will fail the test. If no message is defined the current filename and line will be used. ``` Lua local function f() error("my error") end @@ -127,10 +120,8 @@ fail(f, "expected error", "Failed with incorrect error") -- 'expected errormessage "foo.lua:2: my error" to contain "expected error"' ``` -`eq(a, b)` is a helper to deeply compare lua variables. It supports numbers, -strings, booleans, nils, functions and tables. It's mostly useful within ok() and nok(): -If the variables are equal it returns `true` -else it returns `{msg=''}` This is spechially handled by `ok` and `nok` +`eq(a, b)` is a helper to deeply compare lua variables. It supports numbers, strings, booleans, nils, functions and tables. It's mostly useful within ok() and nok(): +If the variables are equal it returns `true` else it returns `{msg=''}` This is recognized by `ok` and `nok` and results in also logging the reason for difference. ``` Lua ok(eq(1, 1)) @@ -138,11 +129,8 @@ ok(eq({a='b',c='d'}, {c='d',a='b'}) ok(eq('foo', 'bar')) -- will fail ``` -`spy([f])` creates function wrappers that remember each call -(arguments, errors) but behaves much like the real function. Real function is -optional, in this case spy will return nil, but will still record its calls. -Spies are most helpful when passing them as callbacks and testing that they -were called with correct values. +`spy([f])` creates function wrappers that remember each call (arguments, errors) but behaves much like the real function. Real function is optional, in this case spy will return nil, but will still record its calls. +Spies are most helpful when passing them as callbacks and testing that they were called with correct values. ``` Lua local f = spy(function(s) return #s end) @@ -157,11 +145,7 @@ ok(f.errors[3] ~= nil) ## Reports -Another useful feature is that you can customize test reports as you need. -The default reports just more or less prints out a basic report. -You can easily override this behavior as well as add any other -information you need (number of passed/failed assertions, time the test took -etc): +Another useful feature is that you can customize test reports as you need. The default `reports` just more or less prints out a basic report. You can easily override this behavior as well as add any other information you need (number of passed/failed assertions, time the test took etc): Events are: `start` when testing starts @@ -196,8 +180,7 @@ end) ``` Additionally, you can pass a different environment to keep `_G` unpolluted: -You need to set it, so the helper functions mentioned above can be added before calling -the test function. +You need to set it, so the helper functions mentioned above can be added before calling the test function. ``` Lua local myenv = {} @@ -214,4 +197,4 @@ end) This Library is for NodeMCU versions Lua 5.1 and Lua 5.3. -It is based on https://bitbucket.org/zserge/gambiarra and includes bugfixes, extensions of functionality and adaptions to NodeMCU requirements. +It is based on https://bitbucket.org/zserge/gambiarra and includes bugfixes, substantial extensions of functionality and adaptions to NodeMCU requirements. diff --git a/lua_tests/gambiarra/gambiarra_test.lua b/tests/NTest/NTest_test.lua similarity index 93% rename from lua_tests/gambiarra/gambiarra_test.lua rename to tests/NTest/NTest_test.lua index c830bd0087..5d3d4252ec 100644 --- a/lua_tests/gambiarra/gambiarra_test.lua +++ b/tests/NTest/NTest_test.lua @@ -1,4 +1,4 @@ -local N = require('gambiarra')("selftest") +local N = require('NTest')("selftest") local metatest local async @@ -30,12 +30,12 @@ local function load_tests() metatest('ok without a message', function() ok(1 == 1) ok(1 == 2) - end, {'gambiarra_test.lua:31'}, {'gambiarra_test.lua:32'}) + end, {'NTest_test.lua:31'}, {'NTest_test.lua:32'}) metatest('nok without a message', function() nok(1 == "") nok(1 == 1) - end, {'gambiarra_test.lua:36'}, {'gambiarra_test.lua:37'}) + end, {'NTest_test.lua:36'}, {'NTest_test.lua:37'}) -- -- Equality tests @@ -169,13 +169,13 @@ local function load_tests() fail(function() error("my error") end, "different error", "Failed with incorrect error") ok(true, 'unreachable code') end, {}, {'Failed with incorrect error', - 'expected errormessage "gambiarra_test.lua:169: my error" to contain "different error"'}) + 'expected errormessage "NTest_test.lua:169: my error" to contain "different error"'}) metatest('fail with incorrect errormessage default message', function() fail(function() error("my error") end, "different error") ok(true, 'unreachable code') - end, {}, {'gambiarra_test.lua:175', - 'expected errormessage "gambiarra_test.lua:175: my error" to contain "different error"'}) + end, {}, {'NTest_test.lua:175', + 'expected errormessage "NTest_test.lua:175: my error" to contain "different error"'}) metatest('fail with not failing code', function() fail(function() end, "my error", "did not fail") @@ -195,7 +195,7 @@ local function load_tests() metatest('fail with not failing code default message', function() fail(function() end) ok(true, 'unreachable code') - end, {}, {"gambiarra_test.lua:196", 'Expected to fail with Error'}) + end, {}, {"NTest_test.lua:196", 'Expected to fail with Error'}) metatest('=== load more tests ===', function() load_tests2() @@ -210,7 +210,7 @@ load_tests2 = function() metatest('error should panic', function() error("lua error") ok(true, 'unreachable code') - end, {}, {}, {'gambiarra_test.lua:211: lua error'}) + end, {}, {}, {'NTest_test.lua:211: lua error'}) -- -- called function except @@ -223,7 +223,7 @@ load_tests2 = function() metatest('subroutine error should panic', function() subfuncerror() ok(true, 'unreachable code') - end, {}, {}, {'gambiarra_test.lua:220: lua error'}) + end, {}, {}, {'NTest_test.lua:220: lua error'}) local function subfuncok() ok(false) @@ -232,7 +232,7 @@ load_tests2 = function() metatest('subroutine ok should fail', function() subfuncok() ok(true, 'unreachable code') - end, {}, {'gambiarra_test.lua:229'}) + end, {}, {'NTest_test.lua:229'}) --drain_post_queue() @@ -276,7 +276,7 @@ load_tests2 = function() metatest('async except in main', function(next) error("async except") ok(true, 'unreachable code') - end, {}, {}, {'gambiarra_test.lua:277: async except'}, true) + end, {}, {}, {'NTest_test.lua:277: async except'}, true) metatest('async fail in callback', function(next) async(function() @@ -292,7 +292,7 @@ load_tests2 = function() next() end) ok(true, 'foo') - end, {'foo'}, {}, {'gambiarra_test.lua:291: async Lua error'}, true) + end, {'foo'}, {}, {'NTest_test.lua:291: async Lua error'}, true) -- -- sync after async test @@ -332,12 +332,12 @@ load_tests2 = function() fail(function() error("my error") end, "my error", "Failed with correct error") fail(function() error("my error") end, "other error", "Failed with other error") ok(true, 'unreachable code') - end, {'Failed with correct error'}, {'Failed with other error', 'expected errormessage "gambiarra_test.lua:333: my error" to contain "other error"'}, {}, "co") + end, {'Failed with correct error'}, {'Failed with other error', 'expected errormessage "NTest_test.lua:333: my error" to contain "other error"'}, {}, "co") metatest('coroutine except in main', function(getCB, waitCb) error("coroutine except") ok(true, 'unreachable code') - end, {}, {}, {'gambiarra_test.lua:338: coroutine except'}, "co") + end, {}, {}, {'NTest_test.lua:338: coroutine except'}, "co") --local function coasync(f) table.insert(post_queue, 1, f) end local function coasync(f, p1, p2) node.task.post(node.task.MEDIUM_PRIORITY, function() f(p1,p2) end) end @@ -370,7 +370,7 @@ load_tests2 = function() ok(eq(name, "testCb"), "cb") error("error") ok(true, 'unreachable code') - end, {"cb"}, {}, {"gambiarra_test.lua:371: error"}, "co") + end, {"cb"}, {}, {"NTest_test.lua:371: error"}, "co") --- detect stray callbacks after the test has finished local strayCb diff --git a/lua_tests/gambiarra/gambiarra_file.lua b/tests/NTest/tests/file.lua similarity index 99% rename from lua_tests/gambiarra/gambiarra_file.lua rename to tests/NTest/tests/file.lua index fbd2e17e97..39d26d6149 100644 --- a/lua_tests/gambiarra/gambiarra_file.lua +++ b/tests/NTest/tests/file.lua @@ -1,4 +1,4 @@ -local N = require('gambiarra')("file") +local N = require('NTest')("file") local function cleanup() file.remove("testfile") diff --git a/lua_tests/gambiarra/gambiarra_tmr.lua b/tests/NTest/tests/tmr.lua similarity index 97% rename from lua_tests/gambiarra/gambiarra_tmr.lua rename to tests/NTest/tests/tmr.lua index 748d0903e1..ed5cf515eb 100644 --- a/lua_tests/gambiarra/gambiarra_tmr.lua +++ b/tests/NTest/tests/tmr.lua @@ -1,4 +1,4 @@ -local N = require('gambiarra')("tmr") +local N = require('NTest')("tmr") N.testasync('SINGLE alarm', function(next) local t = tmr.create(); From 419b837232197317c1803fe97493c94d49a80ab0 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 2 Nov 2020 20:50:45 +0100 Subject: [PATCH 28/36] Add tests to checking mechanisms --- .travis.yml | 4 ++-- tools/travis/run-luacheck-linux.sh | 4 ++-- tools/travis/run-luacheck-windows.sh | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6a4f2ae70c..71c3358ea5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/tools/travis/run-luacheck-linux.sh b/tools/travis/run-luacheck-linux.sh index 929f92e4d2..ad0f7a963b 100755 --- a/tools/travis/run-luacheck-linux.sh +++ b/tools/travis/run-luacheck-linux.sh @@ -67,6 +67,6 @@ if [[ $1 == "" ]]; then fi echo "Static analysys of" -find lua_modules lua_examples -iname "*.lua" -print0 | xargs -0 echo +find lua_modules lua_examples tests/NTest -iname "*.lua" -print0 | xargs -0 echo -(find lua_modules lua_examples -iname "*.lua" -print0 | xargs -0 luacheck --config tools/luacheck_config.lua) || exit +(find lua_modules lua_examples tests/NTest -iname "*.lua" -print0 | xargs -0 luacheck --config tools/luacheck_config.lua) || exit diff --git a/tools/travis/run-luacheck-windows.sh b/tools/travis/run-luacheck-windows.sh index 682510de66..d6e828a23e 100644 --- a/tools/travis/run-luacheck-windows.sh +++ b/tools/travis/run-luacheck-windows.sh @@ -8,6 +8,6 @@ if ! [ -x "cache/luacheck.exe" ]; then fi echo "Static analysys of" -find lua_modules lua_examples -iname "*.lua" -print0 | xargs -0 echo +find lua_modules lua_examples tests/NTest -iname "*.lua" -print0 | xargs -0 echo -(find lua_modules lua_examples -iname "*.lua" -print0 | xargs -0 cache/luacheck.exe --config tools/luacheck_config.lua) || exit +(find lua_modules lua_examples tests/NTest -iname "*.lua" -print0 | xargs -0 cache/luacheck.exe --config tools/luacheck_config.lua) || exit From 4d1617624b9eeb35e1f1db835eb8c98be7e0f43f Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 6 Nov 2020 20:19:41 +0100 Subject: [PATCH 29/36] Add luacheck to tests --- tests/NTest/NTest.lua | 25 ++++---- tests/NTest/NTest_test.lua | 95 +++++++++++++--------------- tests/NTest/tests/file.lua | 36 +++++------ tests/NTest/tests/tmr.lua | 21 +++--- tools/luacheck_NTest_config.lua | 11 ++++ tools/luacheck_config.lua | 6 +- tools/travis/run-luacheck-linux.sh | 7 +- tools/travis/run-luacheck-windows.sh | 7 +- 8 files changed, 110 insertions(+), 98 deletions(-) create mode 100644 tools/luacheck_NTest_config.lua diff --git a/tests/NTest/NTest.lua b/tests/NTest/NTest.lua index bfe6cbf6e3..d134a579d5 100644 --- a/tests/NTest/NTest.lua +++ b/tests/NTest/NTest.lua @@ -28,14 +28,14 @@ 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 + else return notEqual("functions differ") end end @@ -63,23 +63,23 @@ local function args(...) end local function spy(f) - local s = {} - setmetatable(s, {__call = function(s, ...) + 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 or table.unpack)(a, 1, a.n))) + 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 or table.unpack)(r, 2, r.n) + return unpack(r, 2, r.n) end end end}) - return s + return mt end local function getstackframe() @@ -88,6 +88,7 @@ local function getstackframe() 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 @@ -140,7 +141,7 @@ 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 @@ -186,9 +187,9 @@ local function NTest(testrunname, failoldinterface) collectgarbage() if next then next() end end - - local function wrap(f, ...) - f(handler, name, ...) + + local function wrap(method, ...) + method(handler, name, ...) end local function cbError(err) @@ -282,7 +283,7 @@ local function NTest(testrunname, failoldinterface) co = coroutine.create(function(wr, wa) func(wr, wa) end) - + local result, err = coroutine.resume(co, getCB, waitCb) if (not result) then currentCoName = nil diff --git a/tests/NTest/NTest_test.lua b/tests/NTest/NTest_test.lua index 5d3d4252ec..a99916a543 100644 --- a/tests/NTest/NTest_test.lua +++ b/tests/NTest/NTest_test.lua @@ -1,5 +1,5 @@ local N = require('NTest')("selftest") - +local orig_node = node local metatest local async local expected = {} @@ -87,8 +87,8 @@ local function load_tests() metatest('eq functions', function() ok(eq(function(x) return x end, function(x) return x end), 'equal') - nok(eq(function(z) return x end, function(z) return y end), 'wrong variable') - nok(eq(function(x) return x end, function(x) return x+2 end), 'wrong code') + nok(eq(function(z) return x + z end, function(z) return y + z end), 'wrong variable') -- luacheck: ignore + nok(eq(function(x) return x*2 end, function(x) return x+2 end), 'wrong code') end, {'equal', 'wrong variable', 'wrong code'}, {}) metatest('eq different types', function() @@ -125,7 +125,7 @@ local function load_tests() end, {'x == 1', 'called with 1', 'x == 42', 'called with 42'}, {}) metatest('spy with nils', function() - local function nils(a, dummy, b) return a, nil, b, nil end + local function nils(a, _, b) return a, nil, b, nil end local f = spy(nils) local r1, r2, r3, r4 = f(1, nil, 2) ok(eq(f.called, {{1, nil, 2}}), 'nil in args') @@ -145,7 +145,7 @@ local function load_tests() end, {'no errors yet', 'args ok', 'thrown ok'}, {}) metatest('another spy with exception', function() - local f = spy(function() local a = unknownVariable + 1 end) + local f = spy(function() local a = unknownVariable + 1 end) -- luacheck: ignore f() ok(f.errors[1], 'exception thrown') end, {'exception thrown'}, {}) @@ -240,9 +240,9 @@ load_tests2 = function() -- Async tests -- metatest('async test', function(next) - async(function() + async(function() ok(true, 'bar') - async(function() + async(function() ok(true, 'baz') next() end) @@ -255,7 +255,7 @@ load_tests2 = function() next() end, {'bar'}, {}, {}, true) - metatest('async fail in main', function(next) + metatest('async fail in main', function(--[[ next ]]) ok(false, "async fail") ok(true, 'unreachable code') end, {}, {'async fail'}, {}, true) @@ -263,7 +263,7 @@ load_tests2 = function() --drain_post_queue() metatest('another async test after async queue drained', function(next) - async(function() + async(function() ok(true, 'bar') next() end) @@ -273,13 +273,13 @@ load_tests2 = function() -- -- except tests async -- - metatest('async except in main', function(next) + metatest('async except in main', function(--[[ next ]]) error("async except") ok(true, 'unreachable code') end, {}, {}, {'NTest_test.lua:277: async except'}, true) metatest('async fail in callback', function(next) - async(function() + async(function() ok(false, "async fail") next() end) @@ -287,7 +287,7 @@ load_tests2 = function() end, {'foo'}, {'async fail'}, {}, true) metatest('async except in callback', function(next) - async(function() + async(function() error("async Lua error") next() end) @@ -299,7 +299,7 @@ load_tests2 = function() -- local marker = 0 metatest('set marker async', function(next) - async(function() + async(function() marker = "marked" ok(true, 'async bar') next() @@ -314,63 +314,64 @@ load_tests2 = function() -- -- coroutine async tests -- - metatest('coroutine pass', function(getCB, waitCb) + metatest('coroutine pass', function(--[[ getCB, waitCB ]]) ok(true, "passing") end, {"passing"}, {}, {}, "co") - metatest('coroutine fail in main', function(getCB, waitCb) + metatest('coroutine fail in main', function(--[[ getCB, waitCB ]]) ok(false, "coroutine fail") ok(true, 'unreachable code') end, {}, {'coroutine fail'}, {}, "co") - metatest('coroutine fail in main', function(getCB, waitCb) + metatest('coroutine fail in main', function(--[[ getCB, waitCB ]]) nok(true, "coroutine fail") nok(false, 'unreachable code') end, {}, {'coroutine fail'}, {}, "co") - metatest('coroutine fail error', function(getCB, waitCb) + metatest('coroutine fail error', function(--[[ getCB, waitCB ]]) fail(function() error("my error") end, "my error", "Failed with correct error") fail(function() error("my error") end, "other error", "Failed with other error") ok(true, 'unreachable code') - end, {'Failed with correct error'}, {'Failed with other error', 'expected errormessage "NTest_test.lua:333: my error" to contain "other error"'}, {}, "co") + end, {'Failed with correct error'}, {'Failed with other error', + 'expected errormessage "NTest_test.lua:333: my error" to contain "other error"'}, {}, "co") - metatest('coroutine except in main', function(getCB, waitCb) + metatest('coroutine except in main', function(--[[ getCB, waitCB ]]) error("coroutine except") ok(true, 'unreachable code') - end, {}, {}, {'NTest_test.lua:338: coroutine except'}, "co") + end, {}, {}, {'NTest_test.lua:339: coroutine except'}, "co") --local function coasync(f) table.insert(post_queue, 1, f) end local function coasync(f, p1, p2) node.task.post(node.task.MEDIUM_PRIORITY, function() f(p1,p2) end) end - metatest('coroutine with callback', function(getCB, waitCb) + metatest('coroutine with callback', function(getCB, waitCB) coasync(getCB("testCb")) - name = waitCb() + local name = waitCB() ok(eq(name, "testCb"), "cb") end, {"cb"}, {}, {}, "co") - metatest('coroutine with callback with values', function(getCB, waitCb) + metatest('coroutine with callback with values', function(getCB, waitCB) coasync(getCB("testCb"), "p1", 2) - name, p1, p2 = waitCb() + local name, p1, p2 = waitCB() ok(eq(name, "testCb"), "cb") ok(eq(p1, "p1"), "p1") ok(eq(p2, 2), "p2") end, {"cb", "p1", "p2"}, {}, {}, "co") - metatest('coroutine fail after callback', function(getCB, waitCb) + metatest('coroutine fail after callback', function(getCB, waitCB) coasync(getCB("testCb")) - name = waitCb() + local name = waitCB() ok(eq(name, "testCb"), "cb") ok(false, "fail") ok(true, 'unreachable code') end, {"cb"}, {"fail"}, {}, "co") - metatest('coroutine except after callback', function(getCB, waitCb) + metatest('coroutine except after callback', function(getCB, waitCB) coasync(getCB("testCb")) - name = waitCb() + local name = waitCB() ok(eq(name, "testCb"), "cb") error("error") ok(true, 'unreachable code') - end, {"cb"}, {}, {"NTest_test.lua:371: error"}, "co") + end, {"cb"}, {}, {"NTest_test.lua:372: error"}, "co") --- detect stray callbacks after the test has finished local strayCb @@ -378,12 +379,12 @@ load_tests2 = function() coasync(strayCb) end - metatest('leave stray callback', function(getCB, waitCb) + metatest('leave stray callback', function(getCB--[[ , waitCB ]]) strayCb = getCB("testa") coasync(rewrap) end, {}, {}, {}, "co") - metatest('test after stray cb', function(getCB, waitCb) + metatest('test after stray cb', function(--[[ getCB, waitCB ]]) end, {}, {"Found stray Callback 'testa' from test 'leave stray callback'"}, {}, "co") @@ -397,14 +398,12 @@ load_tests2 = function() failed = failed + 1 end print("failed: "..failed, "passed: "..passed) - node = orig_node + node = orig_node -- luacheck: ignore 121 (setting read-only global variable) end, {}, {}) end -- load_tests() -local currentTest = {} -local orig_node = node local cbWrap = function(cb) return cb end -- implement pseudo task handling for on host testing @@ -427,6 +426,7 @@ if not node then end end + -- luacheck: push ignore 121 122 (setting read-only global variable) node = {} node.task = {LOW_PRIORITY = 1, MEDIUM_PRIORITY = 2, HIGH_PRIORITY = 3} node.task.post = function (p, f) @@ -435,6 +435,8 @@ if not node then local errorfunc node.setonerror = function(fn) errorfunc = fn end + -- luacheck: pop + cbWrap = function(cb) return function(...) local ok, p1,p2,p3,p4 = pcall(cb, ...) @@ -460,26 +462,17 @@ local function stringify(t) return s:gsub('..$', '') end --- Helper function to compare two tables -local function comparetables(t1, t2) - if #t1 ~= (t2 and #t2 or 0) then return false end - for i=1,#t1 do - if t1[i] ~= t2[i] then return false end - end - return true -end - local pass -- Set meta test handler N.report(function(e, test, msg, errormsg) - local function consumemsg(msg, area) + local function consumemsg(msg, area) -- luacheck: ignore if not expected[1][area][1] then print("--- FAIL "..expected[1].name..' ('..area..'ed): unexpected "'.. msg..'"') pass = false return end - + if msg ~= expected[1][area][1] then print("--- FAIL "..expected[1].name..' ('..area..'ed): expected "'.. expected[1][area][1]..'" vs "'.. @@ -498,7 +491,6 @@ N.report(function(e, test, msg, errormsg) end if e == 'begin' then - currentTest.name = test pass = true elseif e == 'end' then consumeremainder("pass") @@ -510,7 +502,6 @@ N.report(function(e, test, msg, errormsg) else failed = failed + 1 end - currentTest = {} table.remove(expected, 1) elseif e == 'pass' then consumemsg(msg, "pass") @@ -521,7 +512,7 @@ N.report(function(e, test, msg, errormsg) elseif e == 'except' then consumemsg(msg, "except") if errormsg then consumemsg(errormsg, "except") end - elseif e == 'start' or e == 'finish' then + elseif e == 'start' or e == 'finish' then -- luacheck: ignore -- ignore else print("Extra output: ", e, test, msg, errormsg) @@ -533,7 +524,7 @@ async = function(f) table.insert(async_queue, cbWrap(f)) end local function async_next() local f = table.remove(async_queue, 1) if f then - f() + f() end end @@ -543,14 +534,14 @@ local function drain_async_queue() end end -metatest = function(name, f, expectedPassed, expectedFailed, expectedExcept, async) +metatest = function(name, f, expectedPassed, expectedFailed, expectedExcept, asyncMode) local ff = f - if async then + if asyncMode then ff = function(...) f(...) drain_async_queue() end - if (async == "co") then + if (asyncMode == "co") then N.testco(name,ff) else N.testasync(name, ff) diff --git a/tests/NTest/tests/file.lua b/tests/NTest/tests/file.lua index 39d26d6149..284dcea790 100644 --- a/tests/NTest/tests/file.lua +++ b/tests/NTest/tests/file.lua @@ -1,6 +1,6 @@ local N = require('NTest')("file") -local function cleanup() +local function cleanup() file.remove("testfile") file.remove("testfile2") local testfiles = {"testfile1&", "testFILE2"} @@ -10,7 +10,7 @@ local function cleanup() end local function buildfn(fn, ...) - params = {...} + local params = {...} local fnp = function() fn(unpack(params)) end return fnp end @@ -56,20 +56,18 @@ end) N.test('getcontents more than 1K', function() cleanup() local f = file.open("testfile", "w") - local i - for i = 1,100 do + for i = 1,100 do -- luacheck: ignore f:write("some text to test") end f:close() - content = file.getcontents("testfile") + local content = file.getcontents("testfile") ok(eq(#content, 1700), "long contents") end) N.test('read more than 1K', function() cleanup() local f = file.open("testfile", "w") - local i - for i = 1,100 do + for i = 1,100 do -- luacheck: ignore f:write("some text to test") end f:close() @@ -78,15 +76,15 @@ N.test('read more than 1K', function() ok(eq(#buffer, 1024), "first block") buffer = f:read() f:close() - ok(eq(#buffer, 1700-1024), "second block") + ok(eq(#buffer, 1700-1024), "second block") end) -function makefile(name, num128) +local function makefile(name, num128) local s128 = "16 bytes written" s128 = s128..s128..s128..s128 s128 = s128..s128 local f = file.open(name, "w") - for i = 1, num128 do + for i = 1, num128 do -- luacheck: ignore f:write(s128) end f:close() @@ -95,7 +93,7 @@ end N.test('read 7*128 bytes', function() cleanup() makefile("testfile", 7) - f = file.open("testfile","r") + local f = file.open("testfile","r") local buffer = f:read() f:close() nok(eq(buffer, nil), "nil first block") @@ -117,26 +115,26 @@ end) N.test('read 9*128 bytes', function() cleanup() makefile("testfile", 9) - f = file.open("testfile","r") + local f = file.open("testfile","r") local buffer = f:read() nok(eq(buffer, nil), "nil first block") ok(eq(#buffer, 1024), "size first block") buffer = f:read() f:close() nok(eq(buffer, nil), "nil second block") - ok(eq(#buffer, 128*8-1024), "size second block") + ok(eq(#buffer, 128*9-1024), "size second block") end) N.test('list', function() cleanup() - local files local function count(files) local filecount = 0 - for k,v in pairs(files) do filecount = filecount+1 end + for _,_ in pairs(files) do filecount = filecount+1 end return filecount end + local files local function testfile(name) ok(eq(files[name],#name), "length matches name length") end @@ -165,7 +163,7 @@ N.test('open non existing', function() file.close() file.remove(filename) end - + testopen(nok, "testfile", "r") testopen(ok, "testfile", "w") testopen(ok, "testfile", "a") @@ -173,7 +171,7 @@ N.test('open non existing', function() testopen(ok, "testfile", "w+") testopen(ok, "testfile", "a+") - fail(buildfn(file.open, "testfile"), "errormsg", "x") -- shouldn't this fail? + --fail(buildfn(file.open, "testfile"), "errormsg", "x") -- shouldn't this fail? end) N.test('open existing', function() @@ -193,7 +191,7 @@ N.test('open existing', function() testopen("w+", 0) testopen("a+", 11) - fail(buildfn(file.open, "testfile"), "errormsg", "x") -- shouldn't this fail? + --fail(buildfn(file.open, "testfile"), "errormsg", "x") -- shouldn't this fail? end) N.test('remove', function() @@ -246,6 +244,6 @@ end) N.test('stat non existing file', function() cleanup() local stat = file.stat("not existing file") - + ok(stat == nil, "stat empty") end) diff --git a/tests/NTest/tests/tmr.lua b/tests/NTest/tests/tmr.lua index ed5cf515eb..60ba795a77 100644 --- a/tests/NTest/tests/tmr.lua +++ b/tests/NTest/tests/tmr.lua @@ -4,7 +4,7 @@ N.testasync('SINGLE alarm', function(next) local t = tmr.create(); local count = 0 t:alarm(200, tmr.ALARM_SINGLE, - function(t) + function() count = count + 1 ok(count <= 1, "only 1 invocation") next() @@ -16,7 +16,7 @@ end) N.testasync('SEMI alarm', function(next) local t = tmr.create(); local count = 0 - t:alarm(200, tmr.ALARM_SEMI, + t:alarm(200, tmr.ALARM_SEMI, function(tp) count = count + 1 if count <= 1 then @@ -32,7 +32,7 @@ end) N.testasync('AUTO alarm', function(next) local t = tmr.create(); local count = 0 - t:alarm(200, tmr.ALARM_AUTO, + t:alarm(200, tmr.ALARM_AUTO, function(tp) count = count + 1 if count == 2 then @@ -47,9 +47,10 @@ end) N.testco('SINGLE alarm coroutine', function(getCB, waitCB) local t = tmr.create(); t:alarm(200, tmr.ALARM_SINGLE, getCB("timer")) - + local name, timer = waitCB() ok(eq("timer", name), "CB name matches") + ok(eq(t, timer), "CB tmr instance matches") ok(true, "coroutine end") end) @@ -57,14 +58,16 @@ end) N.testco('SEMI alarm coroutine', function(getCB, waitCB) local t = tmr.create(); t:alarm(200, tmr.ALARM_SEMI, getCB("timer")) - + local name, timer = waitCB() ok(eq("timer", name), "CB name matches") + ok(eq(t, timer), "CB tmr instance matches") timer:start() - + name, timer = waitCB() ok(eq("timer", name), "CB name matches again") + ok(eq(t, timer), "CB tmr instance matches again") ok(true, "coroutine end") end) @@ -75,12 +78,14 @@ N.testco('AUTO alarm coroutine', function(getCB, waitCB) local name, timer = waitCB() ok(eq("timer", name), "CB name matches") - + ok(eq(t, timer), "CB tmr instance matches") + name, timer = waitCB() ok(eq("timer", name), "CB name matches again") + ok(eq(t, timer), "CB tmr instance matches again") timer:stop() - + ok(true, "coroutine end") end) diff --git a/tools/luacheck_NTest_config.lua b/tools/luacheck_NTest_config.lua new file mode 100644 index 0000000000..63850d1f70 --- /dev/null +++ b/tools/luacheck_NTest_config.lua @@ -0,0 +1,11 @@ +stds.nodemcu_libs = {} +loadfile ("tools/luacheck_config.lua")(stds) +local empty = { } + +stds.nodemcu_libs.read_globals.ok = empty +stds.nodemcu_libs.read_globals.nok = empty +stds.nodemcu_libs.read_globals.eq = empty +stds.nodemcu_libs.read_globals.fail = empty +stds.nodemcu_libs.read_globals.spy = empty + +std = "lua51+nodemcu_libs" diff --git a/tools/luacheck_config.lua b/tools/luacheck_config.lua index 80f303e191..7114da9afe 100644 --- a/tools/luacheck_config.lua +++ b/tools/luacheck_config.lua @@ -1,3 +1,4 @@ +stds = stds or ... -- set stds if this script is called by another config script local empty = { } local read_write = {read_only = false} @@ -423,6 +424,7 @@ stds.nodemcu_libs = { restore = empty, setcpufreq = empty, setpartitiontable = empty, + setonerror = empty, sleep = empty, stripdebug = empty, writercr = empty, @@ -942,9 +944,7 @@ stds.nodemcu_libs = { }, pack = empty, unpack = empty, - size = empty, - package = {fields = {seeall = read_write}}, - _ENV = empty + package = {fields = {seeall = read_write}} } } diff --git a/tools/travis/run-luacheck-linux.sh b/tools/travis/run-luacheck-linux.sh index ad0f7a963b..2e3b3d6c81 100755 --- a/tools/travis/run-luacheck-linux.sh +++ b/tools/travis/run-luacheck-linux.sh @@ -67,6 +67,9 @@ if [[ $1 == "" ]]; then fi echo "Static analysys of" -find lua_modules lua_examples tests/NTest -iname "*.lua" -print0 | xargs -0 echo +find lua_modules lua_examples -iname "*.lua" -print0 | xargs -0 echo +(find lua_modules lua_examples -iname "*.lua" -print0 | xargs -0 luacheck --config tools/luacheck_config.lua) || exit -(find lua_modules lua_examples tests/NTest -iname "*.lua" -print0 | xargs -0 luacheck --config tools/luacheck_config.lua) || exit +echo "Static analysys of" +find tests -iname "*.lua" -print0 | xargs -0 echo +(find tests -iname "*.lua" -print0 | xargs -0 luacheck --config tools/luacheck_NTest_config.lua) || exit diff --git a/tools/travis/run-luacheck-windows.sh b/tools/travis/run-luacheck-windows.sh index d6e828a23e..acea9c9f9d 100644 --- a/tools/travis/run-luacheck-windows.sh +++ b/tools/travis/run-luacheck-windows.sh @@ -8,6 +8,9 @@ if ! [ -x "cache/luacheck.exe" ]; then fi echo "Static analysys of" -find lua_modules lua_examples tests/NTest -iname "*.lua" -print0 | xargs -0 echo +find lua_modules lua_examples -iname "*.lua" -print0 | xargs -0 echo +(find lua_modules lua_examples -iname "*.lua" -print0 | xargs -0 cache/luacheck.exe --config tools/luacheck_config.lua) || exit -(find lua_modules lua_examples tests/NTest -iname "*.lua" -print0 | xargs -0 cache/luacheck.exe --config tools/luacheck_config.lua) || exit +echo "Static analysys of" +find tests -iname "*.lua" -print0 | xargs -0 echo +(find tests -iname "*.lua" -print0 | xargs -0 cache/luacheck.exe --config tools/luacheck_NTest_config.lua) || exit From c6c979b0017bc70ee94108353c27739f51d8dc6a Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 6 Nov 2020 21:22:23 +0100 Subject: [PATCH 30/36] Some pushing around of files --- tests/NTest/NTest.md | 2 +- tests/NTest/{NTest_test.lua => NTest_NTest.lua} | 0 tests/{NTest/tests/file.lua => NTest_file.lua} | 10 ---------- tests/{NTest/tests/tmr.lua => NTest_tmr.lua} | 0 4 files changed, 1 insertion(+), 11 deletions(-) rename tests/NTest/{NTest_test.lua => NTest_NTest.lua} (100%) rename tests/{NTest/tests/file.lua => NTest_file.lua} (95%) rename tests/{NTest/tests/tmr.lua => NTest_tmr.lua} (100%) diff --git a/tests/NTest/NTest.md b/tests/NTest/NTest.md index 98b11fed9a..6aa3425b47 100644 --- a/tests/NTest/NTest.md +++ b/tests/NTest/NTest.md @@ -3,7 +3,7 @@ | :----- | :-------------------- | :---------- | :------ | | 2020-11-01 | [Gregor Hartmann](https://github.com/HHHartmann) | [Gregor Hartmann](https://github.com/HHHartmann) | [NTest.lua](NTest.lua) | -NTest is a test system for NodeMCU which is originally based on gambiarra. It is designed to run on chip but also runs on the host using luac.cross. +NTest is a test system for NodeMCU which is originally based on gambiarra. It is designed to run on chip but the selftest also runs on the host using luac.cross. !!! attention You will have to use LFS to run this as it is too big to fit in memory. diff --git a/tests/NTest/NTest_test.lua b/tests/NTest/NTest_NTest.lua similarity index 100% rename from tests/NTest/NTest_test.lua rename to tests/NTest/NTest_NTest.lua diff --git a/tests/NTest/tests/file.lua b/tests/NTest_file.lua similarity index 95% rename from tests/NTest/tests/file.lua rename to tests/NTest_file.lua index 284dcea790..2d3e4fef3b 100644 --- a/tests/NTest/tests/file.lua +++ b/tests/NTest_file.lua @@ -9,12 +9,6 @@ local function cleanup() end end -local function buildfn(fn, ...) - local params = {...} - local fnp = function() fn(unpack(params)) end - return fnp -end - N.test('exist', function() cleanup() nok(file.exists("non existing file"), "non existing file") @@ -170,8 +164,6 @@ N.test('open non existing', function() testopen(nok, "testfile", "r+") testopen(ok, "testfile", "w+") testopen(ok, "testfile", "a+") - - --fail(buildfn(file.open, "testfile"), "errormsg", "x") -- shouldn't this fail? end) N.test('open existing', function() @@ -190,8 +182,6 @@ N.test('open existing', function() testopen("r+", 0) testopen("w+", 0) testopen("a+", 11) - - --fail(buildfn(file.open, "testfile"), "errormsg", "x") -- shouldn't this fail? end) N.test('remove', function() diff --git a/tests/NTest/tests/tmr.lua b/tests/NTest_tmr.lua similarity index 100% rename from tests/NTest/tests/tmr.lua rename to tests/NTest_tmr.lua From c68e4aaf118431a0f507d3ce48042237e3be6446 Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 7 Nov 2020 16:56:56 +0100 Subject: [PATCH 31/36] more (last) fixes and file juggling --- tests/NTest/NTest.md | 4 ++-- tools/luacheck_NTest_config.lua | 2 +- tools/luacheck_config.lua | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/NTest/NTest.md b/tests/NTest/NTest.md index 6aa3425b47..6d7b1b5fcd 100644 --- a/tests/NTest/NTest.md +++ b/tests/NTest/NTest.md @@ -5,8 +5,8 @@ NTest is a test system for NodeMCU which is originally based on gambiarra. It is designed to run on chip but the selftest also runs on the host using luac.cross. -!!! attention - You will have to use LFS to run this as it is too big to fit in memory. +!!! warning + This module is too big to load by standard `require` function or compile on ESP8266 using `node.compile()`. The only option to load and use it is to use [LFS](../lfs.md). ## Example diff --git a/tools/luacheck_NTest_config.lua b/tools/luacheck_NTest_config.lua index 63850d1f70..98bd1ecd0d 100644 --- a/tools/luacheck_NTest_config.lua +++ b/tools/luacheck_NTest_config.lua @@ -8,4 +8,4 @@ stds.nodemcu_libs.read_globals.eq = empty stds.nodemcu_libs.read_globals.fail = empty stds.nodemcu_libs.read_globals.spy = empty -std = "lua51+nodemcu_libs" +std = "lua51+lua53+nodemcu_libs" diff --git a/tools/luacheck_config.lua b/tools/luacheck_config.lua index 7114da9afe..8c3ea5b8d1 100644 --- a/tools/luacheck_config.lua +++ b/tools/luacheck_config.lua @@ -948,4 +948,4 @@ stds.nodemcu_libs = { } } -std = "lua51+nodemcu_libs" \ No newline at end of file +std = "lua51+lua53+nodemcu_libs" From 42e9436a4850c01f6e8591f176b39ba2091266ed Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 7 Nov 2020 22:04:09 +0100 Subject: [PATCH 32/36] Minor tweaks and forgotten checkin --- tests/NTest/NTest.lua | 6 ++---- tests/NTest/NTest.md | 3 +++ tests/NTest/NTest_NTest.lua | 28 ++++++++++++++-------------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/tests/NTest/NTest.lua b/tests/NTest/NTest.lua index d134a579d5..b3029e9caf 100644 --- a/tests/NTest/NTest.lua +++ b/tests/NTest/NTest.lua @@ -245,10 +245,8 @@ local function NTest(testrunname, failoldinterface) end local function report(f, envP) - if type(f) == 'function' then - outputhandler = f - env = envP or _G - end + outputhandler = f or outputhandler + env = envP or env end local currentCoName diff --git a/tests/NTest/NTest.md b/tests/NTest/NTest.md index 6d7b1b5fcd..ca4e9d6607 100644 --- a/tests/NTest/NTest.md +++ b/tests/NTest/NTest.md @@ -193,6 +193,9 @@ tests.test('Some test', function() end) ``` +You can set any of the parameters to `nil` to leave the value unchanged. + + ## Appendix This Library is for NodeMCU versions Lua 5.1 and Lua 5.3. diff --git a/tests/NTest/NTest_NTest.lua b/tests/NTest/NTest_NTest.lua index a99916a543..0e4a282096 100644 --- a/tests/NTest/NTest_NTest.lua +++ b/tests/NTest/NTest_NTest.lua @@ -30,12 +30,12 @@ local function load_tests() metatest('ok without a message', function() ok(1 == 1) ok(1 == 2) - end, {'NTest_test.lua:31'}, {'NTest_test.lua:32'}) + end, {'NTest_NTest.lua:31'}, {'NTest_NTest.lua:32'}) metatest('nok without a message', function() nok(1 == "") nok(1 == 1) - end, {'NTest_test.lua:36'}, {'NTest_test.lua:37'}) + end, {'NTest_NTest.lua:36'}, {'NTest_NTest.lua:37'}) -- -- Equality tests @@ -169,13 +169,13 @@ local function load_tests() fail(function() error("my error") end, "different error", "Failed with incorrect error") ok(true, 'unreachable code') end, {}, {'Failed with incorrect error', - 'expected errormessage "NTest_test.lua:169: my error" to contain "different error"'}) + 'expected errormessage "NTest_NTest.lua:169: my error" to contain "different error"'}) metatest('fail with incorrect errormessage default message', function() fail(function() error("my error") end, "different error") ok(true, 'unreachable code') - end, {}, {'NTest_test.lua:175', - 'expected errormessage "NTest_test.lua:175: my error" to contain "different error"'}) + end, {}, {'NTest_NTest.lua:175', + 'expected errormessage "NTest_NTest.lua:175: my error" to contain "different error"'}) metatest('fail with not failing code', function() fail(function() end, "my error", "did not fail") @@ -195,7 +195,7 @@ local function load_tests() metatest('fail with not failing code default message', function() fail(function() end) ok(true, 'unreachable code') - end, {}, {"NTest_test.lua:196", 'Expected to fail with Error'}) + end, {}, {"NTest_NTest.lua:196", 'Expected to fail with Error'}) metatest('=== load more tests ===', function() load_tests2() @@ -210,7 +210,7 @@ load_tests2 = function() metatest('error should panic', function() error("lua error") ok(true, 'unreachable code') - end, {}, {}, {'NTest_test.lua:211: lua error'}) + end, {}, {}, {'NTest_NTest.lua:211: lua error'}) -- -- called function except @@ -223,7 +223,7 @@ load_tests2 = function() metatest('subroutine error should panic', function() subfuncerror() ok(true, 'unreachable code') - end, {}, {}, {'NTest_test.lua:220: lua error'}) + end, {}, {}, {'NTest_NTest.lua:220: lua error'}) local function subfuncok() ok(false) @@ -232,7 +232,7 @@ load_tests2 = function() metatest('subroutine ok should fail', function() subfuncok() ok(true, 'unreachable code') - end, {}, {'NTest_test.lua:229'}) + end, {}, {'NTest_NTest.lua:229'}) --drain_post_queue() @@ -276,7 +276,7 @@ load_tests2 = function() metatest('async except in main', function(--[[ next ]]) error("async except") ok(true, 'unreachable code') - end, {}, {}, {'NTest_test.lua:277: async except'}, true) + end, {}, {}, {'NTest_NTest.lua:277: async except'}, true) metatest('async fail in callback', function(next) async(function() @@ -292,7 +292,7 @@ load_tests2 = function() next() end) ok(true, 'foo') - end, {'foo'}, {}, {'NTest_test.lua:291: async Lua error'}, true) + end, {'foo'}, {}, {'NTest_NTest.lua:291: async Lua error'}, true) -- -- sync after async test @@ -333,12 +333,12 @@ load_tests2 = function() fail(function() error("my error") end, "other error", "Failed with other error") ok(true, 'unreachable code') end, {'Failed with correct error'}, {'Failed with other error', - 'expected errormessage "NTest_test.lua:333: my error" to contain "other error"'}, {}, "co") + 'expected errormessage "NTest_NTest.lua:333: my error" to contain "other error"'}, {}, "co") metatest('coroutine except in main', function(--[[ getCB, waitCB ]]) error("coroutine except") ok(true, 'unreachable code') - end, {}, {}, {'NTest_test.lua:339: coroutine except'}, "co") + end, {}, {}, {'NTest_NTest.lua:339: coroutine except'}, "co") --local function coasync(f) table.insert(post_queue, 1, f) end local function coasync(f, p1, p2) node.task.post(node.task.MEDIUM_PRIORITY, function() f(p1,p2) end) end @@ -371,7 +371,7 @@ load_tests2 = function() ok(eq(name, "testCb"), "cb") error("error") ok(true, 'unreachable code') - end, {"cb"}, {}, {"NTest_test.lua:372: error"}, "co") + end, {"cb"}, {}, {"NTest_NTest.lua:372: error"}, "co") --- detect stray callbacks after the test has finished local strayCb From 8ce26d4a43c22ffe94d98fd7dd9bde35ce652d1b Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 7 Nov 2020 22:15:11 +0100 Subject: [PATCH 33/36] Add NTest selftest to travis --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 71c3358ea5..eaa3e7540c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,10 @@ 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 tests/NTest -iname "*.lua" -print0 | xargs -0 echo -- find lua_modules lua_examples tests/NTest -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 +- cd tests/NTest +- ../../$LUACC -e ../NTest/NTest_NTest.lua +- cd "$TRAVIS_BUILD_DIR" - 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 From 2e0a5cd5ce8212ebe2a27ae1b7961f25d98e4fea Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 7 Nov 2020 22:35:29 +0100 Subject: [PATCH 34/36] Trying how to master travis --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index eaa3e7540c..557943f3fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,9 @@ script: - 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 - cd tests/NTest -- ../../$LUACC -e ../NTest/NTest_NTest.lua +- bash ../../$LUACC -e ../NTest/NTest_NTest.lua +- bash "../../$LUACC -e ../NTest/NTest_NTest.lua" +- ../../{$LUACC} -e ../NTest/NTest_NTest.lua - cd "$TRAVIS_BUILD_DIR" - 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 From ad7b8e930a40b76c871999e28ecc476dad5691b5 Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 7 Nov 2020 22:58:54 +0100 Subject: [PATCH 35/36] another try --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 557943f3fe..876b23ad04 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,9 +35,9 @@ script: - 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 - cd tests/NTest -- bash ../../$LUACC -e ../NTest/NTest_NTest.lua -- bash "../../$LUACC -e ../NTest/NTest_NTest.lua" -- ../../{$LUACC} -e ../NTest/NTest_NTest.lua +- echo $LUACC ../../$LUACC ../NTest/NTest_NTest.lua +- ls -l $LUACC ../../$LUACC ../NTest/NTest_NTest.lua +- if [ "$OS" = "linux" ]; then ../../$LUACC -e ../NTest/NTest_NTest.lua; fi - cd "$TRAVIS_BUILD_DIR" - 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 From b5c53f6c1cbb75309f61cf637fc6b620ea77c685 Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 7 Nov 2020 23:06:35 +0100 Subject: [PATCH 36/36] restrict NTest selftest to linux --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 876b23ad04..54ae80e62b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,8 +35,6 @@ script: - 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 - cd tests/NTest -- echo $LUACC ../../$LUACC ../NTest/NTest_NTest.lua -- ls -l $LUACC ../../$LUACC ../NTest/NTest_NTest.lua - if [ "$OS" = "linux" ]; then ../../$LUACC -e ../NTest/NTest_NTest.lua; fi - cd "$TRAVIS_BUILD_DIR" - if [ "$OS" = "linux" ]; then bash "$TRAVIS_BUILD_DIR"/tools/travis/run-luacheck-linux.sh; fi