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