How do I write busted unit tests for functions that return ok,err? - lua

I have a Lua function that returns false followed by error message and want to test its behavior using the busted testing framework. Right now I am doing it a bit like this:
function safe_divide(a, b)
if b > 0 then -- buggy! should be b ~= 0
return a / b
else
return false, "division by zero"
end
end
describe("safe_divide", function()
it("can divide by positive numbers", function()
local ok, err = safe_divide(10.0, 5.0)
assert.truthy(ok)
assert.are.same(2.0, ok)
end)
it("errors when dividing by zero", function()
local ok, err = safe_divide(10.0, 0.0)
assert.not_truthy(ok)
assert.are.same("division by zero", err)
end)
it("can divide by negative numbers", function()
local ok, err = safe_divide(-10.0, -5.0)
assert.truthy(ok)
assert.are.same(2.0, ok)
end)
end)
There are two things that I don't like about my current approach:
Each test is 3 lines instead of a single clean line
When the 3rd test fails, busted just says that false is not a truthy value as expected, and never mentions the "divide by zero" error message.
Is there a way I can improve my test file to avoid these problems?
I think what I want to do is kind of similar to the has_error assertion in busted but that seems to be only for functions that actually raise exceptions, not for functions that return false followed by error message.

Busted makes use of luassert, that can be extended with your own assertions.
For instance, the following code uses a user-defined assertion answers that takes as first parameter a table of the expected result, and the effective function results as remaining parameters.
local assert = require "luassert"
local function safe_divide(a, b)
if b > 0 then -- buggy! should be b ~= 0
return a / b
else
return false, "division by zero"
end
end
local function answers(state, arguments)
local expected = arguments[1]
assert(type(expected) == "table")
for i = 2, #arguments do
if arguments[i] ~= expected[i-1] then
state.failure_message = "unexpected result " .. tostring (i-1) .. ": " .. tostring (arguments [i])
return false
end
end
return true
end
assert:register("assertion", "answers", answers)
describe("safe_divide", function()
it("can divide by positive numbers", function()
assert.answers({ 2.0 }, safe_divide(10.0, 5.0))
end)
it("errors when dividing by zero", function()
assert.answers({ false, "division by zero" }, safe_divide(10.0, 0.0))
end)
it("can divide by negative numbers", function()
assert.answers({ 2.0 }, safe_divide(-10.0, -5.0))
end)
end)
This code lacks correct formatting for the assertion message. You can look at the luaassert documentation, or at the predefined assertions. It contains a say module for translations of messages.

Related

Lua 3d table lacks object permanence (key values do not consistently match to table data)

So I'm having a bit of a strange problem with Lua, I use the following functions to setup, read and write from a global 3D "array" called Cube. However it seems that every time I am reading or writing to this "array", the data is only stored on the instance used by the function, despite Cube being a global variable, can't say I have ever encountered this before, very strange.
-- stole this one from: https://stackoverflow.com/questions/27976526/using-a-coordinate-pair-as-a-key-in-a-lua-table
-- basically the intended use is to store a 2d table of block information for the level the turtle is on
function setUpLevel()
local test = {_props = {}}
local mt = {}
local function coord2index(x, z)
return ((x-1) * xMax) + z
end
mt.__index = function(s, k)
if s._props[coord2index(k[1], k[2])] ~= nil then
return s._props[coord2index(k[1], k[2])]
end
end
mt.__newindex = function(s, k, v)
s._props[coord2index(k[1], k[2])] = v
end
mt.__call = function (t, k)
if type(k) == "table" then print "Table" end
end
setmetatable(test, mt)
return test
--test[{1,2}] = 5
end
function setupCube()
local cube = {}
cube[relY]=setUpLevel()
return cube
end
Cube = setupCube()
function readCubeData(x,y,z)
if (Cube[y]==nil) then
return nil
end
-- debug
return Cube[y][{x,z}]
end
function storeCubeData(x,y,z,data)
if (readCubeData(x,y,z)==nil) then
Cube[y]=setUpLevel()
end
Cube[y][{x,z}]=data
data= readCubeData(x,y,z)
print (x,",",y,",",z,":",readCubeData(x,y,z))
sleep(.5)
end
Output Examples:
The print statements in storeCubeData() would provide the correct output of (order doesn't matter, just corresponding data to x,y,z values)
However the following print statements in start()
function start()
detectAndStore()
print("=============")
sleep(1)
data= readCubeData(0,relY,1)
print (0,",",relY,",",1,":",readCubeData(0,relY,1))
sleep(.5)
data= readCubeData(-1,relY,0)
print (-1,",",relY,",",0,":",readCubeData(-1,relY,0))
sleep(.5)
data= readCubeData(0,y,-1)
print (0,",",relY,",",-1,":",readCubeData(0,relY,-1))
sleep(.5)
data= readCubeData(1,y,0)
print (1,",",relY,",",0,":",readCubeData(1,relY,0))
sleep(.5)
end
start()
would produce an output of
which is just tragically incorrect, the x,y,z values do not point to the correct values and also tables are not meant to be repeated in the same x,y,z location. I seriously can't for the life of me figure this out
The error is in if (readCubeData(x,y,z)==nil) then in storeCubeData(x,y,z,data). You overwrite the entire 'level' every time you try to store new information in it. It should be if (Cube [y] == nil) then. The whole if statement can be further reduced to Cube [y] = Cube [y] or setUpLevel ().
Also, if s._props[coord2index(k[1], k[2])] ~= nil then is not necessary; and the functions can be defined as local; and the hash function coord2index will cause collisions (e.g. coord2index (1, 5) == coord2index (2, -5), if xMax == 10), so replace it as #Egor Skriptunoff said.

How can a Lua function return nil, even if the returned value is not nil inside the function?

I have created a function that (pseudo)randomly creates a table containing numbers. I then loop this function until at least correct result is found. As soon as I've confirmed that at least one such result exists, I stop the function and return the table.
When I create tables containing small values, there are no issues. However, once the random numbers grow to the range of hundreds, the function begins to return nil, even though the table is true the line before I return it.
local sort = table.sort
local random = math.random
local aMin, aMax = 8, 12
local bMin, bMax = 200, 2000
local function compare( a, b )
return a < b
end
local function getNumbers()
local valid = false
local numbers = {}
-- Generate a random length table, containing random number values.
for i = 1, random( aMin, aMax ) do
numbers[i] = random( bMin, bMax )
end
sort( numbers, compare )
-- See if a specific sequence of numbers exist in the table.
for i = 2, #numbers do
if numbers[i-1]+1 == numbers[i] or numbers[i-1] == numbers[i] then
-- Sequence found, so stop.
valid = true
break
end
end
for i = 1, #numbers-1 do
for j = i+1, #numbers do
if numbers[j] % numbers[i] == 0 and numbers[i] ~= 1 then
valid = true
break
end
end
end
if valid then
print( "Within function:", numbers )
return numbers
else
getNumbers()
end
end
local numbers = getNumbers()
print( "Outside function:", numbers )
This function, to my understanding, is supposed to loop infinitely until I find a valid sequence. The only way that the function can even end, according to my code, is if valid is true.
Sometimes, more often than not, with large numbers the function simply outputs a nil value to the outside of the function. What is going on here?
You're just doing getNumbers() to recurse instead of return getNumbers(). This means that if the recursion gets entered, the final returned value will be nil no matter what else happens.
In the else case of the if valid then, you are not returning anything. You only return anything in the valid case. In the else case, a recursive call may return something, but then you ignore that returned value. The print you see is corresponding to the return from the recursive call; it isn't making it out the original call.
You mean to return getNumbers().

What is the alternative for switch statement in Lua language?

I have this piece of code in C++ and i want to know how can i write some codes that replace switch statement in Lua because i face many problems and i need to use this statement.
int choice;
do// loop
{
cout<<"\n >>> The General Menu <<< \n";
cout << endl;
cout<< " press (1) to Add "<<endl;
cout<< " press (2) to Save "<<endl;
cout<< " press (3) to Quit " << endl;
cout<< endl;
cout<< "Enter your choice please (1/2/3): ";
cin>>choice;
switch(choice)
{
case 1:
add();
break;
case 2:
save();
break;
default:
cout<<" The program has been terminated "<<endl;
cout<<" Thank you! \n";
}
} while (choice != 3);
}
The statement has been used inside a do..while loop.
In general, if you want a switch statement in Lua, what you ought to be doing is building a table. For your simple case of choice that could be 1, 2, or fail, a simple if statement with a few conditions is sufficient. For more complex cases, a table of functions should be employed:
local c_tbl =
{
[1] = add,
[2] = save,
}
local func = c_tbl[choice]
if(func) then
func()
else
print " The program has been terminated."
print " Thank you!";
end
You can use lexical scoping to allow the functions in the table to be able to access local variables, just as if the code was written inline.
Try this one (click here to run the script in a Lua compiler), Hope the code is self-explanatory ;-) and
resembles the same pseudo code format..!!
print("enter your choice : ")
mychoice = io.read()
switch = function (choice)
-- accepts both number as well as string
choice = choice and tonumber(choice) or choice -- returns a number if the choic is a number or string.
-- Define your cases
case =
{
[1] = function ( ) -- case 1 :
print("your choice is Number 1 ") -- code block
end, -- break statement
add = function ( ) -- case 'add' :
print("your choice is string add ") -- code block
end, -- break statement
['+'] = function ( ) -- case '+' :
print("your choice is char + ") -- code block
end, -- break statement
default = function ( ) -- default case
print(" your choice is din't match any of those specified cases")
end, -- u cant exclude end hear :-P
}
-- execution section
if case[choice] then
case[choice]()
else
case["default"]()
end
end
-- Now you can use it as a regular function. Tadaaa..!!
switch(mychoice)
Lua:
if choice == 1
then add()
elseif choice == 2
then save()
else print "The program has been terminated\nThank you!"
end
one more version of switcher (without initializing table as variable):
local case=2;
local result=({[1]="case1", [2]="case2", 3="case3"})[case];
print (result); --> case2
While simply creating a table indexed by cases with functions as elements is most probably the fastest approach, there is this solution I've made which IMO has better code readability:
function switch(element)
local Table = {
["Value"] = element,
["DefaultFunction"] = nil,
["Functions"] = {}
}
Table.case = function(testElement, callback)
Table.Functions[testElement] = callback
return Table
end
Table.default = function(callback)
Table.DefaultFunction = callback
return Table
end
Table.process = function()
local Case = Table.Functions[Table.Value]
if Case then
Case()
elseif Table.DefaultFunction then
Table.DefaultFunction()
end
end
return Table
end
Example Use:
switch(Player:GetName())
.case("Kate", function() print("This player's name rhymes with Fate")end)
.case("Tod", function() print("This player's name rhymes with Cod") end)
.default(function() print("This player's name is not Kate or Tod") end)
.process()
I encountered this issue with functions that would take different parameters - something which the other answers don't handle well.
I solved that with anonymous functions.
-- call the relevant execution based on its opcode
local instructions = {
[01] = function () self:perform_add(table.unpack(valargs)) end,
[02] = function () self:perform_multiply(table.unpack(valargs)) end,
[03] = function () self:perform_store_input(outputargs[1]) end,
[04] = function () self:perform_output(valargs[1]) end,
[05] = function () self:perform_jnz(table.unpack(valargs)) end,
[06] = function () self:perform_jz(table.unpack(valargs)) end,
[07] = function () self:perform_less_than(table.unpack(valargs)) end,
[08] = function () self:perform_equals(table.unpack(valargs)) end,
[99] = function () self:perform_exit() end,
}
local instr = instructions[opcode]
if (instr) then
instr()
else
print("No instruction for opcode " .. opcode)
end
The actions I want to take in my different switch cases are all defined as anonymous functions in a table. The keys used (e.g. 08 here) are the values our variable to switch on might assume (opcode here). The default case of the switch statement happens in my else clause. There is no requirement for a break equivalent - but if you want to have one case continue with the next you would have to call it explicitly.
Reply to comment asking for clarification:
You're right that this example is not complete. You can find my usage here when I did adventofcode 2019 day 7. I can try answer your questions but I never touched lua before, and never after. valargs is a table of arguments because different functions here take different numbers of arguments. But that is not necessarily relevant to the question. Basically, I'm just calling functions here.
In my example, self exists because I defined the functions on a local (and did some weird changes as outlined here). The relevant code parts:
-- a "class"
local IntComputer = {}
function IntComputer:perform_exit()
self.program_ended = true
end
function IntComputer:perform_add(a, b, target)
print(" " .. a .. " + " .. b .. " => " .. target)
self:set_value(target, a+b)
end
If you want switch as a function that is callable, you could use something funny with the callback feature:
(The example below is a switch statement based on the variable type, but you could make the table index into whatever you want to test it for. Just change the return statement of the switch function to not test for type(case))
(This is essentially a lazy table lookup much like Python's dictionary feature but each element is a function)
#!/usr/bin/lua
-- Callback switch statement:
local function switch(a, case)
-- Local variable instead of function(a) on every case:
local value = a
-- Cases list:
local switchcase = {}
-- Cases:
switchcase["string"] = function()
return (tostring(value) .. " is a string")
end
switchcase["number"] = function()
return tostring(value .. " is a number")
end
switchcase["boolean"] = function()
return (tostring(avalue) .. " is a boolean")
end
return switchcase[type(case)](a)
end
local value = 5
print(switch(value,value)) --> 5 is a number
local value = "test"
print(switch(value,value)) --> test is a string
local value = true
print(switch(value,value)) --> true is a boolean
I don't know the performance of this code compared to the two answers given above, but using local variables ought to make it quick enough for repeated use. If you make your switch function in the global scope it could become a standard function for your project to be used.
Here's another fun method using loadstring() and a table lookup.
switch = function(cases,args)
if (cases[args] == nil) then return args else return assert(loadstring ('return ' .. cases[args]))() end
end
local case = 2
local result = switch({
[1] = "2^" .. case,
[2] = string.format("2^%i",case),
[3] = tostring(2^case)
},
case
)
print(result) --> 4
This method is somewhat dangerous to use since loadstring() is similar to Python's eval() function.
I found it ugly to write "function(x)" on every case in the examples provided by the Lua wiki. This is a neat way.
The "default" case is the "return args" part of the function.
I use this code :
while true do local tmpswitch1 = exp ; --[[ switch <exp> do ]]
if tmpswitch1 == exp1 then --[[ case <exp1> : ]]
-- do something
break
end ;if tmpswitch1 == exp2 then --[[ case <exp2> : ]]
-- do something
break
end ; --[[ default : ]]
-- do something
break ; end --[[ switch tmpswitch1 ]]
function case(i,d) return function(t) return t[i] or d end end
x='two'
r=case(x) {
one=1,
two=2,
three=3,
}
case(r,function() print "default" end) {
[1]=function() print "one" end,
[2]=function() print "two" end,
[3]=function() print "three" end,
}()

How to put floating points in LUA [duplicate]

I'd like to format a number to look like 1,234 or 1,234,432 or 123,456,789, you get the idea. I tried doing this as follows:
function reformatint(i)
local length = string.len(i)
for v = 1, math.floor(length/3) do
for k = 1, 3 do
newint = string.sub(mystring, -k*v)
end
newint = ','..newint
end
return newint
end
As you can see, a failed attempt, my problem is that I can't figure out what the error is because the program I am running this in refuses to report an error back to me.
Here's a function that takes negative numbers, and fractional parts into account:
function format_int(number)
local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)')
-- reverse the int-string and append a comma to all blocks of 3 digits
int = int:reverse():gsub("(%d%d%d)", "%1,")
-- reverse the int-string back remove an optional comma and put the
-- optional minus and fractional part back
return minus .. int:reverse():gsub("^,", "") .. fraction
end
assert(format_int(1234) == '1,234')
assert(format_int(1234567) == '1,234,567')
assert(format_int(123456789) == '123,456,789')
assert(format_int(123456789.1234) == '123,456,789.1234')
assert(format_int(-123456789.) == '-123,456,789')
assert(format_int(-123456789.1234) == '-123,456,789.1234')
assert(format_int('-123456789.1234') == '-123,456,789.1234')
print('All tests passed!')
Well, let's take this from the top down. First of all, it's failing because you've got a reference error:
...
for k = 1, 3 do
newint = string.sub(mystring, -k*v) -- What is 'mystring'?
end
...
Most likely you want i to be there, not mystring.
Second, while replacing mystring with i will fix the errors, it still won't work correctly.
> =reformatint(100)
,100
> =reformatint(1)
,000
That's obviously not right. It seems like what you're trying to do is go through the string, and build up the new string with the commas added. But there are a couple of problems...
function reformatint(i)
local length = string.len(i)
for v = 1, math.floor(length/3) do
for k = 1, 3 do -- What is this inner loop for?
newint = string.sub(mystring, -k*v) -- This chops off the end of
-- your string only
end
newint = ','..newint -- This will make your result have a ',' at
-- the beginning, no matter what
end
return newint
end
With some rework, you can get a function that work.
function reformatint(integer)
for i = 1, math.floor((string.len(integer)-1) / 3) do
integer = string.sub(integer, 1, -3*i-i) ..
',' ..
string.sub(integer, -3*i-i+1)
end
return integer
end
The function above seems to work correctly. However, it's fairly convoluted... Might want to make it more readable.
As a side note, a quick google search finds a function that has already been made for this:
function comma_value(amount)
local formatted = amount
while true do
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
if (k==0) then
break
end
end
return formatted
end
You can do without loops:
function numWithCommas(n)
return tostring(math.floor(n)):reverse():gsub("(%d%d%d)","%1,")
:gsub(",(%-?)$","%1"):reverse()
end
assert(numWithCommas(100000) == "100,000")
assert(numWithCommas(100) == "100")
assert(numWithCommas(-100000) == "-100,000")
assert(numWithCommas(10000000) == "10,000,000")
assert(numWithCommas(10000000.00) == "10,000,000")
The second gsub is needed to avoid -,100 being generated.
I remember discussing about this in the LÖVE forums ... let me look for it...
Found it!
This will work with positive integers:
function reformatInt(i)
return tostring(i):reverse():gsub("%d%d%d", "%1,"):reverse():gsub("^,", "")
end
On the link above you may read details about implementation.

Print a table in pairs

I'm trying to use in pairs inside a function but it doesn't work, it just prints the first row (key). This is my code:
set = {1,2,3,4};
unset = {5,6,7,8};
function listen(ftype)
if (ftype == [[~]]) then
for num,str in pairs(set) do
return str;
end
end
if (ftype == [[$]]) then
for num,str in pairs(unset) do
return str;
end
end
end
print(listen([[~]])..[[ =:= ]]..listen([[$]]));
If I do something like this..
for num,str in pairs(unset) do
print(str);
end
It works like a charm. That’s exactly what I want but inside a function.
You can build your own iterator:
function double_pair(t1, t2)
local i = 0
return function() i = i + 1
return t1[i] and t1[i] .. " =:= " .. t2[i]
end
end
Then you can use it like this:
for str in double_pair(set, unset) do
print(str)
end
Output:
1 =:= 5
2 =:= 6
3 =:= 7
4 =:= 8
Note that you don't need semicolons to end your statement, unless the statements are in one line and you want to make them clear. And [[ =:= ]] is usually used to build long multi-line strings, normally we choose to use double quote " =:= " or single quote ' =:= '.
A function can't return multiple times. Putting an unconditional return inside a loop is nonsensical - it will never get to the second iteration of the loop.
You are essentially trying to return multiple values from the function. Lua supports that; you could, for instance, just return 1,2,3,4. For an unknown number of return values, you can build them up in a table and call unpack on it, like so:
function listen(ftype)
local result = {}
local num, str
if (ftype == [[~]]) then
for num,str in pairs(set) do
table.insert(result, str)
end
elseif (ftype == [[$]]) then
for num,str in pairs(unset) do
table.insert(result, str)
end
end
return unpack(result)
end
But since your results are already in a couple tables, it would be silly to reconstruct them that way. You can just unpack the originals:
function listen(ftype)
if (ftype == [[~]]) then
return unpack(set)
elseif (ftype == [[$]]) then
return unpack(unset)
end
end
Great. But when you put the function call into an expression like your print statement, it will only return the first value, which puts you back where you started.
So to print out your pairs, you can't avoid having to either:
1) do some iteration outside the function
or
2) do the actual printing inside the function
The cleanest solution is probably a custom iterator, as suggested by #YuHao.

Resources