mirror of
https://github.com/minetest-mods/mesecons.git
synced 2024-11-16 23:30:34 +01:00
Add FPGA tests
This commit is contained in:
parent
60e03e98a5
commit
6cecec7612
3
.github/workflows/check-release.yml
vendored
3
.github/workflows/check-release.yml
vendored
|
@ -33,3 +33,6 @@ jobs:
|
||||||
- name: run mesecons_mvps tests
|
- name: run mesecons_mvps tests
|
||||||
working-directory: ./mesecons_mvps/
|
working-directory: ./mesecons_mvps/
|
||||||
run: $HOME/.luarocks/bin/mineunit -q
|
run: $HOME/.luarocks/bin/mineunit -q
|
||||||
|
- name: run mesecons_fpga tests
|
||||||
|
working-directory: ./mesecons_fpga/
|
||||||
|
run: $HOME/.luarocks/bin/mineunit -q
|
||||||
|
|
|
@ -36,9 +36,13 @@ files["mesecons/actionqueue.lua"] = {
|
||||||
}
|
}
|
||||||
|
|
||||||
files["*/spec/**/*.lua"] = {
|
files["*/spec/**/*.lua"] = {
|
||||||
read_globals = {"assert", "fixture", "mineunit", "sourcefile", "world"},
|
read_globals = {"assert", "fixture", "mineunit", "Player", "sourcefile", "world"},
|
||||||
}
|
}
|
||||||
|
|
||||||
files["mesecons/spec/fixtures/voxelmanip.lua"] = {
|
files["mesecons/spec/fixtures/voxelmanip.lua"] = {
|
||||||
globals = {"minetest.get_voxel_manip"},
|
globals = {"minetest.get_voxel_manip"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
files["mesecons_fpga/spec/fixtures/mesecons_fpga.lua"] = {
|
||||||
|
globals = {"minetest.register_on_player_receive_fields"},
|
||||||
|
}
|
||||||
|
|
57
mesecons_fpga/spec/fixtures/mesecons_fpga.lua
vendored
Normal file
57
mesecons_fpga/spec/fixtures/mesecons_fpga.lua
vendored
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
mineunit("player")
|
||||||
|
|
||||||
|
fixture("mesecons")
|
||||||
|
|
||||||
|
local registered_on_player_receive_fields = {}
|
||||||
|
local old_register_on_player_receive_fields = minetest.register_on_player_receive_fields
|
||||||
|
function minetest.register_on_player_receive_fields(func)
|
||||||
|
old_register_on_player_receive_fields(func)
|
||||||
|
table.insert(registered_on_player_receive_fields, func)
|
||||||
|
end
|
||||||
|
|
||||||
|
mineunit:set_current_modname("mesecons_fpga")
|
||||||
|
mineunit:set_modpath("mesecons_fpga", "../mesecons_fpga")
|
||||||
|
sourcefile("../mesecons_fpga/init")
|
||||||
|
|
||||||
|
local fpga_user = Player("mesecons_fpga_user")
|
||||||
|
|
||||||
|
function mesecon._test_program_fpga(pos, program)
|
||||||
|
local node = minetest.get_node(pos)
|
||||||
|
assert.equal("mesecons_fpga:fpga", node.name:sub(1, 18))
|
||||||
|
|
||||||
|
local fields = {program = true}
|
||||||
|
for i, instr in ipairs(program) do
|
||||||
|
local op1, act, op2, dst
|
||||||
|
if #instr == 3 then
|
||||||
|
act, op2, dst = unpack(instr)
|
||||||
|
else
|
||||||
|
assert.equal(4, #instr)
|
||||||
|
op1, act, op2, dst = unpack(instr)
|
||||||
|
end
|
||||||
|
fields[i .. "op1"] = op1
|
||||||
|
fields[i .. "act"] = (" "):rep(4 - #act) .. act
|
||||||
|
fields[i .. "op2"] = op2
|
||||||
|
fields[i .. "dst"] = dst
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.registered_nodes[node.name].on_rightclick(pos, node, fpga_user)
|
||||||
|
|
||||||
|
for _, func in ipairs(registered_on_player_receive_fields) do
|
||||||
|
if func(fpga_user, "mesecons:fpga", fields) then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mesecon._test_copy_fpga_program(pos)
|
||||||
|
fpga_user:get_inventory():set_stack("main", 1, "mesecons_fpga:programmer")
|
||||||
|
local pt = {type = "node", under = vector.new(pos), above = vector.offset(pos, 0, 1, 0)}
|
||||||
|
fpga_user:do_place(pt)
|
||||||
|
return fpga_user:get_wielded_item()
|
||||||
|
end
|
||||||
|
|
||||||
|
function mesecon._test_paste_fpga_program(pos, tool)
|
||||||
|
fpga_user:get_inventory():set_stack("main", 1, tool)
|
||||||
|
local pt = {type = "node", under = vector.new(pos), above = vector.offset(pos, 0, 1, 0)}
|
||||||
|
fpga_user:do_use(pt)
|
||||||
|
end
|
6
mesecons_fpga/spec/fixtures/screwdriver.lua
vendored
Normal file
6
mesecons_fpga/spec/fixtures/screwdriver.lua
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
mineunit:set_current_modname("screwdriver")
|
||||||
|
|
||||||
|
screwdriver = {}
|
||||||
|
|
||||||
|
screwdriver.ROTATE_FACE = 1
|
||||||
|
screwdriver.ROTATE_AXIS = 2
|
134
mesecons_fpga/spec/helper_spec.lua
Normal file
134
mesecons_fpga/spec/helper_spec.lua
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
require("mineunit")
|
||||||
|
|
||||||
|
fixture("mesecons_fpga")
|
||||||
|
fixture("screwdriver")
|
||||||
|
|
||||||
|
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}
|
||||||
|
|
||||||
|
describe("FPGA rotation", function()
|
||||||
|
before_each(function()
|
||||||
|
world.set_node(pos, "mesecons_fpga:fpga0000")
|
||||||
|
end)
|
||||||
|
|
||||||
|
after_each(function()
|
||||||
|
mesecon._test_reset()
|
||||||
|
world.clear()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("rotates I/O operands clockwise", function()
|
||||||
|
mesecon._test_program_fpga(pos, {{"A", "OR", "B", "C"}})
|
||||||
|
|
||||||
|
local node = world.get_node(pos)
|
||||||
|
minetest.registered_nodes[node.name].on_rotate(pos, node, nil, screwdriver.ROTATE_FACE)
|
||||||
|
|
||||||
|
mesecon._test_place(pos_b, "mesecons:test_receptor_on")
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
assert.equal("mesecons_fpga:fpga1000", world.get_node(pos).name)
|
||||||
|
|
||||||
|
mesecon._test_dig(pos_b)
|
||||||
|
mesecon._test_place(pos_c, "mesecons:test_receptor_on")
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
assert.equal("mesecons_fpga:fpga1000", world.get_node(pos).name)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("rotates I/O operands counterclockwise", function()
|
||||||
|
mesecon._test_program_fpga(pos, {{"A", "OR", "B", "C"}})
|
||||||
|
|
||||||
|
local node = world.get_node(pos)
|
||||||
|
minetest.registered_nodes[node.name].on_rotate(pos, node, nil, screwdriver.ROTATE_AXIS)
|
||||||
|
|
||||||
|
mesecon._test_place(pos_d, "mesecons:test_receptor_on")
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
assert.equal("mesecons_fpga:fpga0010", world.get_node(pos).name)
|
||||||
|
|
||||||
|
mesecon._test_dig(pos_d)
|
||||||
|
mesecon._test_place(pos_a, "mesecons:test_receptor_on")
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
assert.equal("mesecons_fpga:fpga0010", world.get_node(pos).name)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("updates ports", function()
|
||||||
|
mesecon._test_program_fpga(pos, {{"NOT", "A", "B"}})
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
assert.equal("mesecons_fpga:fpga0010", world.get_node(pos).name)
|
||||||
|
|
||||||
|
local node = world.get_node(pos)
|
||||||
|
minetest.registered_nodes[node.name].on_rotate(pos, node, nil, screwdriver.ROTATE_AXIS)
|
||||||
|
assert.equal("mesecons_fpga:fpga0001", world.get_node(pos).name)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- mineunit does not support deprecated ItemStack:get_metadata()
|
||||||
|
pending("FPGA programmer", function()
|
||||||
|
local pos2 = {x = 10, y = 0, z = 0}
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
world.set_node(pos, "mesecons_fpga:fpga0000")
|
||||||
|
world.set_node(pos2, "mesecons_fpga:fpga0000")
|
||||||
|
end)
|
||||||
|
|
||||||
|
after_each(function()
|
||||||
|
mesecon._test_reset()
|
||||||
|
world.clear()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("transfers instructions", function()
|
||||||
|
mesecon._test_program_fpga(pos2, {{"NOT", "A", "B"}})
|
||||||
|
|
||||||
|
mesecon._test_paste_fpga_program(pos, mesecon._test_copy_fpga_program(pos2))
|
||||||
|
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
assert.equal("mesecons_fpga:fpga0010", world.get_node(pos).name)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("does not copy from new FPGAs", function()
|
||||||
|
mesecon._test_program_fpga(pos, {{"NOT", "A", "B"}})
|
||||||
|
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
|
||||||
|
mesecon._test_paste_fpga_program(pos, mesecon._test_copy_fpga_program(pos2))
|
||||||
|
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
assert.equal("mesecons_fpga:fpga0010", world.get_node(pos).name)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("does not copy from cleared FPGAs", function()
|
||||||
|
mesecon._test_program_fpga(pos, {{"NOT", "A", "B"}})
|
||||||
|
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
|
||||||
|
mesecon._test_program_fpga(pos2, {{"=", "A", "B"}})
|
||||||
|
mesecon._test_program_fpga(pos2, {})
|
||||||
|
mesecon._test_paste_fpga_program(pos, mesecon._test_copy_fpga_program(pos2))
|
||||||
|
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
assert.equal("mesecons_fpga:fpga0010", world.get_node(pos).name)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("does not copy from non-FPGA nodes", function()
|
||||||
|
mesecon._test_program_fpga(pos, {{"NOT", "A", "B"}})
|
||||||
|
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
|
||||||
|
mesecon._test_paste_fpga_program(pos, mesecon._test_copy_fpga_program(vector.add(pos2, 1)))
|
||||||
|
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
assert.equal("mesecons_fpga:fpga0010", world.get_node(pos).name)
|
||||||
|
end)
|
||||||
|
end)
|
195
mesecons_fpga/spec/logic_spec.lua
Normal file
195
mesecons_fpga/spec/logic_spec.lua
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
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()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
|
||||||
|
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}, {{"=", "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("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()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
assert.equal("mesecons_fpga:fpga0100", world.get_node(pos).name)
|
||||||
|
|
||||||
|
mesecon._test_dig(pos_a)
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
assert.equal("mesecons_fpga:fpga0100", world.get_node(pos).name)
|
||||||
|
|
||||||
|
mesecon._test_place(pos_b, "mesecons:test_receptor_on")
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
assert.equal("mesecons_fpga:fpga1000", world.get_node(pos).name)
|
||||||
|
|
||||||
|
mesecon._test_dig(pos_b)
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
mineunit:execute_globalstep()
|
||||||
|
assert.equal("mesecons_fpga:fpga1000", world.get_node(pos).name)
|
||||||
|
end)
|
||||||
|
end)
|
1
mesecons_fpga/spec/mineunit.conf
Normal file
1
mesecons_fpga/spec/mineunit.conf
Normal file
|
@ -0,0 +1 @@
|
||||||
|
fixture_paths = {"../mesecons/spec/fixtures"}
|
Loading…
Reference in New Issue
Block a user