require("mineunit") --[[ Technic tool regression tests. Execute mineunit at technic source directory. --]] -- Load complete technic mod fixture("technic") sourcefile("init") local RUN_TECHNIC_ADDONS_CHAINSAWMK3_TESTS = false describe("Technic power tool", function() if RUN_TECHNIC_ADDONS_CHAINSAWMK3_TESTS then -- Load technic_addons mod mineunit:set_modpath("technic_addons", "../../technic_addons") mineunit:set_current_modname("technic_addons") sourcefile("../../technic_addons/init") mineunit:restore_current_modname() end world.set_default_node("air") -- HV battery box and some HV solar arrays for charging local BB_Charge_POS = {x=0,y=51,z=0} local BB_Discharge_POS = {x=0,y=51,z=2} world.layout({ -- Network with generators for charging tools in battery box {BB_Charge_POS, "technic:hv_battery_box0"}, {{x=1,y=51,z=0}, "technic:switching_station"}, {{{x=2,y=51,z=0},{x=10,y=51,z=0}}, "technic:solar_array_hv"}, {{{x=0,y=50,z=0},{x=10,y=50,z=0}}, "technic:hv_cable"}, -- Network without generators for discharging tools in battery box {BB_Discharge_POS, "technic:hv_battery_box0"}, {{x=1,y=51,z=2}, "technic:switching_station"}, {{{x=0,y=50,z=2},{x=1,y=50,z=2}}, "technic:hv_cable"}, }) -- Some helpers to make stack access simpler local player = Player("SX") local charge_inv = minetest.get_meta(BB_Charge_POS):get_inventory() local discharge_inv = minetest.get_meta(BB_Discharge_POS):get_inventory() local function set_charge_stack(stack) charge_inv:set_stack("src", 1, stack) end local function get_charge_stack() return charge_inv:get_stack("src", 1) end local function set_discharge_stack(stack) discharge_inv:set_stack("dst", 1, stack) end local function get_discharge_stack() return discharge_inv:get_stack("dst", 1) end local function set_player_stack(stack) return player:get_inventory():set_stack("main", 1, stack) end local function get_player_stack() return player:get_inventory():get_stack("main", 1) end -- Execute on mods loaded callbacks to finish loading. mineunit:mods_loaded() -- Tell mods that 1 minute passed already to execute all weird minetest.after hacks. mineunit:execute_globalstep(60) describe("API", function() setup(function() set_charge_stack(ItemStack()) set_discharge_stack(ItemStack()) mineunit:set_current_modname("mymod") mineunit:execute_on_joinplayer(player) end) teardown(function() mineunit:execute_on_leaveplayer(player) mineunit:restore_current_modname() end) local use_RE_charge_result it("technic.register_power_tool works", function() technic.register_power_tool("mymod:powertool", { description = "My Mod Power Tool", inventory_image = "mymod_powertool.png", max_charge = 1234, on_use = function(itemstack, player, pointed_thing) use_RE_charge_result = technic.use_RE_charge(itemstack, 123) return itemstack end, }) local itemdef = minetest.registered_items["mymod:powertool"] assert.is_hashed(itemdef) assert.is_function(itemdef.on_use) assert.is_function(itemdef.on_refill) assert.equals("technic_RE_charge", itemdef.wear_represents) assert.is_number(itemdef.technic_max_charge) assert.gt(itemdef.technic_max_charge, 0) end) it("technic.use_RE_charge works (zero charge)", function() set_player_stack("mymod:powertool") spy.on(technic, "use_RE_charge") player:do_use(player:get_pos()) assert.spy(technic.use_RE_charge).called(1) assert.equals("boolean", type(use_RE_charge_result)) assert.is_false(use_RE_charge_result) end) it("technic.get_RE_charge works (zero charge)", function() assert.equals(0, technic.get_RE_charge(ItemStack("mymod:powertool"))) end) it("technic.set_RE_charge works (zero charge -> 123)", function() local stack = ItemStack("mymod:powertool") technic.set_RE_charge(stack, 123) assert.equals(123, technic.get_RE_charge(stack)) end) it("technic.use_RE_charge works (minimum charge)", function() -- Add partially charged tool to player inventory local stack = ItemStack("mymod:powertool") technic.set_RE_charge(stack, 123) set_player_stack(stack) -- Use tool and verify results spy.on(technic, "use_RE_charge") player:do_use(player:get_pos()) assert.spy(technic.use_RE_charge).called(1) assert.equals("boolean", type(use_RE_charge_result)) assert.is_true(use_RE_charge_result) assert.equals(0, technic.get_RE_charge(get_player_stack())) end) it("technic.use_RE_charge works (minimum charge + 1)", function() -- Add partially charged tool to player inventory local stack = ItemStack("mymod:powertool") technic.set_RE_charge(stack, 124) set_player_stack(stack) -- Use tool and verify results player:do_use(player:get_pos()) assert.equals(1, technic.get_RE_charge(get_player_stack())) end) end) describe("charging and discharging extreme ratios", function() local function register_test_tool(name, charge_per_use, max_charge) technic.register_power_tool("mymod:"..name, { description = name, inventory_image = "mymod_powertool.png", max_charge = max_charge, on_use = function(itemstack, player, pointed_thing) technic.use_RE_charge(itemstack, charge_per_use) return itemstack end, }) end setup(function() set_charge_stack(ItemStack()) set_discharge_stack(ItemStack()) mineunit:set_current_modname("mymod") register_test_tool("t1_1", 1, 1) register_test_tool("t1_2", 1, 2) register_test_tool("t1_10001", 1, 10001) register_test_tool("t1_65535", 1, 65535) register_test_tool("t1_65536", 1, 65536) register_test_tool("t100_6553500", 1, 6553500) register_test_tool("t100_6553600", 1, 6553600) register_test_tool("t2_3", 2, 3) mineunit:restore_current_modname() mineunit:execute_on_joinplayer(player) end) teardown(function() mineunit:execute_on_leaveplayer(player) end) -- Very basic tests for few simple and straightforward max_charge values -- Add tool to battery box and test charging, 10kEU / cycle it("t1_1 can be charged", function() set_charge_stack(ItemStack("mymod:t1_1")) assert.equals(0, technic.get_RE_charge(get_charge_stack())) mineunit:execute_globalstep(1) assert.equals(1, technic.get_RE_charge(get_charge_stack())) end) it("t1_2 can be charged", function() -- Add tool to battery box set_charge_stack(ItemStack("mymod:t1_2")) -- Test charging, 10kEU / cycle assert.equals(0, technic.get_RE_charge(get_charge_stack())) mineunit:execute_globalstep(1) assert.equals(2, technic.get_RE_charge(get_charge_stack())) end) it("t1_10001 can be charged", function() -- Add tool to battery box set_charge_stack(ItemStack("mymod:t1_10001")) -- Test charging, 10kEU / cycle assert.equals(0, technic.get_RE_charge(get_charge_stack())) mineunit:execute_globalstep(1) assert.equals(10000, technic.get_RE_charge(get_charge_stack())) mineunit:execute_globalstep(1) assert.equals(10001, technic.get_RE_charge(get_charge_stack())) end) it("t1_65535 can be charged", function() -- Add tool to battery box set_charge_stack(ItemStack("mymod:t1_65535")) -- Test charging, 10kEU / cycle assert.equals(0, technic.get_RE_charge(get_charge_stack())) for i=1,6 do mineunit:execute_globalstep(1) end assert.equals(60000, technic.get_RE_charge(get_charge_stack())) mineunit:execute_globalstep(1) assert.equals(65535, technic.get_RE_charge(get_charge_stack())) end) it("t1_65536 can be charged", function() -- Add tool to battery box set_charge_stack(ItemStack("mymod:t1_65536")) -- Test charging, 10kEU / cycle assert.equals(0, technic.get_RE_charge(get_charge_stack())) for i=1,6 do mineunit:execute_globalstep(1) end assert.equals(60000, technic.get_RE_charge(get_charge_stack())) mineunit:execute_globalstep(1) assert.equals(65536, technic.get_RE_charge(get_charge_stack())) end) it("t100_6553500 can be charged", function() -- Add tool to battery box set_charge_stack(ItemStack("mymod:t100_6553500")) -- Test charging, 10kEU / cycle assert.equals(0, technic.get_RE_charge(get_charge_stack())) for i=1,6 do mineunit:execute_globalstep(1) end assert.equals(60000, technic.get_RE_charge(get_charge_stack())) mineunit:execute_globalstep(1) assert.equals(70000, technic.get_RE_charge(get_charge_stack())) end) it("t100_6553600 can be charged", function() -- Add tool to battery box set_charge_stack(ItemStack("mymod:t100_6553600")) -- Test charging, 10kEU / cycle assert.equals(0, technic.get_RE_charge(get_charge_stack())) for i=1,7 do mineunit:execute_globalstep(1) end -- This tool already has small charge error and it is acceptable as long as error stays small -- Charge value must be 69999-70001 after 7 charge cycles assert.lt(69998, technic.get_RE_charge(get_charge_stack())) assert.gt(70002, technic.get_RE_charge(get_charge_stack())) end) it("t100_6553600 can be used", function() -- Add tool to battery box local stack = ItemStack("mymod:t100_6553600") technic.set_RE_charge(stack, 700) set_player_stack(stack) -- Test using, 100 / cycle for i=1,6 do player:do_use({x=0, y=0, z=0}) end assert.equals(100, technic.get_RE_charge(get_player_stack())) player:do_use({x=0, y=0, z=0}) assert.equals(0, technic.get_RE_charge(get_player_stack())) end) end) describe("Flashlight", function() local itemname = "technic:flashlight" local itemdef = minetest.registered_items[itemname] setup(function() set_charge_stack(ItemStack()) set_discharge_stack(ItemStack()) mineunit:execute_on_joinplayer(player) end) teardown(function() mineunit:execute_on_leaveplayer(player) end) it("is registered", function() assert.is_hashed(itemdef) assert.is_function(itemdef.on_refill) assert.equals("technic_RE_charge", itemdef.wear_represents) assert.is_number(itemdef.technic_max_charge) assert.gt(itemdef.technic_max_charge, 0) end) it("charge is used", function() -- Get fully charged item local stack = ItemStack(itemname) technic.set_RE_charge(stack, itemdef.technic_max_charge) set_player_stack(stack) -- Use item, flashlight charge is used every globalstep and there's no on_use definition spy.on(technic, "use_RE_charge") for i=1, 100 do mineunit:execute_globalstep(1) end assert.spy(technic.use_RE_charge).called(100) -- Check that item charge was actually used and error is acceptable local charge_used = itemdef.technic_max_charge - technic.get_RE_charge(get_player_stack()) local exact_use = 2 * 100 -- 2 per cycle / 100 cycles assert.lt(0.9, charge_used / exact_use) assert.gt(1.1, charge_used / exact_use) end) end) describe("Multimeter", function() local itemname = "technic:multimeter" local itemdef = minetest.registered_items[itemname] setup(function() set_charge_stack(ItemStack()) set_discharge_stack(ItemStack()) mineunit:execute_on_joinplayer(player) player:get_inventory():set_stack("main", 1, itemname) end) teardown(function() mineunit:execute_on_leaveplayer(player) end) it("is registered", function() assert.is_hashed(itemdef) assert.is_function(itemdef.on_use) assert.is_function(itemdef.on_refill) assert.equals("technic_RE_charge", itemdef.wear_represents) assert.is_number(itemdef.technic_max_charge) assert.gt(itemdef.technic_max_charge, 0) end) it("new item can be used", function() spy.on(itemdef, "on_use") player:do_use({x=0, y=0, z=0}) assert.spy(itemdef.on_use).called(1) end) it("has zero charge", function() local stack = player:get_wielded_item() assert.is_ItemStack(stack) assert.is_false(stack:is_empty()) assert.equals(0, technic.get_RE_charge(stack)) end) it("can be charged", function() -- Put item from player inventory to battery box src inventory player:do_metadata_inventory_put(BB_Charge_POS, "src", 1) -- Verify that item charge is empty and charge in battery box for 30 seconds assert.equals(0, technic.get_RE_charge(get_charge_stack())) for i=1, 30 do mineunit:execute_globalstep(1) end -- Take item from battery box and check charge / wear values player:do_metadata_inventory_take(BB_Charge_POS, "src", 1) assert.gt(itemdef.technic_max_charge, 0) assert.equals(itemdef.technic_max_charge, technic.get_RE_charge(get_player_stack())) end) it("charge is used", function() spy.on(itemdef, "on_use") player:set_pos(vector.add(BB_Charge_POS, {x=0,y=1,z=0})) player:do_use(BB_Charge_POS) assert.spy(itemdef.on_use).called(1) -- Check that item charge was actually used and is not zero local charge = technic.get_RE_charge(get_player_stack()) assert.is_number(charge) assert.gt(charge, 0) assert.lt(charge, itemdef.technic_max_charge) end) it("can be discharged", function() -- Put item from player inventory to battery box src inventory player:do_metadata_inventory_put(BB_Discharge_POS, "dst", 1) -- Verify that item is charged and discharge in battery box for 3 seconds assert.lt(itemdef.technic_max_charge / 2, technic.get_RE_charge(get_discharge_stack())) for i=1, 3 do mineunit:execute_globalstep(1) end -- Take item from battery box and check charge / wear values player:do_metadata_inventory_take(BB_Discharge_POS, "dst", 1) assert.gt(itemdef.technic_max_charge, 0) assert.equals(0, technic.get_RE_charge(get_player_stack())) end) end) describe("technic_addons:chainsawmk3", function() -- Not running technic_addons:chainsawmk3 tests, this tools is just example of -- actually available mod where max_charge vs use ratio goes over safe limits. if not RUN_TECHNIC_ADDONS_CHAINSAWMK3_TESTS then return end local itemname = "technic_addons:chainsawmk3" local itemdef = minetest.registered_items[itemname] setup(function() world.add_layout({ -- Some wood for chainsaw {{{x=-10,y=0,z=-10},{x=10,y=10,z=10}}, "default:wood"}, }) set_charge_stack(ItemStack()) set_discharge_stack(ItemStack()) mineunit:execute_on_joinplayer(player) set_player_stack(itemname) end) teardown(function() mineunit:execute_on_leaveplayer(player) end) it("is registered", function() assert.is_hashed(itemdef) assert.is_function(itemdef.on_use) assert.is_function(itemdef.on_refill) assert.equals("technic_RE_charge", itemdef.wear_represents) assert.is_number(itemdef.technic_max_charge) assert.gt(itemdef.technic_max_charge, 0) end) it("new item can be used", function() spy.on(itemdef, "on_use") player:do_use({x=0, y=0, z=0}) assert.spy(itemdef.on_use).called(1) end) it("has zero charge", function() local stack = player:get_wielded_item() assert.is_ItemStack(stack) assert.is_false(stack:is_empty()) assert.equals(0, technic.get_RE_charge(stack)) end) it("can be charged", function() -- Put item from player inventory to battery box src inventory player:do_metadata_inventory_put(BB_Charge_POS, "src", 1) -- Verify that item charge is empty and charge in battery box for 30 seconds assert.equals(0, technic.get_RE_charge(get_charge_stack())) for i=0, math.ceil(itemdef.technic_max_charge / 10000) do mineunit:execute_globalstep(1) end -- Take item from battery box and check charge / wear values player:do_metadata_inventory_take(BB_Charge_POS, "src", 1) assert.gt(itemdef.technic_max_charge, 0) assert.equals(itemdef.technic_max_charge, technic.get_RE_charge(get_player_stack())) end) it("charge is used", function() spy.on(itemdef, "on_use") player:set_pos({x=0,y=1,z=0}) player:do_use(player:get_pos()) assert.spy(itemdef.on_use).called(1) -- Check that item charge was actually used and is not zero local charge_used = itemdef.technic_max_charge - technic.get_RE_charge(get_player_stack()) local accurate_use = 21 * 21 * 11 * 10 -- Area times use per node local acceptable_error = accurate_use * 0.0001 assert.gt(accurate_use + acceptable_error, charge_used) assert.lt(accurate_use - acceptable_error, charge_used) assert.equals(accurate_use, charge_used) end) end) end)