From 9448deb7ffeade9ee85cf1d2463b6ae61e7fa6c0 Mon Sep 17 00:00:00 2001 From: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com> Date: Thu, 9 Jul 2026 17:15:05 -0400 Subject: [PATCH] Unit test for making sure that boulders can be processed correctly. (#96611) ## About The Pull Request This PR adds a unit test for boulder processing, which follows these steps: Spawns 2 boulders, a refinery, and a smelter. Confirms that the boulder is spawning with materials (we're using a subtype that should allow for processing through both machines, by setting it's mats to iron and glass.) Moves the boulder into the refinery, then the smelter, and at each machine makes sure that it's able to enter the machine properly. Calls Process on the machine a set number of times, to ensure that the boulder should be finished. Checks to make sure the boulder has lost it's processed material. And then, after the moving the processes through the second machine, makes sure that it qdels the boulder without leaving any mysterious ghost boulders lingering in this mortal coil. The test also runs twice, once going refinery -> smelter, and again from smelter -> refinery. ## Why It's Good For The Game The intent is to futureproof boulder refinery, as well as to help prevent issues like #96549, though I'm not confident that this PR closes the listed issue just yet. This process seems to be somewhat delicate based on the number of times that it's broken thus far in the past 2 years, and starting to implement a unit test is the best way to improve it's reliability going forward. ## Changelog No player facing changes. --------- Co-authored-by: san7890 Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com> --- code/game/atom/_atom.dm | 7 ++- .../objects/structures/lavaland/ore_vent.dm | 4 ++ .../boulder_processing/boulder_types.dm | 2 +- code/modules/unit_tests/_unit_tests.dm | 1 + code/modules/unit_tests/boulder_processing.dm | 53 +++++++++++++++++++ 5 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 code/modules/unit_tests/boulder_processing.dm diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm index d5fc1da4ee6..0ea9dac0f16 100644 --- a/code/game/atom/_atom.dm +++ b/code/game/atom/_atom.dm @@ -225,7 +225,12 @@ if(!mover.generic_canpass) return mover.CanPassThrough(src, REVERSE_DIR(border_dir), .) -/// Returns true or false to allow the mover to move through src +/** + * Returns true or false to allow the mover to move through src + * @params + * mover: The mob trying to move into this atom. + * border_dir: Typically the direction that mover has in relation to src. + */ /atom/proc/CanAllowThrough(atom/movable/mover, border_dir) SHOULD_CALL_PARENT(TRUE) //SHOULD_BE_PURE(TRUE) diff --git a/code/game/objects/structures/lavaland/ore_vent.dm b/code/game/objects/structures/lavaland/ore_vent.dm index 47ef1b028c3..3f2edfc7416 100644 --- a/code/game/objects/structures/lavaland/ore_vent.dm +++ b/code/game/objects/structures/lavaland/ore_vent.dm @@ -669,6 +669,10 @@ if(!mapload) vent_size_setup(random = TRUE) // We only do this here specific to random distribution ore vents, and within mapload we handle this manually within SSore_generation. +/obj/structure/ore_vent/random/LateInitialize() + . = ..() + if(!length(mineral_breakdown)) + CRASH("We generated an ore vent, and after init, it had no mineral breakdown!") /obj/structure/ore_vent/random/icebox //The one that shows up on the top level of icebox icon_state = "ore_vent_ice" diff --git a/code/modules/mining/boulder_processing/boulder_types.dm b/code/modules/mining/boulder_processing/boulder_types.dm index 4c8a4d45fa7..e5e93a74c7c 100644 --- a/code/modules/mining/boulder_processing/boulder_types.dm +++ b/code/modules/mining/boulder_processing/boulder_types.dm @@ -79,7 +79,7 @@ var/amount = rand(10, 13) set_custom_materials(list(pick_weight(gulag_minerals) = SHEET_MATERIAL_AMOUNT * amount)) -///lowgrade boulder, Exists as an admin spawn for testing +///lowgrade boulder, Exists as an admin spawn for testing and unit testing. /obj/item/boulder/shabby name = "shabby boulder" desc = "A bizarre, twisted boulder. Wait, wait no, it's just a rock." diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 901afe40974..d714702deea 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -127,6 +127,7 @@ #include "blood_volume_procs.dm" #include "bloody_footprints.dm" #include "borg_tools.dm" +#include "boulder_processing.dm" #include "breath.dm" #include "buckle.dm" #include "burning.dm" diff --git a/code/modules/unit_tests/boulder_processing.dm b/code/modules/unit_tests/boulder_processing.dm new file mode 100644 index 00000000000..39a107c8c84 --- /dev/null +++ b/code/modules/unit_tests/boulder_processing.dm @@ -0,0 +1,53 @@ +/** + * This unit test crates a boulder, spawns it, and then moves it through boulder processing to confirm that boulders can be processed without issue. + */ + +/datum/unit_test/boulder_processing + test_flags = UNIT_TEST_FOCUS + +/datum/unit_test/boulder_processing/Run() + var/turf/refinery_loc = get_step(run_loc_floor_bottom_left, EAST) + var/turf/smelter_loc = get_step(refinery_loc, EAST) + var/turf/opposite_loc = get_step(smelter_loc, EAST) + var/obj/item/boulder/shabby/test_boulder = EASY_ALLOCATE() //Called because we know it has both iron and glass for each machine. + var/obj/machinery/bouldertech/refinery/test_refine = allocate(/obj/machinery/bouldertech/refinery, refinery_loc) + var/obj/machinery/bouldertech/refinery/smelter/test_smelter = allocate(/obj/machinery/bouldertech/refinery/smelter, smelter_loc) + test_refine.dir = WEST + test_smelter.dir = WEST + //Test to confirm that the boulder is as we expect it to be: + TEST_ASSERT(test_boulder.durability > 0, "Boulder was spawned such that it's durability is less than 1!") + test_boulder.durability = 2 + test_boulder.Move(get_turf(refinery_loc), EAST) + TEST_ASSERT_EQUAL(test_boulder.loc, test_refine, "The boulder was not moved into the refinery's contents!") + for(var/i in 1 to 2) + test_refine.process() + TEST_ASSERT_NOTEQUAL(test_boulder.loc, test_refine, "The boulder was not moved out of the refinery's contents!") + TEST_ASSERT(!test_boulder.has_material_type(/datum/material/glass), "After the boulder was successfully processed by the refinery, no-ferrous materials still remain inside!") + TEST_ASSERT(test_boulder.durability > 0, "Boulder was processed successfully, but exited with durability under 1!") + test_boulder.durability = 2 + test_boulder.Move(get_turf(smelter_loc), EAST) + TEST_ASSERT_EQUAL(test_boulder.loc, test_smelter, "The boulder was not moved into the smelter's contents! We are at: [test_boulder.x], [test_boulder.y], and machine is at [test_smelter.x], [test_smelter.y] which is [test_smelter.loc]") + for(var/i in 1 to 2) + test_smelter.process() + TEST_ASSERT(QDELETED(test_boulder),"After being processed by both a refinery and smelter, the boulder was not qdeleted!") + /// Now we run it in reverse, using the opposite_loc to start with the smelter! + // Manually reset cooldowns for accepting new boulders. + COOLDOWN_RESET(test_refine, accept_cooldown) + COOLDOWN_RESET(test_smelter, accept_cooldown) + //Test to confirm that the boulder is as we expect it to be: + var/obj/item/boulder/shabby/second_boulder = allocate(/obj/item/boulder/shabby, opposite_loc) //Called because we know it has both iron and glass for each machine. + TEST_ASSERT(second_boulder.durability > 0, "Boulder was spawned such that it's durability is less than 1!") + second_boulder.durability = 2 + second_boulder.Move(get_turf(smelter_loc), WEST) + TEST_ASSERT_EQUAL(second_boulder.loc, test_smelter, "The boulder was not moved into the smelter's contents! We are at: [second_boulder.x], [second_boulder.y], and machine is at [test_smelter.x], [test_smelter.y] which is [test_smelter.loc]") + for(var/i in 1 to 2) + test_smelter.process() + TEST_ASSERT_NOTEQUAL(second_boulder.loc, test_smelter, "The boulder was not moved out of the smelter's contents!") + TEST_ASSERT(!second_boulder.has_material_type(/datum/material/iron), "After the boulder was successfully processed by the smelter, ferrous materials still remain inside!") + TEST_ASSERT(second_boulder.durability > 0, "Boulder was processed successfully, but exited with durability under 1!") + second_boulder.durability = 2 + second_boulder.Move(get_turf(refinery_loc), WEST) + TEST_ASSERT_EQUAL(second_boulder.loc, test_refine, "The boulder was not moved into the refinery's contents!") + for(var/i in 1 to 2) + test_refine.process() + TEST_ASSERT(QDELETED(second_boulder), "After being processed by both a refinery and smelter, the boulder was not qdeleted!")