Skip to content

Commit

Permalink
Accurate machine output assertions
Browse files Browse the repository at this point in the history
  • Loading branch information
S-S-X committed Jan 26, 2025
1 parent 9762ab9 commit 1196573
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 86 deletions.
37 changes: 37 additions & 0 deletions technic/spec/fixtures/technic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,40 @@ fixture("mesecons")
fixture("digilines")
fixture("pipeworks")
fixture("technic_worldgen")

spec_utility = {}

-- Calculate expected amount of items produced by base machines within completed network cycles
function spec_utility.base_machine_output_calculator(RUN_CYCLES)
return function (machine_speed, recipe_time, output_amount)
-- Total amount not necessarily divisible by per cycle output
local partial = math.floor(RUN_CYCLES * (output_amount * machine_speed / recipe_time))
-- Maximum amount divisible by per cycle output (amount clamped to full cycles)
return math.floor(partial / output_amount) * output_amount
end
end

-- Helper function to execute multiple globalsteps
function spec_utility.run_globalsteps(times, dtime)
-- By default, run once with dtime = 1 second instead of every 0.1 seconds
for i=1, times or 1 do
mineunit:execute_globalstep(dtime or 1)
end
end

-- Helper function to place itemstack into machine inventory, default listname = src
function spec_utility.place_itemstack(pos, itemstack, listname)
local meta = core.get_meta(pos)
local inv = meta:get_inventory()
if not inv:room_for_item(listname or "src", itemstack) then
inv:set_stack(listname or "src", 1, ItemStack(nil))
end
inv:add_item(listname or "src", itemstack)
end

-- Get itemstack in inventory for inspection without removing it, default listname = dst
function spec_utility.get_itemstack(pos, listname, index)
local meta = core.get_meta(pos)
local inv = meta:get_inventory()
return inv:get_stack(listname or "dst", index or 1)
end
94 changes: 50 additions & 44 deletions technic/spec/hv_network_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ describe("HV machine network", function()
mineunit:execute_globalstep(60)
world.set_default_node("air")

-- Execute multiple globalsteps: run_network(times = 1, dtime = 1)
local run_network = spec_utility.run_globalsteps

-- Place itemstack into inventory slot 1: place_itemstack(pos, itemstack, listname = "src")
local place_itemstack = spec_utility.place_itemstack

-- Get itemstack for inspection without removing it: get_itemstack(pos, listname = "dst", index = 1)
local get_itemstack = spec_utility.get_itemstack

-- Execute this many 1 second glopbalstep cycles for each RE machine
local RUN_CYCLES = 4
-- Function to calculate amount of items produced by base machines within completed network cycles
-- usage: base_machine_expected_amount(machine_speed, recipe_time, output_amount)
local base_machine_expected_amount = spec_utility.base_machine_output_calculator(RUN_CYCLES)

local machines = {
"technic:hv_generator",
"technic:hv_battery_box0",
Expand All @@ -29,45 +44,23 @@ describe("HV machine network", function()
"technic:hv_solar_array",
}

local function reset_machine(pos)
world.place_node(pos, machines[pos.x], player)
end

world.clear()
world.place_node({x=0,y=51,z=0}, "technic:switching_station", player)
for x = 0, 10 do
world.place_node({x=x,y=50,z=0}, "technic:hv_cable", player)
end
for x, name in ipairs(machines) do
world.place_node({x=x,y=51,z=0}, name, player)
end

-- Helper function to execute netowork
local function run_network(times)
times = times or 1
for i=1, times do
-- Globalstep every second instead of every 0.1 seconds
mineunit:execute_globalstep(1)
end
end

-- Helper function to place itemstack into machine inventory
local function place_itemstack(pos, itemstack, listname)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if not inv:room_for_item(listname or "src", itemstack) then
inv:set_stack(listname or "src", 1, ItemStack(nil))
end
inv:add_item(listname or "src", itemstack)
end

-- Get itemstack in inventory for inspection without removing it
local function get_itemstack(pos, listname, index)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:get_stack(listname or "dst", index or 1)
for x = 1, #machines do
reset_machine({x=x,y=51,z=0})
end

it("executes network", function()
spy.on(technic, "network_run")
run_network(60)
assert.spy(technic.network_run).called(60)
run_network(4)
assert.spy(technic.network_run).called(4)
local id = technic.pos2network({x=0,y=50,z=0})
assert.not_nil(technic.networks[id])
assert.gt(technic.networks[id].supply, 0)
Expand Down Expand Up @@ -95,48 +88,61 @@ describe("HV machine network", function()

it("smelts ores", function()
local machine_pos = {x=3,y=51,z=0}
reset_machine(machine_pos)
place_itemstack(machine_pos, "technic:lead_lump 99")
run_network(50)
-- Check results, at least 10 items processed and results in correct stuff
-- Extra cycle: powers up the machine but wont produce anything
run_network(RUN_CYCLES + 1)
-- Check results
local stack = get_itemstack(machine_pos)
assert.gt(stack:get_count(), 9)
assert.equals(stack:get_name(), "technic:lead_ingot")
-- Expected amount of items produced: machine speed, recipe time, items per cycle
assert.equals(base_machine_expected_amount(12, 3, 1), stack:get_count())
end)

it("grinds ores", function()
local machine_pos = {x=4,y=51,z=0}
reset_machine(machine_pos)
place_itemstack(machine_pos, "technic:lead_lump 99")
run_network(50)
-- Check results, at least 10 items processed and results in correct stuff
-- Extra cycle: powers up the machine but wont produce anything
run_network(RUN_CYCLES + 1)
-- Check results
local stack = get_itemstack(machine_pos)
assert.gt(stack:get_count(), 9)
assert.equals(stack:get_name(), "technic:lead_dust")
-- Expected amount of items produced: machine speed, recipe time, items per cycle
assert.equals(base_machine_expected_amount(5, 3, 2), stack:get_count())
end)

it("accepts battery upgrades", function()
local machine_pos = {x=4,y=51,z=0}
mineunit:clear_InvRef(machine_pos)
reset_machine(machine_pos)
place_itemstack(machine_pos, "technic:battery 1", "upgrade1")
place_itemstack(machine_pos, "technic:battery 1", "upgrade2")
place_itemstack(machine_pos, "technic:lead_lump 99")
run_network(6)
-- Check results, at least 2 items processed and results in correct stuff
-- Extra cycle: powers up the machine but wont produce anything
run_network(RUN_CYCLES + 1)
-- Check results
local stack = get_itemstack(machine_pos)
assert.gt(stack:get_count(), 2)
assert.equals(stack:get_name(), "technic:lead_dust")
-- Expected amount of items produced: machine speed, recipe time, items per cycle
assert.equals(base_machine_expected_amount(5, 3, 2), stack:get_count())
end)

it("accepts control logic upgrades", function()
local machine_pos = {x=4,y=51,z=0}
mineunit:clear_InvRef(machine_pos)
reset_machine(machine_pos)
place_itemstack(machine_pos, "technic:control_logic_unit 1", "upgrade1")
place_itemstack(machine_pos, "technic:control_logic_unit 1", "upgrade2")
place_itemstack(machine_pos, "technic:lead_lump 99")
run_network(6)
-- Check results, at least 2 items processed and results in correct stuff
-- Extra cycle: powers up the machine but wont produce anything
run_network(RUN_CYCLES + 1)
-- Check results
local stack = get_itemstack(machine_pos)
assert.gt(stack:get_count(), 1)
assert.equals(stack:get_name(), "technic:lead_dust")
-- Expected amount of items produced: machine speed, recipe time, items per cycle
-- Minus items sent away by CLUs: single item per cycle except first cycle which only produces
local expected_amount = base_machine_expected_amount(5, 3, 2) - RUN_CYCLES + 1
assert.equals(expected_amount, stack:get_count())
-- Pipeworks isn't loaded and tubes are handled with no-op functions, sent items are lost
end)

end)
86 changes: 44 additions & 42 deletions technic/spec/lv_network_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ describe("LV machine network", function()
mineunit:execute_globalstep(60)
world.set_default_node("air")

-- Execute multiple globalsteps: run_network(times = 1, dtime = 1)
local run_network = spec_utility.run_globalsteps

-- Place itemstack into inventory slot 1: place_itemstack(pos, itemstack, listname = "src")
local place_itemstack = spec_utility.place_itemstack

-- Get itemstack for inspection without removing it: get_itemstack(pos, listname = "dst", index = 1)
local get_itemstack = spec_utility.get_itemstack

-- Execute this many 1 second glopbalstep cycles for each RE machine
local RUN_CYCLES = 4
-- Function to calculate amount of items produced by base machines within completed network cycles
-- usage: base_machine_expected_amount(machine_speed, recipe_time, output_amount)
local base_machine_expected_amount = spec_utility.base_machine_output_calculator(RUN_CYCLES)

local machines = {
"technic:lv_battery_box0",
"technic:lv_electric_furnace",
Expand All @@ -34,21 +49,25 @@ describe("LV machine network", function()
"technic:lv_solar_array",
}

local function reset_machine(pos)
world.place_node(pos, machines[pos.x], player)
end

world.clear()
world.place_node({x=0,y=51,z=0}, "technic:switching_station", player)
for x = 0, 15 do
world.place_node({x=x,y=50,z=0}, "technic:lv_cable", player)
end
for x, name in ipairs(machines) do
world.place_node({x=x,y=51,z=0}, name, player)
reset_machine({x=x,y=51,z=0})
end

-- Helper to destroy nodes in test world returning list of removed nodes indexed by coordinates
local function remove_nodes(nodes)
local removed = {}
for x = 0, 15 do
local pos = {x=x,y=51,z=0}
local node = minetest.get_node(pos)
local node = core.get_node(pos)
if nodes[node.name] then
removed[pos] = node
world.remove_node(pos)
Expand All @@ -64,36 +83,10 @@ describe("LV machine network", function()
end
end

-- Helper function to execute netowork
local function run_network(times)
times = times or 1
for i=1, times do
-- Globalstep every second instead of every 0.1 seconds
mineunit:execute_globalstep(1)
end
end

-- Helper function to place itemstack into machine inventory
local function place_itemstack(pos, itemstack, listname)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if not inv:room_for_item(listname or "src", itemstack) then
inv:set_stack(listname or "src", 1, ItemStack(nil))
end
inv:add_item(listname or "src", itemstack)
end

-- Get itemstack in inventory for inspection without removing it
local function get_itemstack(pos, listname, index)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:get_stack(listname or "dst", index or 1)
end

it("executes network", function()
spy.on(technic, "network_run")
run_network(60)
assert.spy(technic.network_run).called(60)
run_network(4)
assert.spy(technic.network_run).called(4)
local id = technic.pos2network({x=0,y=50,z=0})
assert.not_nil(technic.networks[id])
assert.gt(technic.networks[id].supply, 0)
Expand Down Expand Up @@ -121,32 +114,41 @@ describe("LV machine network", function()

it("smelts ores", function()
local machine_pos = {x=2,y=51,z=0}
reset_machine(machine_pos)
place_itemstack(machine_pos, "technic:lead_lump 99")
run_network(60)
-- Check results, at least 10 items processed and results in correct stuff
-- Extra cycle: powers up the machine but wont produce anything
run_network(RUN_CYCLES + 1)
-- Check results
local stack = get_itemstack(machine_pos)
assert.gt(stack:get_count(), 10)
assert.equals(stack:get_name(), "technic:lead_ingot")
-- Expected amount of items produced: machine speed, recipe time, items per cycle
assert.equals(base_machine_expected_amount(2, 3, 1), stack:get_count())
end)

it("grinds ores", function()
local machine_pos = {x=4,y=51,z=0}
reset_machine(machine_pos)
place_itemstack(machine_pos, "technic:lead_lump 99")
run_network(60)
-- Check results, at least 10 items processed and results in correct stuff
-- Extra cycle: powers up the machine but wont produce anything
run_network(RUN_CYCLES + 1)
-- Check results
local stack = get_itemstack(machine_pos)
assert.gt(stack:get_count(), 10)
assert.equals(stack:get_name(), "technic:lead_dust")
-- Expected amount of items produced: machine speed, recipe time, items per cycle
assert.equals(base_machine_expected_amount(1, 3, 2), stack:get_count())
end)

it("comperess sand", function()
local machine_pos = {x=6,y=51,z=0}
reset_machine(machine_pos)
place_itemstack(machine_pos, "default:sand 99")
run_network(60)
-- Check results, at least 10 items processed and results in correct stuff
-- Extra cycle: powers up the machine but wont produce anything
run_network(RUN_CYCLES + 1)
-- Check results
local stack = get_itemstack(machine_pos)
assert.gt(stack:get_count(), 10)
assert.equals(stack:get_name(), "default:sandstone")
-- Expected amount of items produced: machine speed, recipe time, items per cycle
assert.equals(base_machine_expected_amount(1, 4, 1), stack:get_count())
end)

it("cuts power when generators disappear", function()
Expand All @@ -163,15 +165,15 @@ describe("LV machine network", function()
}
local restore = remove_nodes(generators)

-- Verify that network power is down immediately
-- Verify that network gets immediately powered down
local net = technic.networks[id]
run_network(1)
run_network()
assert.equal(net.supply, 0)

-- Get current battery charge for network and execute few more cycles
local battery_charge = net.battery_charge
assert.gt(net.battery_charge, 1000)
run_network(60)
run_network(RUN_CYCLES)

-- Verify that significant battery charge was used and network still does not generate energy
assert.lt(net.battery_charge, battery_charge / 2)
Expand Down

0 comments on commit 1196573

Please sign in to comment.