- Libraries can run tests when directly run
check()- run code only when
check()isn't disabled assert()- nester
- Run tests only if a pragma or Lua definition is set
Libraries can run tests when directly run
local fib = @record{}
function fib.recursive(n: integer, current: integer, next: integer): integer
if n == 0 then return current end
return fib.recursive(n - 1, next, current + next)
end
## if fib.scope.parent.is_root then
require 'string'
local function clock(): integer <cimport, nodecl, cinclude '<time.h>'> end
print('time:', clock())
## for _, n in ipairs({5, 15, 30, 40}) do
print(string.format('fib(%d) = %d', #[n]#+1, fib.recursive(#[n]#, 0, 1)))
print('time:', clock())
## end
## else
return fib
## endUsage as a library and as a script:
$ nelua -i 'local fib = require("fibonacci") print(fib.recursive(15,0,1))'
610
$ nelua fibonacci.nelua
time: 497
fib(6) = 5
time: 559
fib(16) = 610
time: 567
fib(31) = 832040
time: 572
fib(41) = 102334155
time: 577
Run tests with check()
check() is like assert(), but is removed in release builds, so you could it for checks you don't mind running all the time in debug builds. The sqrt() candidates in the next example would likely be fine. This is also great for contract programming:
-- Example from "Programming in Ada 2012", by John Barnes.
require 'vector'
local stack = @record{data: vector(integer), limit: integer}
function stack:isempty() return 0 == #self.data end
function stack:isfull() return self.limit >= #self.data end
function stack:push(x: integer)
check(not self:isfull()) -- precondition
defer check(not self:isempty()) end -- postcondition
self.data:push(x)
end
function stack:pop()
check(not self:isempty())
defer check(not self:isfull()) end
return self.data:pop()
end
function stack:__eq(other: stack)
return self.data == other.data
and self.limit == other.limit
end
return stackUsage:
$ nelua -i 'local stack = require("stack") local x: stack x:push(1)'
stack.nelua:7:9: runtime error: assertion failed!
check(not self:isfull()) -- precondition
^~~~~~~~~~~~~~~~~
Oops, zero-initialized stacks can't be pushed to, because the depth limit is 0.
run code only when check() isn't disabled
To test the first return value of a function call, it's enough to have a list of check()s:
check(2 == machine{1,0,0,0,99})
check(2 == machine{2,3,0,3,99})
check(2 == machine{2,4,4,5,99,0})
check(30 == machine{1,1,1,4,99,5,6,0,99})But if you start needing to test more than a single return, you'll need to store variables somewhere. The way to do this without having to reorganize your testing significantly, is to test the same pragma that check() itself tests, pragmas.nochecks:
## if not pragmas.nochecks then do local op, m = decode(1002) check(op == Opcode.Mul) check(m[0] == Mode.Position) check(m[1] == Mode.Immediate) check(m[2] == Mode.Position) end ## end
Run tests with assert()
It's not so nice as the next option, but a minimal option is assert() gated by Lua (i.e., Nelua's preprocessor). In the following testmath has no builtin meaning, it's just a keyword decided on the spot, that can be supplied with the -P flag to nelua.
local function sqrt(n: integer): integer require 'math' return math.sqrt(n) end ## if pragmas.testmath then assert(sqrt(1) == 1) assert(sqrt(4) == 2) assert(sqrt(9) == 3) assert(sqrt(16) == 5) ## end
As tested:
$ nelua mysqrt.nelua # no output
$ nelua -P testmath mysqrt.nelua
mysqrt.nelua:10:19: runtime error: assertion failed!
assert(sqrt(16) == 5)
^~~~
Aborted
$ nelua -d -P testmath mysqrt.nelua # shows C line numbers and backtrace, including nelua_assert_line_4 and nelua_abort functions
...
$ nelua -o test -P testmath mysqrt.nelua && gdb ./test
(gdb) b nelua_abort
(gdb) run
(gdb) bt # confirm backtrace again
(gdb) up
(gdb) up
(gdb) list
183 }
184 int nelua_main(int argc, char** argv) {
185 nelua_assert_line_1((mysqrt_sqrt(1) == 1));
186 nelua_assert_line_2((mysqrt_sqrt(4) == 2));
187 nelua_assert_line_3((mysqrt_sqrt(9) == 3));
188 nelua_assert_line_4((mysqrt_sqrt(16) == 5));
189 return 0;
190 }
191 int main(int argc, char** argv) {
192 return nelua_main(argc, argv);
(gdb) p mysqrt_sqrt(16)
$1 = 4
(gdb) p 4 == 5
$2 = 0
Run tests with nester
For nice output you can use nester from edubart/nelua-batteries. For example:
require 'nester'
local regexp = require("pcre")
nester.describe('pcre', function()
nester.it('regex', function()
local matches: [6]cint
local re <close> = regexp.MustCompile('^t \\d+ / (?P<goal>\\d+) \\(-?\\d+\\) = \\d+%$', &matches, #matches)
local str = 't 400 / 1600 (1200) = 25%'
expect.truthy(re:Match(str))
expect.equal(re:Group(str, 1), "1600")
expect.equal(re:Group(str, 1), "1601")
end)
end)
nester.report()
Has brightly colored output:
[FAIL] pcre | regex (test_pcre.nelua:5) test_pcre.nelua:11: expected value be equal first value: 1600 second value: 1601 [====] pcre | 0 successes / 1 failures / 0.000454 seconds 0 successes / 1 failures / 0.000513 seconds
This can be in a separate file by itself that loads your application's libraries and tests them 'externally', with a build system running each test separately, e.g., nelua test_pcre.
You can also have inline tests gated by Lua:
local function f() return 1 end
local function main()
print('f', f())
end
## if pragmas.testrun then
require 'nester'
nester.describe('example', function()
nester.it('f', function()
expect.equal(f(), 2)
end)
end)
## else
main()
## endUsage:
$ nelua example.nelua f 1 $ nelua -P testrun example.nelua -L$HOME/nelua/nelua-batteries warning: using error handling module, it is highly experimental and incomplete! [FAIL] example | f (example.nelua:10) example.nelua:11: expected value be equal first value: 1 second value: 2 [====] example | 0 successes / 1 failures / 0.000216 seconds