236 lines
7.9 KiB
Lua
236 lines
7.9 KiB
Lua
|
require("mineunit")
|
||
|
|
||
|
fixture("mesecons_fpga")
|
||
|
|
||
|
describe("FPGA logic", function()
|
||
|
local pos = {x = 0, y = 0, z = 0}
|
||
|
local pos_a = {x = -1, y = 0, z = 0}
|
||
|
local pos_b = {x = 0, y = 0, z = 1}
|
||
|
local pos_c = {x = 1, y = 0, z = 0}
|
||
|
local pos_d = {x = 0, y = 0, z = -1}
|
||
|
|
||
|
local fpga_set = false
|
||
|
|
||
|
local function set_fpga()
|
||
|
if not fpga_set then
|
||
|
world.set_node(pos, "mesecons_fpga:fpga0000")
|
||
|
fpga_set = true
|
||
|
end
|
||
|
end
|
||
|
before_each(set_fpga)
|
||
|
|
||
|
local function reset_world()
|
||
|
if fpga_set then
|
||
|
mesecon._test_reset()
|
||
|
world.clear()
|
||
|
fpga_set = false
|
||
|
end
|
||
|
end
|
||
|
after_each(reset_world)
|
||
|
|
||
|
local function test_program(inputs, outputs, program)
|
||
|
set_fpga()
|
||
|
|
||
|
mesecon._test_program_fpga(pos, program)
|
||
|
|
||
|
if inputs.a then mesecon._test_place(pos_a, "mesecons:test_receptor_on") end
|
||
|
if inputs.b then mesecon._test_place(pos_b, "mesecons:test_receptor_on") end
|
||
|
if inputs.c then mesecon._test_place(pos_c, "mesecons:test_receptor_on") end
|
||
|
if inputs.d then mesecon._test_place(pos_d, "mesecons:test_receptor_on") end
|
||
|
mineunit:execute_globalstep() -- Execute receptor_on actions
|
||
|
mineunit:execute_globalstep() -- Execute activate/change actions
|
||
|
|
||
|
local expected_name = "mesecons_fpga:fpga"
|
||
|
.. (outputs.d and 1 or 0) .. (outputs.c and 1 or 0)
|
||
|
.. (outputs.b and 1 or 0) .. (outputs.a and 1 or 0)
|
||
|
assert.equal(expected_name, world.get_node(pos).name)
|
||
|
|
||
|
reset_world()
|
||
|
end
|
||
|
|
||
|
it("operator and", function()
|
||
|
local prog = {{"A", "AND", "B", "C"}}
|
||
|
test_program({}, {}, prog)
|
||
|
test_program({a = true}, {}, prog)
|
||
|
test_program({b = true}, {}, prog)
|
||
|
test_program({a = true, b = true}, {c = true}, prog)
|
||
|
end)
|
||
|
|
||
|
it("operator or", function()
|
||
|
local prog = {{"A", "OR", "B", "C"}}
|
||
|
test_program({}, {}, prog)
|
||
|
test_program({a = true}, {c = true}, prog)
|
||
|
test_program({b = true}, {c = true}, prog)
|
||
|
test_program({a = true, b = true}, {c = true}, prog)
|
||
|
end)
|
||
|
|
||
|
it("operator not", function()
|
||
|
local prog = {{"NOT", "A", "B"}}
|
||
|
test_program({}, {b = true}, prog)
|
||
|
test_program({a = true}, {}, prog)
|
||
|
end)
|
||
|
|
||
|
it("operator xor", function()
|
||
|
local prog = {{"A", "XOR", "B", "C"}}
|
||
|
test_program({}, {}, prog)
|
||
|
test_program({a = true}, {c = true}, prog)
|
||
|
test_program({b = true}, {c = true}, prog)
|
||
|
test_program({a = true, b = true}, {}, prog)
|
||
|
end)
|
||
|
|
||
|
it("operator nand", function()
|
||
|
local prog = {{"A", "NAND", "B", "C"}}
|
||
|
test_program({}, {c = true}, prog)
|
||
|
test_program({a = true}, {c = true}, prog)
|
||
|
test_program({b = true}, {c = true}, prog)
|
||
|
test_program({a = true, b = true}, {}, prog)
|
||
|
end)
|
||
|
|
||
|
it("operator buf", function()
|
||
|
local prog = {{"=", "A", "B"}}
|
||
|
test_program({}, {}, prog)
|
||
|
test_program({a = true}, {b = true}, prog)
|
||
|
end)
|
||
|
|
||
|
it("operator xnor", function()
|
||
|
local prog = {{"A", "XNOR", "B", "C"}}
|
||
|
test_program({}, {c = true}, prog)
|
||
|
test_program({a = true}, {}, prog)
|
||
|
test_program({b = true}, {}, prog)
|
||
|
test_program({a = true, b = true}, {c = true}, prog)
|
||
|
end)
|
||
|
|
||
|
it("operator nor", function()
|
||
|
local prog = {{"A", "NOR", "B", "C"}}
|
||
|
test_program({}, {c = true}, prog)
|
||
|
test_program({a = true}, {}, prog)
|
||
|
test_program({b = true}, {}, prog)
|
||
|
test_program({a = true, b = true}, {}, prog)
|
||
|
end)
|
||
|
|
||
|
it("rejects duplicate operands", function()
|
||
|
test_program({a = true}, {}, {{"A", "OR", "A", "B"}})
|
||
|
test_program({a = true}, {}, {{"=", "A", "0"}, {"0", "OR", "0", "B"}})
|
||
|
end)
|
||
|
|
||
|
it("rejects unassigned memory operands", function()
|
||
|
test_program({a = true}, {}, {{"A", "OR", "0", "B"}})
|
||
|
test_program({a = true}, {}, {{"0", "OR", "A", "B"}})
|
||
|
end)
|
||
|
|
||
|
it("rejects double memory assignment", function()
|
||
|
test_program({a = true}, {}, {{"=", "A", "0"}, {"=", "A", "0"}, {"=", "0", "B"}})
|
||
|
end)
|
||
|
|
||
|
it("rejects assignment to memory operand", function()
|
||
|
test_program({a = true}, {}, {{"=", "A", "0"}, {"A", "OR", "0", "0"}, {"=", "0", "B"}})
|
||
|
end)
|
||
|
|
||
|
it("allows double port assignment", function()
|
||
|
test_program({a = true}, {b = true}, {{"NOT", "A", "B"}, {"=", "A", "B"}})
|
||
|
end)
|
||
|
|
||
|
it("allows assignment to port operand", function()
|
||
|
test_program({a = true}, {b = true}, {{"A", "OR", "B", "B"}})
|
||
|
end)
|
||
|
|
||
|
it("preserves initial pin states", function()
|
||
|
test_program({a = true}, {b = true}, {{"=", "A", "B"}, {"=", "B", "C"}})
|
||
|
end)
|
||
|
|
||
|
it("rejects binary operations with single operands", function()
|
||
|
test_program({a = true}, {}, {{"=", "A", "B"}, {" ", "OR", "A", "C"}})
|
||
|
test_program({a = true}, {}, {{"=", "A", "B"}, {"A", "OR", " ", "C"}})
|
||
|
end)
|
||
|
|
||
|
it("rejects unary operations with first operands", function()
|
||
|
test_program({a = true}, {}, {{"=", "A", "B"}, {"A", "=", " ", "C"}})
|
||
|
end)
|
||
|
|
||
|
it("rejects operations without destinations", function()
|
||
|
test_program({a = true}, {}, {{"=", "A", "B"}, {"=", "A", " "}})
|
||
|
end)
|
||
|
|
||
|
it("allows blank statements", function()
|
||
|
test_program({a = true}, {b = true, c = true}, {
|
||
|
{" ", " ", " ", " "},
|
||
|
{"=", "A", "B"},
|
||
|
{" ", " ", " ", " "},
|
||
|
{" ", " ", " ", " "},
|
||
|
{"=", "A", "C"},
|
||
|
})
|
||
|
end)
|
||
|
|
||
|
it("transmits output signals to adjacent nodes", function()
|
||
|
mesecon._test_program_fpga(pos, {
|
||
|
{"=", "A", "B"},
|
||
|
{"=", "A", "C"},
|
||
|
{"NOT", "A", "D"},
|
||
|
})
|
||
|
mesecon._test_place(pos_b, "mesecons:test_effector")
|
||
|
mesecon._test_place(pos_c, "mesecons:test_effector")
|
||
|
mesecon._test_place(pos_d, "mesecons:test_effector")
|
||
|
mineunit:execute_globalstep() -- Execute receptor_on actions
|
||
|
mineunit:execute_globalstep() -- Execute activate/change actions
|
||
|
|
||
|
-- Makes an object from the last three effector events in the list for use with assert.same.
|
||
|
-- This is necessary to ignore the ordering of events.
|
||
|
local function event_tester(list)
|
||
|
local o = {list[#list - 2], list[#list - 1], list[#list - 0]}
|
||
|
table.sort(o, function(a, b)
|
||
|
local fmt = "%s %d %d %d"
|
||
|
return fmt:format(a[1], a[2].x, a[2].y, a[2].z) < fmt:format(b[1], b[2].x, b[2].y, b[2].z)
|
||
|
end)
|
||
|
return o
|
||
|
end
|
||
|
|
||
|
mesecon._test_place(pos_a, "mesecons:test_receptor_on")
|
||
|
mineunit:execute_globalstep() -- Execute receptor_on action
|
||
|
mineunit:execute_globalstep() -- Execute activate/change actions
|
||
|
mineunit:execute_globalstep() -- Execute receptor_on/receptor_off actions
|
||
|
mineunit:execute_globalstep() -- Execute activate/deactivate/change actions
|
||
|
assert.equal("mesecons_fpga:fpga0110", world.get_node(pos).name)
|
||
|
assert.same(event_tester({{"on", pos_b}, {"on", pos_c}, {"off", pos_d}}), event_tester(mesecon._test_effector_events))
|
||
|
|
||
|
mesecon._test_dig(pos_a)
|
||
|
mineunit:execute_globalstep() -- Execute receptor_off action
|
||
|
mineunit:execute_globalstep() -- Execute deactivate/change actions
|
||
|
mineunit:execute_globalstep() -- Execute receptor_on/receptor_off actions
|
||
|
mineunit:execute_globalstep() -- Execute activate/deactivate/change actions
|
||
|
assert.equal("mesecons_fpga:fpga1000", world.get_node(pos).name)
|
||
|
assert.same(event_tester({{"off", pos_b}, {"off", pos_c}, {"on", pos_d}}), event_tester(mesecon._test_effector_events))
|
||
|
end)
|
||
|
|
||
|
it("considers past outputs in determining inputs", function()
|
||
|
-- Memory cell: Turning on A turns on C; turning on B turns off C.
|
||
|
mesecon._test_program_fpga(pos, {
|
||
|
{"A", "OR", "C", "0"},
|
||
|
{"B", "OR", "D", "1"},
|
||
|
{"NOT", "A", "2"},
|
||
|
{"NOT", "B", "3"},
|
||
|
{"0", "AND", "3", "C"},
|
||
|
{"1", "AND", "2", "D"},
|
||
|
})
|
||
|
|
||
|
mesecon._test_place(pos_a, "mesecons:test_receptor_on")
|
||
|
mineunit:execute_globalstep() -- Execute receptor_on actions
|
||
|
mineunit:execute_globalstep() -- Execute activate/change actions
|
||
|
assert.equal("mesecons_fpga:fpga0100", world.get_node(pos).name)
|
||
|
|
||
|
mesecon._test_dig(pos_a)
|
||
|
mineunit:execute_globalstep() -- Execute receptor_off actions
|
||
|
mineunit:execute_globalstep() -- Execute deactivate/change actions
|
||
|
assert.equal("mesecons_fpga:fpga0100", world.get_node(pos).name)
|
||
|
|
||
|
mesecon._test_place(pos_b, "mesecons:test_receptor_on")
|
||
|
mineunit:execute_globalstep() -- Execute receptor_on actions
|
||
|
mineunit:execute_globalstep() -- Execute activate/change actions
|
||
|
assert.equal("mesecons_fpga:fpga1000", world.get_node(pos).name)
|
||
|
|
||
|
mesecon._test_dig(pos_b)
|
||
|
mineunit:execute_globalstep() -- Execute receptor_off actions
|
||
|
mineunit:execute_globalstep() -- Execute deactivate/change actions
|
||
|
assert.equal("mesecons_fpga:fpga1000", world.get_node(pos).name)
|
||
|
end)
|
||
|
end)
|