Resetting package.loaded after a test in Lua - unit-testing

I am developing unit tests in Lua and a major part of these tests is changing package.loaded. This goes from
a = require "parser"
a.b = nil
to
package.loaded["checker"] = function() return true end
And the situation where I modify an entry of package.loaded that was loaded before the test started.
I would like to reset package.loaded after every test. But I have a hard time. I tried wrapping require, which solves some problems, but does not solve the problem of the second and the third example. How can I properly make a savepoint of package.loaded right before the tests and reload that savepoint just before I start a new test? Or just roll back package.loaded to the one just after the interpeter started?

This is actually the answer of #EgorSkriptunoff, but he placed it as a comment.
For me, it was good enough to just make a shallow-copy of bot the package.loaded and the _G. Shallow-copy can be found here: http://lua-users.org/wiki/CopyTable:
function shallowcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in pairs(orig) do
copy[orig_key] = orig_value
end
else -- number, string, boolean, etc
copy = orig
end
return copy
end
The actual testing then looks like this:
local packageLoadedCopy = shallowcopy(package.loaded)
local gCopy = shallowcopy(_G)
for _, test in ipairs(tests) do
-- Do the actual test
-- Clean up the environment
local markDeletion = {}
local markModify = {}
for name in pairs(package.loaded) do
if not packageLoadedCopy[name] then
table.insert(markDeletion, name)
elseif packageLoadedCopy[name] ~= package.loaded[name] then
table.insert(markModify, name)
end
end
for _, name in pairs(markDeletion) do
package.loaded[name] = nil
end
for _, name in pairs(markModify) do
package.loaded[name] = packageLoadedCopy[name]
end
markDeletion, markModify = {}, {}
for name in pairs(_G) do
if not gCopy[name] then
table.insert(markDeletion, name)
elseif _G[name] ~= gCopy[name] then
table.insert(markModify, name)
end
end
for _, name in pairs(markDeletion) do
_G[name] = nil
end
for _, name in pairs(markModify) do
_G[name] = gCopy[name]
end
end
Do note that simply
package.loaded = packageLoadedCopy
_G = gCopy
Does not work, and I am not sure why.

Related

Is there an alternative to an if statement in Lua?

I would like to know if there is a alternative to using a normal if statement in lua. For example, in java there are switch statements but I cant seem to find that in Lua
Lua lacks a C-style switch statement.
A simple version of a switch statement can be implemented using a table to map the case value to an action. This is very efficient in Lua since tables are hashed by key value which avoids repetitive if then ... elseif ... end statements.
action = {
[1] = function (x) print(1) end,
[2] = function (x) z = 5 end,
["nop"] = function (x) print(math.random()) end,
["my name"] = function (x) print("fred") end,
}
The frequently used pattern
local var; if condition then var = x else var = y end
can be shortened using an and-or "ternary" substitute if x is truthy:
local var = condition and x or y
if test == nil or test == false then return 0xBADEAFFE else return test end
Can be shorten up to...
return test or 0xBADEAFFEE
This works even where you dont can do: if ... then ... else ... end
Like in a function...
print(test or 0xBADEAFFE)
-- Output: 3135156222
...or fallback to a default if an argument is ommited...
function check(test)
local test = test or 0xBADEAFFE
return test
end
print(check())
-- Returns: 3135156222

Checking values across multiple location and returning a match only if the sources are unique

Lets say I have a list of Vendors: Asda, Tesco, Spar.
And I have a list of Sources (or suppliers in this analogy): Kellogg, Cadbury, Nestle, Johnsons, Pampers, Simple, etc. (there is a defined list of around 20).
Elsewhere in the flow of data. I am returning a result, which is Yes/No for each vendor, for multiple different things.
For example: Asda: ukOnly = "Yes"; Spar: ukOnly = "No" etc.
In this specific section, I am collating results.
Mostly it doesn't matter if the sources from the vendors overlap. So I can just say:
function concatResults(x) -- concats the result of "x" for each vendor
local pathAsda = *this is where I call the path location specific to Asda*
local pathTesco = *this is where I call the path location specific to Tesco*
local pathSpar = *this is where I call the path location specific to Spar*
if (pathAsda == "Yes" or pathTesco == "Yes" or pathSpar == "Yes") then
return "Yes"
else
return "No"
end
end
ukOnlyAgr = concatResults("ukOnly")
Great!
Now, say I want to do something more comple.
I want to know how many unique suppliers are providing chocolate and cereal. The example below is being used further up the process to produce a fact suppliesSweet, only if there is at least two sources (suppliers) involved and they must be at least supplying chocolate. This will be done for each vendor separately (please assume I have already defined my variables based on the input data:
if (suppliesChoc > 0 and suppliesCereal > 0 and numSources > 1) or (suppliesChoc > 1) then
then suppliesSweet = "Yes"
else suppliesSweet = "No"
end
Not a problem yet.
The issue comes when I try to aggregate these results across vendors (as I did before with ukOnly).
I already have the following function being used:
table.contains = function(t, value) -- Finds if "value" exists inside the table "t"
for index = 1, #t do
if t[index] == value then
return index
end
end
end
And was thinking of creating this:
table.overlap = function(t,g) -- Finds if tables "g" and "t" have any overlapping values
for i=1,#t do
if table.contains(g,t[i]) then
return true
else
return false
end
end
end
But I'm just not sure where to go from there.
You can assume I have already got a list of unique sources for each vendor and I don't mind if we're over restrictive. I.e. if any sources overlap between the two vendors, that would invalidate the entire result.
You can also assume I have each "fact": suppliesChoc, suppliesCereal, numSources and suppliesSweet for each vendor returned separately.
I believe your looking for the intersection of two sets.
https://en.wikipedia.org/wiki/Intersection_(set_theory)
One set being your vendor's suppliers and the other being the suppliers who supply sweets.
local vendors = {
Asda = {Kellogg = true, Cadbury = true, Nestle = true, Johnsons = true, Pampers = true, Simple = true},
Tesco = {Kellogg = true, Cadbury = true, Nestle = true, Johnsons = true},
Spar ={Nestle = true, Johnsons = true, Pampers = true, Simple = true}
}
function intersection(s1, s2)
local output = {}
for key in pairs(s1) do
output[#output + 1] = s2[key]
end
return output
end
local sweetSuppliers = {Kellogg = true, Cadbury = true, Nestle = true}
for name, suppliers in pairs(vendors) do
local result = intersection(sweetSuppliers, suppliers)
print(name .. " has " .. #result .. " sweets suppliers")
end
Here are some examples of a libraries for handling sets:
odkr's properset.lua
Windower's sets.lua
Both can give you an idea of how you can use sets to accomplish things like intersection, and much more

Lua : attempt to index a nil value; avoiding errors in conditionals

Let's say I have a giant table, something like:
test.test[1].testing.test.test_test
The table isn't guaranteed to exist. Neither are the tables containing it. I would like to just be able to do:
if test.test[1].testing.test.test_test then
print("it exits!")
end
But of course, this would give me an "Attempt to index ? (a nil value)" error if any of the indices aren't yet defined. So many times, I'll end up doing something like this:
if test then
if test.test then
if test.test[1] then
if test.test[1].testing then -- and so on
Is there a better, less-tedious way to accomplish this?
You can write a function that takes a list of keys to look up and does whatever action you want if it finds the entry. Here's an example:
function forindices(f, table, indices)
local entry = table
for _,idx in ipairs(indices) do
if type(entry) == 'table' and entry[idx] then
entry = entry[idx]
else
entry = nil
break
end
end
if entry then
f()
end
end
test = {test = {{testing = {test = {test_test = 5}}}}}
-- prints "it exists"
forindices(function () print("it exists") end,
test,
{"test", 1, "testing", "test", "test_test"})
-- doesn't print
forindices(function () print("it exists") end,
test,
{"test", 1, "nope", "test", "test_test"})
As an aside, the functional programming concept that solves this kind of problem is the Maybe monad. You could probably solve this with a Lua implementation of monads, though it wouldn't be very nice since there's no syntactic sugar for it.
You can avoid raising errors by setting an __index metamethod for nil:
debug.setmetatable(nil, { __index=function () end })
print(test.test[1].testing.test.test_test)
test = {test = {{testing = {test = {test_test = 5}}}}}
print(test.test[1].testing.test.test_test)
You also use an empty table:
debug.setmetatable(nil, { __index={} })

Boo Language Calculation on Web Drop Down List

The software we use LanDesk Service Desk uses Boo Language calculations to allow for dynamic windows.
I have a drop down list on a form that has one of two options. "Acute" and "Ambulatory". Based on which is chosen, one of two possible fields will no longer be hidden and will be set to mandatory. I have managed to get this to work, but I'm afraid if the number of options grows on future forms that the code will get a little wordy. Do any of you have any suggestions for alternatives. Thank you,
import System
static def GetAttributeValue(Request):
isAcuteHidden = true
isAcuteMandatory = false
isAmbulatoryHidden = true
isAmbulatoryMandatory = false
if Request._PharmacyType != null and Request._PharmacyType._Name == "Acute":
isAcuteHidden = false
isAcuteMandatory = true
elif Request._PharmacyType != null and Request._PharmacyType._Name == "Ambulatory":
isAmbulatoryHidden = false
isAmbulatoryMandatory = true
return String.Format(":SetHidden(_AcutePharmacy, {0});:SetMandatory(_AcutePharmacy, {1});:SetHidden(_AmbulatoryPharmacy, {2});:SetMandatory(_AmbulatoryPharmacy, {3});", isAcuteHidden, isAcuteMandatory, isAmbulatoryHidden, isAmbulatoryMandatory)
A few pointers:
It appears that for both pairs, the is?????Hidden and corresponding is?????Mandatory will always be opposite values. If so, you don't need two separate boolean variables to track them.
If you're going to end up with a lot of blocks that look basically like this one, the proper way to handle it in Boo is with a macro, like so:
macro HiddenOrRequiredValues:
ControlStrings = List[of String]()
ArgVarNames = List[of ReferenceExpression]()
counter = 0
for arg as ReferenceExpression in HiddenOrRequiredValues.Arguments:
argVariable = ReferenceExpression("is$(arg)Hidden")
yield [|$argVariable = true|]
yield [|
if Request._PharmacyType != null and Request._PharmacyType._Name == $(arg.ToString()):
$argVariable = false
|]
ControlStrings.Add(":SetHidden(_$(arg)Pharmacy, {$(counter)});:$(arg)Mandatory(_AcutePharmacy, {$(counter + 1)})")
ArgVarNames.Add(argVariable)
counter += 2
resultString = join(ControlStrings, '')
resultCmd as MethodInvocationExpression = [|String.Format($resultString)|]
for varName in ArgVarNames:
resultCmd.Arguments.Add([|$varName|])
resultCmd.Arguments.Add([|not $varName|])
yield [|return $resultCmd|]
Then the entirety of your method becomes:
static def GetAttributeValue(Request):
HiddenOrRequiredValues Acute, Ambulatory
It's more work to set it up, but once you have the macro in place, adding in more values becomes much, much easier. Feel free to modify the macro to suit your needs. This should be enough to get you started, though.

In Lua what does an if statement with only one argument mean?

I've been taught to program in Java. Lua is new to me and I've tried to do my homework but am not sure what an if statement of the following nature means.
The code is as follows:
local function getMinHeight(self)
local minHeight = 0
for i=1, minimizedLines do
local line = select(9+i, self:GetRegions())
**if(line) then
minHeight = minHeight + line:GetHeight() + 2.5
end**
end
if(minHeight == 0) then
minHeight = select(2, self:GetFont()) + 2.5
end
return minHeight
end
The if statement with the ** before and after is the part I'm not sure about. I don't know what the if statement is checking. If the line is not nil? If the line exists? If what?
In Lua, anything that's not nil or false evaluates to true in a conditional.
If the line is not nil? If the line exists?
Yes to both, because they kinda mean the same thing.
The select function returns a specific argument from it's list of arguments. It's used primarily with ..., but in this case it's being used to select the (i+9)th value returned by self:GetRegions. If there is no such value (for instance, if GetRegions only returns 5 values), then select returns nil.
if(line) is checking to see that it got a value back from select.
if(line) is being used as a shortcut for if(line ~= nil), since nil evaluates to false in a conditional.
It's worth pointing out that this shortcut is not always appropriate. For instance, we can iterate all the values in a table like this:
key, val = next(lookup)
while key do
print(key, val)
key, val = next(lookup, key)
end
However, this will fail if one of the table's keys happens be false:
lookup = {
["fred"] = "Fred Flinstone",
[true] = "True",
[false] = "False",
}
So we have to explicitly check for nil:
key, val = next(lookup)
while key ~= nil do
print(key, val)
key, val = next(lookup, key)
end
As Mud says, in lua anything other than nil and false is considered truthy. So the if above will pass as long as line is not nil or false.
That said, it worries me a bit the way you have phrased the question - "an if with only one argument".
First, it's not called "argument" - it's called expression. And in most languages is always one. In java, for example, you could do something like this:
bool found = false
...
if(found) {
...
}
ifs only care about the final value of the expression; they don't care whether it's a single variable or a more complex construction.