initial commit
This commit is contained in:
commit
8d1cdf7514
6
.luarc.json
Normal file
6
.luarc.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json",
|
||||
"Lua.diagnostics.globals": [
|
||||
"peripheral"
|
||||
]
|
||||
}
|
3
README.md
Normal file
3
README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Fake CC peripherals
|
||||
|
||||
Create fake CC peripherals, useful for testing libraries which directly interact with peripherals
|
221
init.lua
Normal file
221
init.lua
Normal file
@ -0,0 +1,221 @@
|
||||
local PeripheralAPIFake = {}
|
||||
PeripheralAPIFake.__index = PeripheralAPIFake
|
||||
|
||||
local expect = require("cc.expect").expect
|
||||
|
||||
local function isPeripheral(peripheral)
|
||||
if type(peripheral) ~= "table" then return false end
|
||||
local meta = getmetatable(peripheral)
|
||||
|
||||
if type(meta) ~= "table" then return false end
|
||||
if meta.__name ~= "peripheral" then return false end
|
||||
if type(meta.name) ~= "string" then return false end
|
||||
if type(meta.type) ~= "string" then return false end
|
||||
if type(meta.types) ~= "table" then return false end
|
||||
for _, ty in ipairs(meta.types) do
|
||||
if not meta.types[ty] then return false end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- TODO: Add descriptive error messages to asserts
|
||||
local function expectPeripheral(index, peripheral)
|
||||
if not isPeripheral(peripheral) then
|
||||
error(("bad argument #%d (table is not a peripheral)"):format(index), 3)
|
||||
end
|
||||
end
|
||||
|
||||
--- Performs a deep clone of a table.
|
||||
local function cloneTable(t)
|
||||
local mt = getmetatable(t)
|
||||
setmetatable(t, nil)
|
||||
local cloned = {}
|
||||
for k, v in pairs(t) do
|
||||
if type(v) == "table" then
|
||||
cloned[k] = cloneTable(v)
|
||||
else
|
||||
cloned[k] = v
|
||||
end
|
||||
end
|
||||
setmetatable(t, mt)
|
||||
return cloned
|
||||
end
|
||||
|
||||
local function doesTableContain(t, value)
|
||||
for _, v in pairs(t) do
|
||||
if v == value then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function getPeripheralName(peripheral)
|
||||
return getmetatable(peripheral).name
|
||||
end
|
||||
|
||||
local function getPeripheralTypes(peripheral)
|
||||
return getmetatable(peripheral).types
|
||||
end
|
||||
|
||||
function PeripheralAPIFake.new(peripherals)
|
||||
expect(1, peripherals, "table", "nil")
|
||||
|
||||
local self = setmetatable({}, PeripheralAPIFake)
|
||||
self.peripherals = {}
|
||||
|
||||
if peripherals then
|
||||
for _, peripheral in ipairs(peripherals) do
|
||||
self:addPeripehral(peripheral)
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function PeripheralAPIFake:newExternalAPI()
|
||||
local api = {}
|
||||
|
||||
function api.call(name, method, ...)
|
||||
expect(1, name, "string")
|
||||
expect(2, method, "string")
|
||||
local peripheral = self.peripherals[name]
|
||||
if not peripheral then return end
|
||||
local func = peripheral[method]
|
||||
if type(func) ~= "function" then
|
||||
error(("No such method %s"):format(method), 2)
|
||||
end
|
||||
return func(...)
|
||||
end
|
||||
|
||||
function api.find(ty, filter)
|
||||
expect(1, ty, "string")
|
||||
expect(2, filter, "function", "nil")
|
||||
local peripherals = {}
|
||||
for name, peripheral in pairs(self.peripherals) do
|
||||
local types = getPeripheralTypes(peripheral)
|
||||
if doesTableContain(types, ty) then
|
||||
local wrapped = api.wrap(name)
|
||||
if not filter or filter(name, wrapped) then
|
||||
table.insert(peripherals, wrapped)
|
||||
end
|
||||
end
|
||||
end
|
||||
return peripherals
|
||||
end
|
||||
|
||||
function api.getMethods(name)
|
||||
expect(1, name, "string")
|
||||
local peripheral = self:getPeripheral(name)
|
||||
if not peripheral then return end
|
||||
|
||||
local methods = {}
|
||||
for methodName, method in pairs(peripheral) do
|
||||
if type(method) == "function" then
|
||||
table.insert(methods, methodName)
|
||||
end
|
||||
end
|
||||
return methods
|
||||
end
|
||||
|
||||
function api.getName(peripheral)
|
||||
expect(1, peripheral, "table", "string")
|
||||
expectPeripheral(1, peripheral)
|
||||
return getPeripheralName(peripheral)
|
||||
end
|
||||
|
||||
function api.getNames()
|
||||
return self:listPeripherals()
|
||||
end
|
||||
|
||||
function api.getType(peripheral)
|
||||
expect(1, peripheral, "table", "string")
|
||||
if type(peripheral) == "table" then
|
||||
expectPeripheral(1, peripheral)
|
||||
end
|
||||
local p = self:getPeripheral(peripheral)
|
||||
return table.unpack(getPeripheralTypes(p))
|
||||
end
|
||||
|
||||
function api.hasType(peripheral, peripheral_type)
|
||||
expect(1, peripheral, "table", "string")
|
||||
if type(peripheral) == "table" then
|
||||
expectPeripheral(1, peripheral)
|
||||
end
|
||||
expect(2, peripheral_type, "string")
|
||||
local p = self:getPeripheral(peripheral)
|
||||
if not p then return end
|
||||
|
||||
local types = getPeripheralTypes(peripheral)
|
||||
return doesTableContain(types, peripheral_type)
|
||||
end
|
||||
|
||||
function api.isPresent(name)
|
||||
expect(1, name, "string")
|
||||
return self:hasPeripheral(name)
|
||||
end
|
||||
|
||||
function api.wrap(name)
|
||||
expect(1, name, "string")
|
||||
local peripheral = self.peripherals[name]
|
||||
if not peripheral then return end
|
||||
return cloneTable(peripheral)
|
||||
end
|
||||
|
||||
return api
|
||||
end
|
||||
|
||||
function PeripheralAPIFake:addPeripehral(peripheral)
|
||||
expect(1, peripheral, "table")
|
||||
expectPeripheral(1, peripheral)
|
||||
|
||||
local name = getPeripheralName(peripheral)
|
||||
assert(not self.peripherals[name], "Expected peripheral name to be unique, it's not unique")
|
||||
assert(not self:hasPeripheral(peripheral), "Peripheral already added to system")
|
||||
|
||||
self.peripherals[name] = peripheral
|
||||
end
|
||||
|
||||
function PeripheralAPIFake:removePeripheral(peripheral)
|
||||
expect(1, peripheral, "table", "string")
|
||||
|
||||
if type(peripheral) == "string" then
|
||||
self.peripherals[peripheral] = nil
|
||||
else
|
||||
expectPeripheral(1, peripheral)
|
||||
local name = getPeripheralName(peripheral)
|
||||
self.peripherals[name] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function PeripheralAPIFake:hasPeripheral(peripheral)
|
||||
expect(1, peripheral, "table", "string")
|
||||
return self:getPeripheral(peripheral) ~= nil
|
||||
end
|
||||
|
||||
function PeripheralAPIFake:getPeripheral(peripheral)
|
||||
expect(1, peripheral, "table", "string")
|
||||
|
||||
if type(peripheral) == "string" then
|
||||
return self.peripherals[peripheral]
|
||||
else
|
||||
expectPeripheral(1, peripheral)
|
||||
local name = getPeripheralName(peripheral)
|
||||
return self.peripherals[name]
|
||||
end
|
||||
end
|
||||
|
||||
function PeripheralAPIFake:getPeripheralName(peripheral)
|
||||
return getPeripheralName(self:getPeripheral(peripheral))
|
||||
end
|
||||
|
||||
function PeripheralAPIFake:listPeripherals()
|
||||
local names = {}
|
||||
for _, peripheral in pairs(self.peripherals) do
|
||||
table.insert(names, getPeripheralName(peripheral))
|
||||
end
|
||||
return names
|
||||
end
|
||||
|
||||
return PeripheralAPIFake
|
195
init.test.lua
Normal file
195
init.test.lua
Normal file
@ -0,0 +1,195 @@
|
||||
local lust = require("lust").nocolor()
|
||||
local PeripheralAPIMock = require("init")
|
||||
local describe, it, expect = lust.describe, lust.it, lust.expect
|
||||
|
||||
local function newPeripheralStub(name, types, methodNames)
|
||||
local peripheral = {}
|
||||
local metaTypes = {}
|
||||
for i, ty in ipairs(types) do
|
||||
metaTypes[i] = ty
|
||||
metaTypes[ty] = true
|
||||
end
|
||||
|
||||
setmetatable(peripheral, {
|
||||
__name = "peripheral",
|
||||
name = name,
|
||||
type = types[1],
|
||||
types = metaTypes
|
||||
})
|
||||
|
||||
for _, methodName in pairs(methodNames) do
|
||||
peripheral[methodName] = function() end
|
||||
end
|
||||
|
||||
return peripheral
|
||||
end
|
||||
|
||||
local function newInventoryStub(type, id)
|
||||
return newPeripheralStub(("%s_%d"):format(type, id), {type, "inventory"}, {
|
||||
"size",
|
||||
"list",
|
||||
"getItemDetail",
|
||||
"getItemLimit",
|
||||
"pushItems",
|
||||
"pullItems"
|
||||
})
|
||||
end
|
||||
|
||||
local function pprint(...)
|
||||
local pretty = require("cc.pretty")
|
||||
pretty.print(pretty.pretty(...))
|
||||
end
|
||||
|
||||
describe("peripherals mock", function()
|
||||
it("initialize with no peripherals by default", function()
|
||||
local peripheral = PeripheralAPIMock.new()
|
||||
expect(peripheral:listPeripherals()).to.equal{}
|
||||
end)
|
||||
|
||||
it("initialize with initial peripherals", function()
|
||||
local chest1 = newInventoryStub("minecraft:chest", 1)
|
||||
local chest2 = newInventoryStub("minecraft:chest", 2)
|
||||
local peripheral = PeripheralAPIMock.new{ chest1, chest2 }
|
||||
expect(peripheral:listPeripherals()).to.equal{
|
||||
"minecraft:chest_1",
|
||||
"minecraft:chest_2",
|
||||
}
|
||||
end)
|
||||
|
||||
it("throw error if given table is not a valid peripheral", function()
|
||||
local chest = { }
|
||||
expect(function()
|
||||
PeripheralAPIMock.new{ chest }
|
||||
end).to.fail()
|
||||
end)
|
||||
|
||||
it("check if peripheral is added", function()
|
||||
local chest = newInventoryStub("minecraft:chest", 1)
|
||||
local peripheral = PeripheralAPIMock.new{ chest }
|
||||
expect(peripheral:hasPeripheral(chest)).to.be.truthy()
|
||||
end)
|
||||
|
||||
it("check if peripheral is removed", function()
|
||||
local chest = newInventoryStub("minecraft:chest", 1)
|
||||
local peripheral = PeripheralAPIMock.new{ chest }
|
||||
expect(peripheral:hasPeripheral(chest)).to.be.truthy()
|
||||
peripheral:removePeripheral(chest)
|
||||
expect(peripheral:hasPeripheral(chest)).to_not.be.truthy()
|
||||
end)
|
||||
|
||||
it("listPeripherals returns a new list every time", function()
|
||||
local peripheral = PeripheralAPIMock.new()
|
||||
expect(peripheral:listPeripherals()).to_not.be(peripheral:listPeripherals())
|
||||
end)
|
||||
|
||||
describe("external api", function()
|
||||
local chest1, chest2, api
|
||||
lust.before(function()
|
||||
chest1 = newInventoryStub("minecraft:chest", 1)
|
||||
chest2 = newInventoryStub("minecraft:chest", 2)
|
||||
local peripheral = PeripheralAPIMock.new{ chest1, chest2 }
|
||||
api = peripheral:newExternalAPI()
|
||||
end)
|
||||
|
||||
it("has the same methods as the real api", function()
|
||||
for methodName in pairs(peripheral) do
|
||||
assert(type(api[methodName]) == "function", ("Missing method %s"):format(methodName))
|
||||
end
|
||||
expect(#api).to.be(#peripheral)
|
||||
end)
|
||||
|
||||
it("using .call and .wrap, invoke the same function", function()
|
||||
local spy = lust.spy(chest1, "size")
|
||||
api.call("minecraft:chest_1", "size")
|
||||
expect(#spy).to.be(1)
|
||||
api.wrap("minecraft:chest_1").size()
|
||||
expect(#spy).to.be(2)
|
||||
end)
|
||||
|
||||
it(".call dosen't supply self to methods", function()
|
||||
local spy = lust.spy(chest1, "size")
|
||||
api.call("minecraft:chest_1", "size", 1, 2, 3)
|
||||
expect(spy).to.equal{{1, 2, 3}}
|
||||
end)
|
||||
|
||||
it(".call returns nil if peripheral was not found", function()
|
||||
expect(api.call("foobar", "size")).to.be(nil)
|
||||
end)
|
||||
|
||||
it(".call throws error if peripheral dosen't have requested method", function()
|
||||
expect(function()
|
||||
api.call("minecraft:chest_1", "foobar")
|
||||
end).to.fail()
|
||||
end)
|
||||
|
||||
it(".wrap returns a new table each time, but have the same contents", function()
|
||||
local name = "minecraft:chest_1"
|
||||
expect(api.wrap(name)).to.equal(api.wrap(name))
|
||||
expect(api.wrap(name)).to_not.be(api.wrap(name))
|
||||
end)
|
||||
|
||||
it(".wrap returns nil if peripheral was not found", function()
|
||||
expect(api.wrap("foobar")).to.be(nil)
|
||||
end)
|
||||
|
||||
it("check if peripheral exists if .isPresent", function()
|
||||
expect(api.isPresent("minecraft:chest_1")).to.be(true)
|
||||
expect(api.isPresent("foobar")).to.be(false)
|
||||
end)
|
||||
|
||||
it(".getType throws error if given table is not a peripheral", function()
|
||||
expect(function ()
|
||||
api.getType({})
|
||||
end).to.fail()
|
||||
|
||||
expect(function ()
|
||||
api.getType(chest1)
|
||||
end).to_not.fail()
|
||||
end)
|
||||
|
||||
it(".getType returns multiple types if that peripheral has it", function()
|
||||
local types = {api.getType("minecraft:chest_1")}
|
||||
expect(types).to.equal{"minecraft:chest", "inventory"}
|
||||
end)
|
||||
|
||||
it(".getNames returns a new table each time, but have the same contents", function()
|
||||
expect(api.getNames()).to.equal(api.getNames())
|
||||
expect(api.getNames()).to_not.be(api.getNames())
|
||||
end)
|
||||
|
||||
it(".getName throws error if table is not a valid peripheral", function()
|
||||
expect(function()
|
||||
api.getName({})
|
||||
end).to.fail()
|
||||
|
||||
expect(function()
|
||||
api.getName(chest1)
|
||||
end).to_not.fail()
|
||||
end)
|
||||
|
||||
it(".getMethods returns nil if peripheral is not found", function()
|
||||
expect(api.getMethods("foobar")).to.be(nil)
|
||||
end)
|
||||
|
||||
it(".hasType returns nil if peripheral is not found", function()
|
||||
expect(api.hasType("foobar", "inventory")).to.be(nil)
|
||||
end)
|
||||
|
||||
it(".hasType returns true if peripheral has target type", function()
|
||||
expect(api.hasType(chest1, "inventory")).to.be(true)
|
||||
end)
|
||||
|
||||
it(".find returns a list of wrapped peripherals", function()
|
||||
local peripherals = api.find("inventory")
|
||||
expect(peripherals).to.equal{ chest1, chest2 }
|
||||
end)
|
||||
|
||||
it(".find with filter returns a list of wrapped peripherals", function()
|
||||
local peripherals = api.find("inventory", function(_, wrapped)
|
||||
return wrapped.size ~= nil
|
||||
end)
|
||||
expect(peripherals).to.equal{ chest1, chest2 }
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
231
lust.lua
Normal file
231
lust.lua
Normal file
@ -0,0 +1,231 @@
|
||||
-- lust v0.1.0 - Lua test framework
|
||||
-- https://github.com/bjornbytes/lust
|
||||
-- MIT LICENSE
|
||||
|
||||
local lust = {}
|
||||
lust.level = 0
|
||||
lust.passes = 0
|
||||
lust.errors = 0
|
||||
lust.befores = {}
|
||||
lust.afters = {}
|
||||
|
||||
local red = string.char(27) .. '[31m'
|
||||
local green = string.char(27) .. '[32m'
|
||||
local normal = string.char(27) .. '[0m'
|
||||
local function indent(level) return string.rep('\t', level or lust.level) end
|
||||
|
||||
function lust.nocolor()
|
||||
red, green, normal = '', '', ''
|
||||
return lust
|
||||
end
|
||||
|
||||
function lust.describe(name, fn)
|
||||
print(indent() .. name)
|
||||
lust.level = lust.level + 1
|
||||
fn()
|
||||
lust.befores[lust.level] = {}
|
||||
lust.afters[lust.level] = {}
|
||||
lust.level = lust.level - 1
|
||||
end
|
||||
|
||||
function lust.it(name, fn)
|
||||
for level = 1, lust.level do
|
||||
if lust.befores[level] then
|
||||
for i = 1, #lust.befores[level] do
|
||||
lust.befores[level][i](name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local success, err = pcall(fn)
|
||||
if success then lust.passes = lust.passes + 1
|
||||
else lust.errors = lust.errors + 1 end
|
||||
local color = success and green or red
|
||||
local label = success and 'PASS' or 'FAIL'
|
||||
print(indent() .. color .. label .. normal .. ' ' .. name)
|
||||
if err then
|
||||
print(indent(lust.level + 1) .. red .. err .. normal)
|
||||
end
|
||||
|
||||
for level = 1, lust.level do
|
||||
if lust.afters[level] then
|
||||
for i = 1, #lust.afters[level] do
|
||||
lust.afters[level][i](name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function lust.before(fn)
|
||||
lust.befores[lust.level] = lust.befores[lust.level] or {}
|
||||
table.insert(lust.befores[lust.level], fn)
|
||||
end
|
||||
|
||||
function lust.after(fn)
|
||||
lust.afters[lust.level] = lust.afters[lust.level] or {}
|
||||
table.insert(lust.afters[lust.level], fn)
|
||||
end
|
||||
|
||||
-- Assertions
|
||||
local function isa(v, x)
|
||||
if type(x) == 'string' then
|
||||
return type(v) == x,
|
||||
'expected ' .. tostring(v) .. ' to be a ' .. x,
|
||||
'expected ' .. tostring(v) .. ' to not be a ' .. x
|
||||
elseif type(x) == 'table' then
|
||||
if type(v) ~= 'table' then
|
||||
return false,
|
||||
'expected ' .. tostring(v) .. ' to be a ' .. tostring(x),
|
||||
'expected ' .. tostring(v) .. ' to not be a ' .. tostring(x)
|
||||
end
|
||||
|
||||
local seen = {}
|
||||
local meta = v
|
||||
while meta and not seen[meta] do
|
||||
if meta == x then return true end
|
||||
seen[meta] = true
|
||||
meta = getmetatable(meta) and getmetatable(meta).__index
|
||||
end
|
||||
|
||||
return false,
|
||||
'expected ' .. tostring(v) .. ' to be a ' .. tostring(x),
|
||||
'expected ' .. tostring(v) .. ' to not be a ' .. tostring(x)
|
||||
end
|
||||
|
||||
error('invalid type ' .. tostring(x))
|
||||
end
|
||||
|
||||
local function has(t, x)
|
||||
for k, v in pairs(t) do
|
||||
if v == x then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function strict_eq(t1, t2)
|
||||
if type(t1) ~= type(t2) then return false end
|
||||
if type(t1) ~= 'table' then return t1 == t2 end
|
||||
for k, _ in pairs(t1) do
|
||||
if not strict_eq(t1[k], t2[k]) then return false end
|
||||
end
|
||||
for k, _ in pairs(t2) do
|
||||
if not strict_eq(t2[k], t1[k]) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local paths = {
|
||||
[''] = { 'to', 'to_not' },
|
||||
to = { 'have', 'equal', 'be', 'exist', 'fail' },
|
||||
to_not = { 'have', 'equal', 'be', 'exist', 'fail', chain = function(a) a.negate = not a.negate end },
|
||||
a = { test = isa },
|
||||
an = { test = isa },
|
||||
be = { 'a', 'an', 'truthy',
|
||||
test = function(v, x)
|
||||
return v == x,
|
||||
'expected ' .. tostring(v) .. ' and ' .. tostring(x) .. ' to be equal',
|
||||
'expected ' .. tostring(v) .. ' and ' .. tostring(x) .. ' to not be equal'
|
||||
end
|
||||
},
|
||||
exist = {
|
||||
test = function(v)
|
||||
return v ~= nil,
|
||||
'expected ' .. tostring(v) .. ' to exist',
|
||||
'expected ' .. tostring(v) .. ' to not exist'
|
||||
end
|
||||
},
|
||||
truthy = {
|
||||
test = function(v)
|
||||
return v,
|
||||
'expected ' .. tostring(v) .. ' to be truthy',
|
||||
'expected ' .. tostring(v) .. ' to not be truthy'
|
||||
end
|
||||
},
|
||||
equal = {
|
||||
test = function(v, x)
|
||||
return strict_eq(v, x),
|
||||
'expected ' .. tostring(v) .. ' and ' .. tostring(x) .. ' to be exactly equal',
|
||||
'expected ' .. tostring(v) .. ' and ' .. tostring(x) .. ' to not be exactly equal'
|
||||
end
|
||||
},
|
||||
have = {
|
||||
test = function(v, x)
|
||||
if type(v) ~= 'table' then
|
||||
error('expected ' .. tostring(v) .. ' to be a table')
|
||||
end
|
||||
|
||||
return has(v, x),
|
||||
'expected ' .. tostring(v) .. ' to contain ' .. tostring(x),
|
||||
'expected ' .. tostring(v) .. ' to not contain ' .. tostring(x)
|
||||
end
|
||||
},
|
||||
fail = {
|
||||
test = function(v)
|
||||
return not pcall(v),
|
||||
'expected ' .. tostring(v) .. ' to fail',
|
||||
'expected ' .. tostring(v) .. ' to not fail'
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
function lust.expect(v)
|
||||
local assertion = {}
|
||||
assertion.val = v
|
||||
assertion.action = ''
|
||||
assertion.negate = false
|
||||
|
||||
setmetatable(assertion, {
|
||||
__index = function(t, k)
|
||||
if has(paths[rawget(t, 'action')], k) then
|
||||
rawset(t, 'action', k)
|
||||
local chain = paths[rawget(t, 'action')].chain
|
||||
if chain then chain(t) end
|
||||
return t
|
||||
end
|
||||
return rawget(t, k)
|
||||
end,
|
||||
__call = function(t, ...)
|
||||
if paths[t.action].test then
|
||||
local res, err, nerr = paths[t.action].test(t.val, ...)
|
||||
if assertion.negate then
|
||||
res = not res
|
||||
err = nerr or err
|
||||
end
|
||||
if not res then
|
||||
error(err or 'unknown failure', 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
return assertion
|
||||
end
|
||||
|
||||
function lust.spy(target, name, run)
|
||||
local spy = {}
|
||||
local subject
|
||||
|
||||
local function capture(...)
|
||||
table.insert(spy, {...})
|
||||
return subject(...)
|
||||
end
|
||||
|
||||
if type(target) == 'table' then
|
||||
subject = target[name]
|
||||
target[name] = capture
|
||||
else
|
||||
run = name
|
||||
subject = target or function() end
|
||||
end
|
||||
|
||||
setmetatable(spy, {__call = function(_, ...) return capture(...) end})
|
||||
|
||||
if run then run() end
|
||||
|
||||
return spy
|
||||
end
|
||||
|
||||
lust.test = lust.it
|
||||
lust.paths = paths
|
||||
|
||||
return lust
|
Loading…
Reference in New Issue
Block a user