diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 815aaa41d99..0990e5bd29c 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -2,7 +2,7 @@ //#define TESTING // Uncomment the following line to compile unit tests on a local server. The output will be in a test_run-[DATE].log file in the ./data folder. -// #define LOCAL_UNIT_TESTS +// #define LOCAL_GAME_TESTS // Uncomment the following line to enable Tracy profiling. // DO NOT DO THIS UNLESS YOU UNDERSTAND THE IMPLICATIONS @@ -12,16 +12,16 @@ // Uncomment this to enable support for multiple instances // #define MULTIINSTANCE -#ifdef LOCAL_UNIT_TESTS -#define UNIT_TESTS +#ifdef LOCAL_GAME_TESTS +#define GAME_TESTS #endif #ifdef CIBUILDING -#define UNIT_TESTS +#define GAME_TESTS #endif -#if defined(CIBUILDING) && defined(LOCAL_UNIT_TESTS) -#error CIBUILDING and LOCAL_UNIT_TESTS should not be enabled at the same time! +#if defined(CIBUILDING) && defined(LOCAL_GAME_TESTS) +#error CIBUILDING and LOCAL_GAME_TESTS should not be enabled at the same time! #endif /***** All toggles for the GC ref finder *****/ diff --git a/code/controllers/configuration/sections/database_configuration.dm b/code/controllers/configuration/sections/database_configuration.dm index e7ed707f573..73a396c9e29 100644 --- a/code/controllers/configuration/sections/database_configuration.dm +++ b/code/controllers/configuration/sections/database_configuration.dm @@ -22,7 +22,7 @@ /datum/configuration_section/database_configuration/load_data(list/data) // UNIT TESTS ARE DEFINED - USE CUSTOM CI VALUES - #ifdef UNIT_TESTS + #ifdef GAME_TESTS enabled = TRUE // This needs to happen in the CI environment to ensure the example SQL version gets updated. diff --git a/code/controllers/configuration/sections/redis_configuration.dm b/code/controllers/configuration/sections/redis_configuration.dm index 45661f3643b..b418441e7f9 100644 --- a/code/controllers/configuration/sections/redis_configuration.dm +++ b/code/controllers/configuration/sections/redis_configuration.dm @@ -8,7 +8,7 @@ /datum/configuration_section/redis_configuration/load_data(list/data) // UNIT TESTS ARE DEFINED - USE CUSTOM CI VALUES - #ifdef UNIT_TESTS + #ifdef GAME_TESTS // enabled = TRUE diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 1e1368be7a2..8bdf4ffe992 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -83,7 +83,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new /datum/controller/master/New() if(!random_seed) - #ifdef UNIT_TESTS + #ifdef GAME_TESTS random_seed = 29051994 #else random_seed = rand(1, 1e9) diff --git a/code/controllers/subsystem/SSdbcore.dm b/code/controllers/subsystem/SSdbcore.dm index 9299f837106..9974e20ccbb 100644 --- a/code/controllers/subsystem/SSdbcore.dm +++ b/code/controllers/subsystem/SSdbcore.dm @@ -104,7 +104,7 @@ SUBSYSTEM_DEF(dbcore) /datum/controller/subsystem/dbcore/proc/CheckSchemaVersion() if(GLOB.configuration.database.enabled) // The unit tests have their own version of this check, which wont hold the server up infinitely, so this is disabled if we are running unit tests - #ifndef UNIT_TESTS + #ifndef GAME_TESTS if(GLOB.configuration.database.enabled && GLOB.configuration.database.version != SQL_VERSION) GLOB.configuration.database.enabled = FALSE schema_valid = FALSE diff --git a/code/controllers/subsystem/SSredis.dm b/code/controllers/subsystem/SSredis.dm index 9f0839cdd02..e617d3abf54 100644 --- a/code/controllers/subsystem/SSredis.dm +++ b/code/controllers/subsystem/SSredis.dm @@ -67,7 +67,7 @@ SUBSYSTEM_DEF(redis) // Redis integration stuff /datum/controller/subsystem/redis/proc/connect() if(GLOB.configuration.redis.enabled) - #ifndef UNIT_TESTS // CI uses linux so dont flag up a fail there + #ifndef GAME_TESTS // CI uses linux so dont flag up a fail there if(world.system_type == UNIX) stack_trace("SSredis has known to be very buggy when running on Linux with random dropouts ocurring due to interrupted syscalls. You have been warned!") #endif diff --git a/code/controllers/subsystem/SSticker.dm b/code/controllers/subsystem/SSticker.dm index 715b6ef11b4..2dab6c4b66b 100644 --- a/code/controllers/subsystem/SSticker.dm +++ b/code/controllers/subsystem/SSticker.dm @@ -363,7 +363,7 @@ SUBSYSTEM_DEF(ticker) if(GLOB.configuration.general.enable_night_shifts) SSnightshift.check_nightshift(TRUE) - #ifdef UNIT_TESTS + #ifdef GAME_TESTS // Run map tests first in case unit tests futz with map state GLOB.test_runner.RunMap() GLOB.test_runner.Run() diff --git a/code/controllers/subsystem/SSverb_manager.dm b/code/controllers/subsystem/SSverb_manager.dm index bfa780015f3..9382a121a3e 100644 --- a/code/controllers/subsystem/SSverb_manager.dm +++ b/code/controllers/subsystem/SSverb_manager.dm @@ -75,7 +75,7 @@ SUBSYSTEM_DEF(verb_manager) //we want unit tests to be able to directly call verbs that attempt to queue, and since unit tests should test internal behavior, we want the queue //to happen as if it was actually from player input if its called on a mob. -#ifdef UNIT_TESTS +#ifdef GAME_TESTS if(QDELETED(usr) && ismob(incoming_callback.object)) incoming_callback.usr_uid = incoming_callback.object.UID() var/datum/callback/new_us = CALLBACK(arglist(list(GLOBAL_PROC, GLOBAL_PROC_REF(_queue_verb)) + args.Copy())) diff --git a/code/controllers/subsystem/non_firing/SSmapping.dm b/code/controllers/subsystem/non_firing/SSmapping.dm index 4787783e7b6..a776a3219d2 100644 --- a/code/controllers/subsystem/non_firing/SSmapping.dm +++ b/code/controllers/subsystem/non_firing/SSmapping.dm @@ -88,7 +88,7 @@ SUBSYSTEM_DEF(mapping) // Load all Z level templates preloadTemplates() - preloadTemplates(path = "code/modules/unit_tests/atmos/") + preloadTemplates(path = "code/tests/atmos/") // Load the station loadStation() diff --git a/code/game/world.dm b/code/game/world.dm index e934067c084..da8dac0955d 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -1,6 +1,6 @@ GLOBAL_LIST_INIT(map_transition_config, list(CC_TRANSITION_CONFIG)) -#ifdef UNIT_TESTS +#ifdef GAME_TESTS GLOBAL_DATUM(test_runner, /datum/test_runner) #endif @@ -51,7 +51,7 @@ GLOBAL_DATUM(test_runner, /datum/test_runner) if(TgsAvailable()) world.log = file("[GLOB.log_directory]/dd.log") //not all runtimes trigger world/Error, so this is the only way to ensure we can see all of them. - #ifdef UNIT_TESTS + #ifdef GAME_TESTS log_world("Unit Tests Are Enabled!") #endif @@ -69,7 +69,7 @@ GLOBAL_DATUM(test_runner, /datum/test_runner) Master.Initialize(10, FALSE, TRUE) - #ifdef UNIT_TESTS + #ifdef GAME_TESTS GLOB.test_runner = new GLOB.test_runner.Start() #endif @@ -144,7 +144,7 @@ GLOBAL_LIST_EMPTY(world_topic_handlers) Master.Shutdown() // Shutdown subsystems // If we were running unit tests, finish that run - #ifdef UNIT_TESTS + #ifdef GAME_TESTS GLOB.test_runner.Finalize() return #endif diff --git a/code/modules/mob/living/carbon/carbon_procs.dm b/code/modules/mob/living/carbon/carbon_procs.dm index f41ae3f76ec..594e12a7963 100644 --- a/code/modules/mob/living/carbon/carbon_procs.dm +++ b/code/modules/mob/living/carbon/carbon_procs.dm @@ -493,7 +493,7 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven visible_message("[src] begins climbing into the ventilation system...", \ "You begin climbing into the ventilation system...") -#ifdef UNIT_TESTS +#ifdef GAME_TESTS var/ventcrawl_delay = 0 SECONDS #else var/ventcrawl_delay = 4.5 SECONDS diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index 39e99b7720e..ec8c6b06ee1 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -43,7 +43,7 @@ AddComponent(/datum/component/squeak, list('sound/creatures/mousesqueak.ogg' = 1), 100, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) //as quiet as a mouse or whatever /mob/living/simple_animal/mouse/handle_automated_action() -#ifdef UNIT_TESTS // DO NOT EAT MY CABLES DURING UNIT TESTS +#ifdef GAME_TESTS // DO NOT EAT MY CABLES DURING UNIT TESTS return #endif if(prob(chew_probability) && isturf(loc)) diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm deleted file mode 100644 index eea46b9e67f..00000000000 --- a/code/modules/unit_tests/_unit_tests.dm +++ /dev/null @@ -1,34 +0,0 @@ -//include unit test files in this module in this ifdef -//Keep this sorted alphabetically - -#ifdef UNIT_TESTS -#include "atmos\test_ventcrawl.dm" -#include "games\test_cards.dm" -#include "jobs\test_job_globals.dm" -#include "aicard_icons.dm" -#include "announcements.dm" -#include "areas_apcs.dm" -#include "component_tests.dm" -#include "config_sanity.dm" -#include "crafting_lists.dm" -#include "element_tests.dm" -#include "emotes.dm" -#include "init_sanity.dm" -#include "log_format.dm" -#include "map_templates.dm" -#include "map_tests.dm" -#include "missing_icons.dm" -#include "origin_tech.dm" -#include "purchase_reference_test.dm" -#include "reagent_id_typos.dm" -#include "rustg_version.dm" -#include "spawn_humans.dm" -#include "spell_targeting_test.dm" -#include "sql.dm" -#include "status_effect_ids.dm" -#include "subsystem_init.dm" -#include "subsystem_metric_sanity.dm" -#include "test_runner.dm" -#include "timer_sanity.dm" -#include "unit_test.dm" -#endif diff --git a/code/modules/unit_tests/atmos/test_ventcrawl.dmm b/code/modules/unit_tests/atmos/test_ventcrawl.dmm deleted file mode 100644 index 9b9a7be6af9..00000000000 --- a/code/modules/unit_tests/atmos/test_ventcrawl.dmm +++ /dev/null @@ -1,7 +0,0 @@ -"a" = (/obj/machinery/atmospherics/unary/vent_pump/on{dir = 4},/turf/simulated/floor,/area/space) -"d" = (/obj/machinery/atmospherics/unary/vent_pump/on{dir = 8},/turf/simulated/floor,/area/space) -"X" = (/obj/machinery/atmospherics/pipe/simple/visible{dir = 4},/obj/structure/table,/turf/simulated/floor,/area/space) - -(1,1,1) = {" -aXd -"} diff --git a/code/modules/unit_tests/crafting_lists.dm b/code/modules/unit_tests/crafting_lists.dm deleted file mode 100644 index 43ec7bc1036..00000000000 --- a/code/modules/unit_tests/crafting_lists.dm +++ /dev/null @@ -1,5 +0,0 @@ -/datum/unit_test/crafting_lists/Run() - for(var/I in subtypesof(/datum/crafting_recipe)) - var/datum/crafting_recipe/C = new I() - if(!islist(C.result)) - Fail("Expected a list for the 'result' of [C.type].") diff --git a/code/modules/unit_tests/rustg_version.dm b/code/modules/unit_tests/rustg_version.dm deleted file mode 100644 index 0a259990b97..00000000000 --- a/code/modules/unit_tests/rustg_version.dm +++ /dev/null @@ -1,4 +0,0 @@ -/datum/unit_test/rustg_version/Run() - var/library_version = rustg_get_version() - if(library_version != RUST_G_VERSION) - Fail("Invalid RUSTG Version. Library is [library_version], but in-code API is [RUST_G_VERSION]") diff --git a/code/modules/unit_tests/sql.dm b/code/modules/unit_tests/sql.dm deleted file mode 100644 index e9bbf7d1ebe..00000000000 --- a/code/modules/unit_tests/sql.dm +++ /dev/null @@ -1,10 +0,0 @@ -// Unit test to check SQL version has been updated properly., -/datum/unit_test/sql_version/Run() - // Check if the SQL version set in the code is equal to the CI DB config - if(GLOB.configuration.database.version != SQL_VERSION) - Fail("SQL version error: Game is running V[SQL_VERSION] but config is V[GLOB.configuration.database.version]. You may need to update the example config.") - - if(SSdbcore.total_errors > 0) - Fail("SQL errors occurred on startup. Please fix them.") - - diff --git a/code/modules/unit_tests/subsystem_metric_sanity.dm b/code/modules/unit_tests/subsystem_metric_sanity.dm deleted file mode 100644 index 250e6b4cc39..00000000000 --- a/code/modules/unit_tests/subsystem_metric_sanity.dm +++ /dev/null @@ -1,22 +0,0 @@ -// Unit test to ensure SS metrics are valid -/datum/unit_test/subsystem_metric_sanity/Run() - for(var/datum/controller/subsystem/SS in Master.subsystems) - var/list/data = SS.get_metrics() - if(length(data) != 4) - Fail("SS[SS.ss_id] has invalid metrics data!") - continue - if(isnull(data["cost"])) - Fail("SS[SS.ss_id] has invalid metrics data! No 'cost' found in [json_encode(data)]") - continue - if(isnull(data["tick_usage"])) - Fail("SS[SS.ss_id] has invalid metrics data! No 'tick_usage' found in [json_encode(data)]") - continue - if(isnull(data["custom"])) - Fail("SS[SS.ss_id] has invalid metrics data! No 'custom' found in [json_encode(data)]") - continue - if(!islist(data["custom"])) - Fail("SS[SS.ss_id] has invalid metrics data! 'custom' is not a list in [json_encode(data)]") - continue - if(isnull(data["sleep_count"])) - Fail("SS[SS.ss_id] has invalid metrics data! No 'sleep_count' found in [json_encode(data)]") - continue diff --git a/code/modules/unit_tests/timer_sanity.dm b/code/modules/unit_tests/timer_sanity.dm deleted file mode 100644 index d92323a5253..00000000000 --- a/code/modules/unit_tests/timer_sanity.dm +++ /dev/null @@ -1,3 +0,0 @@ -/datum/unit_test/timer_sanity/Run() - if(SStimer.bucket_count < 0) - Fail("SStimer is going into negative bucket count from something") diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm deleted file mode 100644 index 6ef3c82d85c..00000000000 --- a/code/modules/unit_tests/unit_test.dm +++ /dev/null @@ -1,40 +0,0 @@ -/* -Usage: -Override /Run() to run your test code -Call Fail() to fail the test (You should specify a reason) -You may use /New() and /Destroy() for setup/teardown respectively -You can use the run_loc_bottom_left and run_loc_top_right to get turfs for testing -*/ - -/datum/unit_test - //Bit of metadata for the future maybe - var/list/procs_tested - - //usable vars - var/turf/run_loc_bottom_left - var/turf/run_loc_top_right - - //internal shit - var/succeeded = TRUE - var/list/fail_reasons - -/datum/unit_test/New() - run_loc_bottom_left = locate(1, 1, 1) - run_loc_top_right = locate(5, 5, 1) - -/datum/unit_test/Destroy() - //clear the test area - for(var/atom/movable/AM in block(run_loc_bottom_left, run_loc_top_right)) - qdel(AM) - return ..() - -/datum/unit_test/proc/Run() - Fail("Run() called parent or not implemented") - -/datum/unit_test/proc/Fail(reason = "No reason") - succeeded = FALSE - - if(!istext(reason)) - reason = "FORMATTED: [reason != null ? reason : "NULL"]" - - LAZYADD(fail_reasons, reason) diff --git a/code/tests/_game_test.dm b/code/tests/_game_test.dm new file mode 100644 index 00000000000..784540d7759 --- /dev/null +++ b/code/tests/_game_test.dm @@ -0,0 +1,96 @@ +/// For advanced cases, fail unconditionally but don't return (so a test can return multiple results) +#define TEST_FAIL(reason) (Fail(reason || "No reason", __FILE__, __LINE__)) + +/// Asserts that a condition is true +/// If the condition is not true, fails the test +#define TEST_ASSERT(assertion, reason) if(!(assertion)) { return Fail("Assertion failed: [reason || "No reason"]", __FILE__, __LINE__) } + +#define TEST_ASSERT_NOT(assertion, reason) if(assertion) { return Fail("Assertion failed: [reason || "No reason"]", __FILE__, __LINE__) } + +/// Asserts that a parameter is not null +#define TEST_ASSERT_NOTNULL(a, reason) if(isnull(a)) { return Fail("Expected non-null value: [reason || "No reason"]", __FILE__, __LINE__) } + +/// Asserts that a parameter is null +#define TEST_ASSERT_NULL(a, reason) if(!isnull(a)) { return Fail("Expected null value but received [a]: [reason || "No reason"]", __FILE__, __LINE__) } + +/// Asserts that the two parameters passed are equal, fails otherwise +/// Optionally allows an additional message in the case of a failure +#define TEST_ASSERT_EQUAL(a, b, message) do { \ + var/lhs = ##a; \ + var/rhs = ##b; \ + if(lhs != rhs) { \ + return Fail("Expected [isnull(lhs) ? "null" : lhs] to be equal to [isnull(rhs) ? "null" : rhs].[message ? " [message]" : ""]", __FILE__, __LINE__); \ + } \ +} while(FALSE) + +/// Asserts that the two parameters passed are not equal, fails otherwise +/// Optionally allows an additional message in the case of a failure +#define TEST_ASSERT_NOTEQUAL(a, b, message) do { \ + var/lhs = ##a; \ + var/rhs = ##b; \ + if(lhs == rhs) { \ + return Fail("Expected [isnull(lhs) ? "null" : lhs] to not be equal to [isnull(rhs) ? "null" : rhs].[message ? " [message]" : ""]", __FILE__, __LINE__); \ + } \ +} while(FALSE) + +/** + * Usage: + * + * - Override /Run() to run your test code + * - Call Fail() to fail the test (You should specify a reason) + * - You may use /New() and /Destroy() for setup/teardown respectively + * - You can use the run_loc_bottom_left and run_loc_top_right to get turfs for testing + * +**/ +/datum/game_test + //Bit of metadata for the future maybe + var/list/procs_tested + + //usable vars + var/turf/run_loc_bottom_left + var/turf/run_loc_top_right + + //internal shit + var/succeeded = TRUE + var/list/allocated + var/list/fail_reasons + +/datum/game_test/New() + run_loc_bottom_left = locate(1, 1, 1) + run_loc_top_right = locate(5, 5, 1) + +/datum/game_test/Destroy() + QDEL_LIST_CONTENTS(allocated) + //clear the test area + for(var/atom/movable/AM in block(run_loc_bottom_left, run_loc_top_right)) + qdel(AM) + return ..() + +/datum/game_test/proc/Run() + Fail("Run() called parent or not implemented") + +/datum/game_test/proc/Fail(reason = "No reason", file = "OUTDATED_TEST", line = 1) + succeeded = FALSE + + if(!istext(reason)) + reason = "FORMATTED: [reason != null ? reason : "NULL"]" + + LAZYADD(fail_reasons, list(list(reason, file, line))) + +/// Allocates an instance of the provided type, and places it somewhere in an available loc +/// Instances allocated through this proc will be destroyed when the test is over +/datum/game_test/proc/allocate(type, ...) + var/list/arguments = args.Copy(2) + if(ispath(type, /atom)) + if(!arguments.len) + arguments = list(run_loc_bottom_left) + else if(arguments[1] == null) + arguments[1] = run_loc_bottom_left + var/instance + // Byond will throw an index out of bounds if arguments is empty in that arglist call. Sigh + if(length(arguments)) + instance = new type(arglist(arguments)) + else + instance = new type() + LAZYADD(allocated, instance) + return instance diff --git a/code/modules/unit_tests/atmos/test_ventcrawl.dm b/code/tests/atmos/test_ventcrawl.dm similarity index 69% rename from code/modules/unit_tests/atmos/test_ventcrawl.dm rename to code/tests/atmos/test_ventcrawl.dm index b4b153d1953..fd4ca7c4e38 100644 --- a/code/modules/unit_tests/atmos/test_ventcrawl.dm +++ b/code/tests/atmos/test_ventcrawl.dm @@ -1,4 +1,4 @@ -/datum/unit_test/ventcrawl +/datum/game_test/ventcrawl var/mob/living/simple_animal/slime = null var/obj/machinery/vent = null var/obj/structure/table/table = null @@ -6,7 +6,7 @@ /datum/milla_safe/ventcrawl_test_setup -/datum/milla_safe/ventcrawl_test_setup/on_run(datum/unit_test/ventcrawl/test) +/datum/milla_safe/ventcrawl_test_setup/on_run(datum/game_test/ventcrawl/test) // This setup creates turfs that initialize themselves in MILLA on creation, which is why we need to be MILLA-safe. var/datum/map_template/template = GLOB.map_templates["test_ventcrawl.dmm"] if(!template.load(test.run_loc_bottom_left)) @@ -17,13 +17,13 @@ test.table = test.find_spawned_test_object(get_step(test.run_loc_bottom_left, EAST), /obj/structure/table) test.setup_complete = TRUE -/datum/unit_test/ventcrawl/proc/find_spawned_test_object(turf/location as turf, test_object_type) +/datum/game_test/ventcrawl/proc/find_spawned_test_object(turf/location as turf, test_object_type) for(var/content in location.contents) if(istype(content, test_object_type)) return content - Fail("Couldn't find spawned test object of type: [test_object_type].") + TEST_FAIL("Couldn't find spawned test object of type: [test_object_type].") -/datum/unit_test/ventcrawl/Run() +/datum/game_test/ventcrawl/Run() var/datum/milla_safe/ventcrawl_test_setup/milla = new() milla.invoke_async(src) while(!setup_complete) @@ -31,20 +31,16 @@ // Enter vent vent.AltClick(slime) - if(slime.loc != vent) - Fail("Failed to crawl into vent.") + TEST_ASSERT_EQUAL(slime.loc, vent, "failed to crawl into vent.") // Movement slime.loc.relaymove(slime, EAST) - if(slime.loc == vent) - Fail("Failed to step EAST while wentcrawling.") + TEST_ASSERT_NOTEQUAL(slime.loc, vent, "failed to step EAST while ventcrawling.") // Try to flip table on top of pipe, while inside pipe (shouldn't work) table.AltShiftClick(slime) - if(table.flipped) - Fail("Shouldn't be possible to flip structures while inside vent.") + TEST_ASSERT_NOT(table.flipped, "Shouldn't be possible to flip structures while inside vent.") // Exit vent slime.loc.relaymove(slime, EAST) - if(!isturf(slime.loc)) - Fail("Wasn't able to ventcrawl out of vent.") + TEST_ASSERT(isturf(slime.loc), "wasn't able to ventcrawl out of vent.") diff --git a/code/tests/atmos/test_ventcrawl.dmm b/code/tests/atmos/test_ventcrawl.dmm new file mode 100644 index 00000000000..e7e84c5acd9 --- /dev/null +++ b/code/tests/atmos/test_ventcrawl.dmm @@ -0,0 +1,30 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/obj/machinery/atmospherics/unary/vent_pump/on{ + dir = 4 + }, +/turf/simulated/floor, +/area/space) +"d" = ( +/obj/machinery/atmospherics/unary/vent_pump/on{ + dir = 8 + }, +/turf/simulated/floor, +/area/space) +"X" = ( +/obj/machinery/atmospherics/pipe/simple/visible{ + dir = 4 + }, +/obj/structure/table, +/turf/simulated/floor, +/area/space) + +(1,1,1) = {" +a +"} +(2,1,1) = {" +X +"} +(3,1,1) = {" +d +"} diff --git a/code/tests/game_tests.dm b/code/tests/game_tests.dm new file mode 100644 index 00000000000..c7c35e38667 --- /dev/null +++ b/code/tests/game_tests.dm @@ -0,0 +1,34 @@ +//include game test files in this module in this ifdef +//Keep this sorted alphabetically + +#ifdef GAME_TESTS +#include "_game_test.dm" +#include "atmos\test_ventcrawl.dm" +#include "games\test_cards.dm" +#include "jobs\test_job_globals.dm" +#include "test_aicard_icons.dm" +#include "test_announcements.dm" +#include "test_areas_apcs.dm" +#include "test_components.dm" +#include "test_config_sanity.dm" +#include "test_crafting_lists.dm" +#include "test_elements.dm" +#include "test_emotes.dm" +#include "test_init_sanity.dm" +#include "test_log_format.dm" +#include "test_map_templates.dm" +#include "test_map_tests.dm" +#include "test_missing_icons.dm" +#include "test_origin_tech.dm" +#include "test_purchase_reference_test.dm" +#include "test_reagent_id_typos.dm" +#include "test_runner.dm" +#include "test_rustg_version.dm" +#include "test_spawn_humans.dm" +#include "test_spell_targeting_test.dm" +#include "test_sql.dm" +#include "test_status_effect_ids.dm" +#include "test_subsystem_init.dm" +#include "test_subsystem_metric_sanity.dm" +#include "test_timer_sanity.dm" +#endif diff --git a/code/modules/unit_tests/games/test_cards.dm b/code/tests/games/test_cards.dm similarity index 68% rename from code/modules/unit_tests/games/test_cards.dm rename to code/tests/games/test_cards.dm index bde0b9f1f8b..209fdfb92c1 100644 --- a/code/modules/unit_tests/games/test_cards.dm +++ b/code/tests/games/test_cards.dm @@ -1,4 +1,4 @@ -/datum/unit_test/card_deck/proc/validate_deck(obj/item/deck/deck) +/datum/game_test/card_deck/proc/validate_deck(obj/item/deck/deck) var/list/card_count = list() for(var/datum/playingcard/card in deck.cards) if(card_count[card.name] == null) @@ -14,17 +14,17 @@ return TRUE -/datum/unit_test/card_deck/Run() +/datum/game_test/card_deck/Run() // setup var/loc = pick(block(run_loc_bottom_left, run_loc_top_right)) - var/obj/item/deck/cards/cards = new /obj/item/deck/cards(loc) + var/obj/item/deck/cards/cards = allocate(/obj/item/deck/cards, loc) cards.build_decks() // is deck proper upon spawning if(!validate_deck(cards)) - Fail("52 card deck not initialized correctly.") + TEST_FAIL("52 card deck not initialized correctly.") // is deck proper after shuffling cards.deckshuffle() if(!validate_deck(cards)) - Fail("52 card deck broken after shuffling.") + TEST_FAIL("52 card deck broken after shuffling.") diff --git a/code/modules/unit_tests/jobs/test_job_globals.dm b/code/tests/jobs/test_job_globals.dm similarity index 77% rename from code/modules/unit_tests/jobs/test_job_globals.dm rename to code/tests/jobs/test_job_globals.dm index 7ed56ff9980..eebf80becb6 100644 --- a/code/modules/unit_tests/jobs/test_job_globals.dm +++ b/code/tests/jobs/test_job_globals.dm @@ -1,17 +1,17 @@ -/datum/unit_test/job_globals/Run() +/datum/game_test/job_globals/Run() return -/datum/unit_test/job_globals/proc/is_list_unique(list/L) +/datum/game_test/job_globals/proc/is_list_unique(list/L) var/list_length = length(L) var/unique_list = uniqueList(L) var/unique_list_length = length(unique_list) return list_length == unique_list_length -/datum/unit_test/job_globals/proc/validate_list(list/L, list_name) +/datum/game_test/job_globals/proc/validate_list(list/L, list_name) if(!is_list_unique(L)) - Fail("job_globals list '[list_name]' contains duplicate values.") + TEST_FAIL("job_globals list '[list_name]' contains duplicate values.") -/datum/unit_test/job_globals/no_duplicates/Run() +/datum/game_test/job_globals/no_duplicates/Run() validate_list(GLOB.station_departments, "station_departments") validate_list(GLOB.command_positions, "command_positions") validate_list(GLOB.command_head_positions, "command_head_positions") diff --git a/code/modules/unit_tests/aicard_icons.dm b/code/tests/test_aicard_icons.dm similarity index 65% rename from code/modules/unit_tests/aicard_icons.dm rename to code/tests/test_aicard_icons.dm index cb61ade842f..b35e368d5b9 100644 --- a/code/modules/unit_tests/aicard_icons.dm +++ b/code/tests/test_aicard_icons.dm @@ -1,6 +1,6 @@ -/datum/unit_test/aicard_icons/Run() - var/mob/living/silicon/ai/ai_box = new /mob/living/silicon/ai/ - var/obj/item/aicard/int_card = new /obj/item/aicard/ +/datum/game_test/aicard_icons/Run() + var/mob/living/silicon/ai/ai_box = allocate(/mob/living/silicon/ai) + var/obj/item/aicard/int_card = allocate(/obj/item/aicard) var/list/ai_icon_list = icon_states(ai_box.icon) var/list/aicard_icon_list = icon_states(int_card.icon) @@ -14,6 +14,6 @@ if(icn_st in aicard_icon_list) continue - Fail("Every AI Display must have a corresponding icon of the same name in [int_card.icon] for intelicards, [icn_st] is missing!") + TEST_FAIL("Every AI Display must have a corresponding icon of the same name in [int_card.icon] for intelicards, [icn_st] is missing!") //The exclusions list will need to be updated anytime any non-display icons are added to ai.dmi diff --git a/code/modules/unit_tests/announcements.dm b/code/tests/test_announcements.dm similarity index 97% rename from code/modules/unit_tests/announcements.dm rename to code/tests/test_announcements.dm index 152e8cadc68..c4feea561ac 100644 --- a/code/modules/unit_tests/announcements.dm +++ b/code/tests/test_announcements.dm @@ -1,8 +1,8 @@ /// This test exists largely to ensure that no runtimes occur when announcements /// are made, so there are no explicit Fail calls. It either works or runtimes. -/datum/unit_test/announcements +/datum/game_test/announcements -/datum/unit_test/announcements/Run() +/datum/game_test/announcements/Run() GLOB.major_announcement.Announce("Figments from an eldritch god are being summoned into the NSS Cyberiad from an unknown dimension. Disrupt the ritual at all costs, before the station is destroyed! Space Law and SOP are suspended. The entire crew must kill cultists on sight.", "Central Command Higher Dimensional Affairs", 'sound/AI/cult_summon.ogg') GLOB.major_announcement.Announce( diff --git a/code/modules/unit_tests/areas_apcs.dm b/code/tests/test_areas_apcs.dm similarity index 71% rename from code/modules/unit_tests/areas_apcs.dm rename to code/tests/test_areas_apcs.dm index 00bd31805c0..ecf21dc19da 100644 --- a/code/modules/unit_tests/areas_apcs.dm +++ b/code/tests/test_areas_apcs.dm @@ -1,17 +1,17 @@ -/datum/unit_test/area_apcs +/datum/game_test/area_apcs /// Sometimes, areas may have power, or not. We dont really care about these areas. var/list/optional_areas = list(/area/station/science/toxins/test, /area/station/maintenance/electrical_shop) -/datum/unit_test/area_apcs/Run() +/datum/game_test/area_apcs/Run() for(var/area/station/A in SSmapping.existing_station_areas) if(A.there_can_be_many || A.apc_starts_off || !A.requires_power) continue if(is_type_in_list(A, optional_areas)) continue if(length(A.apc) == 0) - Fail("Area [A.type] has [length(A.apc)] apcs, instead of 1.") + TEST_FAIL("Area [A.type] has [length(A.apc)] apcs, instead of 1.") else if(length(A.apc) > 1) var/list/locations = list() for(var/atom/probably_an_apc as anything in A.apc) locations += "([probably_an_apc.x], [probably_an_apc.y], [probably_an_apc.z])" - Fail("Area [A.type] has [length(A.apc)] apcs, instead of 1. APCs are located at [english_list(locations)]") + TEST_FAIL("Area [A.type] has [length(A.apc)] apcs, instead of 1. APCs are located at [english_list(locations)]") diff --git a/code/modules/unit_tests/component_tests.dm b/code/tests/test_components.dm similarity index 64% rename from code/modules/unit_tests/component_tests.dm rename to code/tests/test_components.dm index 0099d7508c5..0e0933b4e15 100644 --- a/code/modules/unit_tests/component_tests.dm +++ b/code/tests/test_components.dm @@ -1,4 +1,4 @@ -/datum/unit_test/component_duping/Run() +/datum/game_test/component_duping/Run() var/list/bad_dms = list() var/list/bad_dts = list() for(var/t in typesof(/datum/component)) @@ -9,4 +9,4 @@ if(dupe_type && !ispath(dupe_type)) bad_dts += t if(length(bad_dms) || length(bad_dts)) - Fail("Components with invalid dupe modes: ([bad_dms.Join(",")]) ||| Components with invalid dupe types: ([bad_dts.Join(",")])") + TEST_FAIL("Components with invalid dupe modes: ([bad_dms.Join(",")]) ||| Components with invalid dupe types: ([bad_dts.Join(",")])") diff --git a/code/modules/unit_tests/config_sanity.dm b/code/tests/test_config_sanity.dm similarity index 76% rename from code/modules/unit_tests/config_sanity.dm rename to code/tests/test_config_sanity.dm index 55f84fa98ca..52934e87fda 100644 --- a/code/modules/unit_tests/config_sanity.dm +++ b/code/tests/test_config_sanity.dm @@ -1,5 +1,5 @@ // This one test does multiple config things -/datum/unit_test/config_sanity/Run() +/datum/game_test/config_sanity/Run() // First test the ruins. Space then lava. var/list/config_space_ruins = GLOB.configuration.ruins.active_space_ruins.Copy() // Copy so we dont remove var/list/datum/map_template/ruin/space/game_space_ruins = list() @@ -20,14 +20,14 @@ // Do not confuse this with the map_templates unit test. They do different things!!!!! if(length(game_space_ruins)) - Fail("Space ruins exist in the game code that do not exist in the config file") + TEST_FAIL("Space ruins exist in the game code that do not exist in the config file") for(var/datum/map_template/ruin/space/S in game_space_ruins) - Fail("Ruin [S.type] does not have a valid map path ([S.mappath])") + TEST_FAIL("Ruin [S.type] does not have a valid map path ([S.mappath])") if(length(config_space_ruins)) - Fail("Space ruins exist in the game config that do not have associated datums") + TEST_FAIL("Space ruins exist in the game config that do not have associated datums") for(var/path in config_space_ruins) - Fail("- [path]") + TEST_FAIL("- [path]") // Now for lava ruins @@ -50,11 +50,11 @@ // Do not confuse this with the map_templates unit test. They do different things!!!!! if(length(game_lava_ruins)) - Fail("Lava ruins exist in the game code that do not exist in the config file") + TEST_FAIL("Lava ruins exist in the game code that do not exist in the config file") for(var/datum/map_template/ruin/lavaland/L in game_lava_ruins) - Fail("Ruin [L.type] does not have a valid map path ([L.mappath])") + TEST_FAIL("Ruin [L.type] does not have a valid map path ([L.mappath])") if(length(config_lava_ruins)) - Fail("Lava ruins exist in the game config that do not have associated datums") + TEST_FAIL("Lava ruins exist in the game config that do not have associated datums") for(var/path in config_lava_ruins) - Fail("- [path]") + TEST_FAIL("- [path]") diff --git a/code/tests/test_crafting_lists.dm b/code/tests/test_crafting_lists.dm new file mode 100644 index 00000000000..7fa26ffdc26 --- /dev/null +++ b/code/tests/test_crafting_lists.dm @@ -0,0 +1,4 @@ +/datum/game_test/crafting_lists/Run() + for(var/I in subtypesof(/datum/crafting_recipe)) + var/datum/crafting_recipe/C = new I() + TEST_ASSERT(islist(C.result), "Expected a list for the 'result' of [C.type].") diff --git a/code/modules/unit_tests/element_tests.dm b/code/tests/test_elements.dm similarity index 58% rename from code/modules/unit_tests/element_tests.dm rename to code/tests/test_elements.dm index d497984ea49..c9b51fd7d7e 100644 --- a/code/modules/unit_tests/element_tests.dm +++ b/code/tests/test_elements.dm @@ -1,4 +1,4 @@ -/datum/unit_test/bespoke_element/Run() +/datum/game_test/bespoke_element/Run() for(var/datum/element/element_type as anything in subtypesof(/datum/element)) if(initial(element_type.element_flags) & ELEMENT_BESPOKE && initial(element_type.argument_hash_start_idx) == INFINITY) - Fail("Element type [element_type] has ELEMENT_BESPOKE and a default argument_hash_start_idx.") + TEST_FAIL("Element type [element_type] has ELEMENT_BESPOKE and a default argument_hash_start_idx.") diff --git a/code/modules/unit_tests/emotes.dm b/code/tests/test_emotes.dm similarity index 67% rename from code/modules/unit_tests/emotes.dm rename to code/tests/test_emotes.dm index c231c031a15..88ef8ae63e4 100644 --- a/code/modules/unit_tests/emotes.dm +++ b/code/tests/test_emotes.dm @@ -1,6 +1,6 @@ -/datum/unit_test/emote/Run() +/datum/game_test/emote/Run() // Special cases that shouldn't need keybinds. var/list/ignored_emote_types = list( @@ -15,33 +15,30 @@ for(var/emote_type in subtypesof(/datum/emote)) var/datum/emote/cur_emote = new emote_type() if(cur_emote.message_param && !cur_emote.param_desc) - Fail("emote [cur_emote] was given a message parameter without a description.") + TEST_FAIL("emote [cur_emote] was given a message parameter without a description.") // Sanity checks, these emotes probably won't appear to a user but we should make sure they're cleaned up. if(!cur_emote.key) if(cur_emote.message || cur_emote.message_param) - Fail("emote [cur_emote] is missing a key but has a message defined.") + TEST_FAIL("emote [cur_emote] is missing a key but has a message defined.") if(cur_emote.key_third_person) - Fail("emote [cur_emote] has a third-person key defined, but no first-person key. Either first person, both, or neither should be defined.") + TEST_FAIL("emote [cur_emote] has a third-person key defined, but no first-person key. Either first person, both, or neither should be defined.") // These are ones that might appear to a user, and so could use some special handling. else - if(isnull(cur_emote.emote_type)) - Fail("emote [cur_emote] has a null target type.") + TEST_ASSERT_NOTNULL(cur_emote.emote_type, "emote [cur_emote] has a null target type.") // If we're at this point, we're definitely an emote that a user could use, and therefore ought to make sure it's bound to a keybind if possible. if(!is_type_in_list(cur_emote, keybound_emotes) && !is_type_in_list(cur_emote, ignored_emote_types)) - Fail("Emote [cur_emote] is usable, but not assigned a keybind.") + TEST_FAIL("Emote [cur_emote] is usable, but not assigned a keybind.") if(isnum(cur_emote.max_stat_allowed) && cur_emote.max_stat_allowed < cur_emote.stat_allowed) - Fail("emote [cur_emote]'s max_stat_allowed is greater than its stat_allowed, and would be unusable.") + TEST_FAIL("emote [cur_emote]'s max_stat_allowed is greater than its stat_allowed, and would be unusable.") if(isnum(cur_emote.max_unintentional_stat_allowed) && cur_emote.max_unintentional_stat_allowed < cur_emote.unintentional_stat_allowed) - Fail("emote [cur_emote]'s max_unintentional_stat_allowed is greater than its unintentional_stat_allowed, and would be unusable.") + TEST_FAIL("emote [cur_emote]'s max_unintentional_stat_allowed is greater than its unintentional_stat_allowed, and would be unusable.") - - -/datum/unit_test/emote/proc/get_emote_keybinds() +/datum/game_test/emote/proc/get_emote_keybinds() var/list/bound_emotes = list() for(var/keybind in subtypesof(/datum/keybinding/emote)) var/datum/keybinding/emote/E = new keybind() diff --git a/code/modules/unit_tests/init_sanity.dm b/code/tests/test_init_sanity.dm similarity index 60% rename from code/modules/unit_tests/init_sanity.dm rename to code/tests/test_init_sanity.dm index 1584179cbe7..b21263175d8 100644 --- a/code/modules/unit_tests/init_sanity.dm +++ b/code/tests/test_init_sanity.dm @@ -1,6 +1,6 @@ -/datum/unit_test/initialize_sanity/Run() +/datum/game_test/initialize_sanity/Run() if(length(SSatoms.BadInitializeCalls)) - Fail("Bad Initialize() calls detected. Please read logs.") + TEST_FAIL("Bad Initialize() calls detected. Please read logs.") var/list/init_failures_to_text = list( "[BAD_INIT_QDEL_BEFORE]" = "Qdeleted Before Initialized", "[BAD_INIT_DIDNT_INIT]" = "Did Not Initialize", @@ -8,4 +8,4 @@ "[BAD_INIT_NO_HINT]" = "No Initialize() Hint Returned", ) for(var/failure in SSatoms.BadInitializeCalls) - Fail("[failure]: [init_failures_to_text["[SSatoms.BadInitializeCalls[failure]]"]]") // You like stacked brackets? + TEST_FAIL("[failure]: [init_failures_to_text["[SSatoms.BadInitializeCalls[failure]]"]]") // You like stacked brackets? diff --git a/code/modules/unit_tests/log_format.dm b/code/tests/test_log_format.dm similarity index 81% rename from code/modules/unit_tests/log_format.dm rename to code/tests/test_log_format.dm index 11bcd9fd9fe..d3a6f67ca49 100644 --- a/code/modules/unit_tests/log_format.dm +++ b/code/tests/test_log_format.dm @@ -8,7 +8,7 @@ return "\[[date_portion]T[time_portion]] [TEST_MESSAGE]" -/datum/unit_test/log_format/Run() +/datum/game_test/log_format/Run() // Generate a list of valid log timestamps. It can be the current time, or up to 2 seconds later to account for spurious CI lag var/valid_lines = list( "[generate_test_log_message(world.timeofday)]", @@ -19,8 +19,8 @@ rustg_log_write(TEST_LOG_FILE, TEST_MESSAGE) var/list/lines = file2list(TEST_LOG_FILE) - if(!(lines[1] in valid_lines)) - Fail("RUSTG log format is not valid 8601 format. Expected '[generate_test_log_message(world.timeofday)]', got '[lines[1]]'") + TEST_ASSERT(lines[1] in valid_lines, \ + "RUSTG log format is not valid 8601 format. Expected '[generate_test_log_message(world.timeofday)]', got '[lines[1]]'") #undef TEST_MESSAGE #undef TEST_LOG_FILE diff --git a/code/modules/unit_tests/map_templates.dm b/code/tests/test_map_templates.dm similarity index 71% rename from code/modules/unit_tests/map_templates.dm rename to code/tests/test_map_templates.dm index 55834d8d171..6eb6da6a234 100644 --- a/code/modules/unit_tests/map_templates.dm +++ b/code/tests/test_map_templates.dm @@ -1,4 +1,4 @@ -/datum/unit_test/map_templates/Run() +/datum/game_test/map_templates/Run() var/list/datum/map_template/templates = subtypesof(/datum/map_template) for(var/I in templates) var/datum/map_template/MT = new I // The new is important here to ensure stuff gets set properly @@ -6,6 +6,6 @@ continue // Check if it even has a path and if so, does it exist if(MT.mappath && !fexists(MT.mappath)) - Fail("The map file for [MT.type] does not exist!") + TEST_FAIL("The map file for [MT.type] does not exist!") if(MT.mappath && !findtext(MT.mappath, ".dmm")) - Fail("The map file for [MT.type] is not a map!") + TEST_FAIL("The map file for [MT.type] is not a map!") diff --git a/code/modules/unit_tests/map_tests.dm b/code/tests/test_map_tests.dm similarity index 100% rename from code/modules/unit_tests/map_tests.dm rename to code/tests/test_map_tests.dm diff --git a/code/modules/unit_tests/missing_icons.dm b/code/tests/test_missing_icons.dm similarity index 86% rename from code/modules/unit_tests/missing_icons.dm rename to code/tests/test_missing_icons.dm index 17822595c06..0b652a3c466 100644 --- a/code/modules/unit_tests/missing_icons.dm +++ b/code/tests/test_missing_icons.dm @@ -1,8 +1,8 @@ /// Makes sure objects actually have icons that exist! -/datum/unit_test/missing_icons +/datum/game_test/missing_icons var/static/list/possible_icon_states = list() -/datum/unit_test/missing_icons/proc/generate_possible_icon_states_list(directory_path = "icons/obj/") +/datum/game_test/missing_icons/proc/generate_possible_icon_states_list(directory_path = "icons/obj/") for(var/file_path in flist(directory_path)) if(findtext(file_path, ".dmi")) for(var/sprite_icon in icon_states("[directory_path][file_path]", 1)) //2nd arg = 1 enables 64x64+ icon support, otherwise you'll end up with "sword0_1" instead of "sword" @@ -10,7 +10,7 @@ else possible_icon_states += generate_possible_icon_states_list("[directory_path][file_path]") -/datum/unit_test/missing_icons/Run() +/datum/game_test/missing_icons/Run() generate_possible_icon_states_list() generate_possible_icon_states_list("icons/effects/") @@ -41,5 +41,5 @@ for(var/file_place in possible_icon_states[icon_state]) match_message += (match_message ? " & '[file_place]'" : " - Matching sprite found in: '[file_place]'") - Fail("Missing icon_state for [obj_path] in '[icon]'.\n\ticon_state = \"[icon_state]\"[match_message]") + TEST_FAIL("Missing icon_state for [obj_path] in '[icon]'.\n\ticon_state = \"[icon_state]\"[match_message]") diff --git a/code/modules/unit_tests/origin_tech.dm b/code/tests/test_origin_tech.dm similarity index 71% rename from code/modules/unit_tests/origin_tech.dm rename to code/tests/test_origin_tech.dm index 177c89b36b9..5dc7b475e56 100644 --- a/code/modules/unit_tests/origin_tech.dm +++ b/code/tests/test_origin_tech.dm @@ -1,5 +1,5 @@ // Unit test to ensure people have appropriately set up their origin_techs in items, ensuring they give the appropriate resources in RnD breakdown -/datum/unit_test/origin_tech/Run() +/datum/game_test/origin_tech/Run() var/regex/nums = regex("^\[0-9]+") for(var/tpath in subtypesof(/obj/item)) var/obj/item/I = tpath @@ -8,5 +8,4 @@ var/list/tech_list = params2list(tech_str) for(var/k in tech_list) - if(length(nums.Replace(tech_list[k], "")) > 0) - Fail("Invalid origin tech for [tpath]: [tech_str]") + TEST_ASSERT(length(nums.Replace(tech_list[k], "")) == 0, "Invalid origin tech for [tpath]: [tech_str]") diff --git a/code/modules/unit_tests/purchase_reference_test.dm b/code/tests/test_purchase_reference_test.dm similarity index 66% rename from code/modules/unit_tests/purchase_reference_test.dm rename to code/tests/test_purchase_reference_test.dm index 91dca4b846a..42dc5d22f09 100644 --- a/code/modules/unit_tests/purchase_reference_test.dm +++ b/code/tests/test_purchase_reference_test.dm @@ -1,14 +1,13 @@ // tests if there are duplicate or null refs/lognames for uplink items and spellbook items respectively -/datum/unit_test/uplink_refs/Run() +/datum/game_test/uplink_refs/Run() var/list/uplink_refs = list() for(var/datum/uplink_item/I as anything in subtypesof(/datum/uplink_item)) if(isnull(initial(I.item))) continue //don't test them if they don't have an item var/uplink_ref = initial(I.reference) if(isnull(uplink_ref)) - Fail("uplink item [initial(I.name)] has no reference") + TEST_FAIL("uplink item [initial(I.name)] has no reference") continue - if(uplink_ref in uplink_refs) - Fail("uplink reference [uplink_ref] is used multiple times") + TEST_ASSERT_NOT(uplink_ref in uplink_refs, "uplink reference [uplink_ref] is used multiple times") uplink_refs += uplink_ref diff --git a/code/modules/unit_tests/reagent_id_typos.dm b/code/tests/test_reagent_id_typos.dm similarity index 52% rename from code/modules/unit_tests/reagent_id_typos.dm rename to code/tests/test_reagent_id_typos.dm index 7f45774db0b..93d6e4fd6b9 100644 --- a/code/modules/unit_tests/reagent_id_typos.dm +++ b/code/tests/test_reagent_id_typos.dm @@ -1,11 +1,10 @@ -/datum/unit_test/reagent_id_typos +/datum/game_test/reagent_id_typos -/datum/unit_test/reagent_id_typos/Run() +/datum/game_test/reagent_id_typos/Run() for(var/I in GLOB.chemical_reactions_list) for(var/V in GLOB.chemical_reactions_list[I]) var/datum/chemical_reaction/R = V for(var/id in (R.required_reagents + R.required_catalysts)) - if(!GLOB.chemical_reagents_list[id]) - Fail("Unknown chemical id \"[id]\" in recipe [R.type]") + TEST_ASSERT(GLOB.chemical_reagents_list[id], "Unknown chemical id \"[id]\" in recipe [R.type]") diff --git a/code/modules/unit_tests/test_runner.dm b/code/tests/test_runner.dm similarity index 95% rename from code/modules/unit_tests/test_runner.dm rename to code/tests/test_runner.dm index 8ac625e5c12..2d43ad51655 100644 --- a/code/modules/unit_tests/test_runner.dm +++ b/code/tests/test_runner.dm @@ -3,14 +3,14 @@ // intentionally (if there's a lot of legitimate map errors), or accidentally if // a test condition is written incorrectly and starts e.g. logging failures for // every single tile. -#ifdef LOCAL_UNIT_TESTS +#ifdef LOCAL_GAME_TESTS #define MAX_MAP_TEST_FAILURE_COUNT 100 #else #define MAX_MAP_TEST_FAILURE_COUNT 20 #endif /datum/test_runner - var/datum/unit_test/current_test + var/datum/game_test/current_test var/failed_any_test = FALSE var/list/test_logs = list() var/list/durations = list() @@ -56,8 +56,8 @@ /datum/test_runner/proc/Run() CHECK_TICK - for(var/I in subtypesof(/datum/unit_test)) - var/datum/unit_test/test = new I + for(var/I in subtypesof(/datum/game_test)) + var/datum/game_test/test = new I test_logs[I] = list() current_test = test @@ -83,7 +83,7 @@ var/time = world.timeofday set waitfor = FALSE - #ifdef LOCAL_UNIT_TESTS + #ifdef LOCAL_GAME_TESTS emit_failures = TRUE #endif diff --git a/code/tests/test_rustg_version.dm b/code/tests/test_rustg_version.dm new file mode 100644 index 00000000000..de39b377bdc --- /dev/null +++ b/code/tests/test_rustg_version.dm @@ -0,0 +1,3 @@ +/datum/game_test/rustg_version/Run() + var/library_version = rustg_get_version() + TEST_ASSERT_EQUAL(library_version, RUST_G_VERSION, "invalid RUSTG Version") diff --git a/code/modules/unit_tests/spawn_humans.dm b/code/tests/test_spawn_humans.dm similarity index 69% rename from code/modules/unit_tests/spawn_humans.dm rename to code/tests/test_spawn_humans.dm index dd6dac92840..ca0a6d6bd4a 100644 --- a/code/modules/unit_tests/spawn_humans.dm +++ b/code/tests/test_spawn_humans.dm @@ -1,8 +1,8 @@ -/datum/unit_test/spawn_humans/Run() +/datum/game_test/spawn_humans/Run() var/locs = block(run_loc_bottom_left, run_loc_top_right) for(var/I in 1 to 5) - new /mob/living/carbon/human(pick(locs)) + allocate(/mob/living/carbon/human, pick(locs)) // There is a 5 second delay here so that all the items on the humans have time to initialize and spawn sleep(50) diff --git a/code/modules/unit_tests/spell_targeting_test.dm b/code/tests/test_spell_targeting_test.dm similarity index 68% rename from code/modules/unit_tests/spell_targeting_test.dm rename to code/tests/test_spell_targeting_test.dm index e1a18977439..7c66c40549b 100644 --- a/code/modules/unit_tests/spell_targeting_test.dm +++ b/code/tests/test_spell_targeting_test.dm @@ -1,4 +1,4 @@ -/datum/unit_test/spell_targeting/Run() +/datum/game_test/spell_targeting/Run() var/list/bad_spells = list() for(var/datum/spell/S as anything in typesof(/datum/spell)) if(initial(S.name) == "Spell") @@ -7,4 +7,4 @@ if(!S.targeting) bad_spells += S if(length(bad_spells)) - Fail("Spells without targeting found: [bad_spells.Join(", ")]") + TEST_FAIL("Spells without targeting found: [bad_spells.Join(", ")]") diff --git a/code/tests/test_sql.dm b/code/tests/test_sql.dm new file mode 100644 index 00000000000..01acc0ac287 --- /dev/null +++ b/code/tests/test_sql.dm @@ -0,0 +1,5 @@ +// Unit test to check SQL version has been updated properly., +/datum/game_test/sql_version/Run() + // Check if the SQL version set in the code is equal to the CI DB config + TEST_ASSERT_EQUAL(GLOB.configuration.database.version, SQL_VERSION, "SQL version error. You may need to update the example config.") + TEST_ASSERT_EQUAL(SSdbcore.total_errors, 0, "SQL errors occurred on startup.") diff --git a/code/modules/unit_tests/status_effect_ids.dm b/code/tests/test_status_effect_ids.dm similarity index 61% rename from code/modules/unit_tests/status_effect_ids.dm rename to code/tests/test_status_effect_ids.dm index febe085e1c7..a5354b2432c 100644 --- a/code/modules/unit_tests/status_effect_ids.dm +++ b/code/tests/test_status_effect_ids.dm @@ -1,8 +1,8 @@ -/datum/unit_test/status_effect_ids/Run() +/datum/game_test/status_effect_ids/Run() var/list/bad_statuses = list() for(var/datum/status_effect/effect as anything in subtypesof(/datum/status_effect)) if(initial(effect.id) == null) bad_statuses += effect if(length(bad_statuses)) - Fail("STatus effects found without an unique ID: [bad_statuses.Join(", ")]") + TEST_FAIL("Status effects found without an unique ID: [bad_statuses.Join(", ")]") diff --git a/code/modules/unit_tests/subsystem_init.dm b/code/tests/test_subsystem_init.dm similarity index 56% rename from code/modules/unit_tests/subsystem_init.dm rename to code/tests/test_subsystem_init.dm index 1515f12c591..0ea650c0d41 100644 --- a/code/modules/unit_tests/subsystem_init.dm +++ b/code/tests/test_subsystem_init.dm @@ -1,9 +1,9 @@ -/datum/unit_test/subsystem_init/Run() +/datum/game_test/subsystem_init/Run() var/datum/controller/subsystem/base_ss var/default_offline_implications = initial(base_ss.offline_implications) for(var/datum/controller/subsystem/SS as anything in Master.subsystems) if((SS.flags & SS_NO_INIT) && (SS.flags & SS_NO_FIRE)) - Fail("[SS]([SS.type]) is a subsystem which is set to not initialize or fire. Use a global datum instead an SS.") + TEST_FAIL("[SS]([SS.type]) is a subsystem which is set to not initialize or fire. Use a global datum instead an SS.") if(!(SS.flags & SS_NO_FIRE) && SS.offline_implications == default_offline_implications) - Fail("[SS]([SS.type]) is a subsystem which fires but has no offline implications set.") + TEST_FAIL("[SS]([SS.type]) is a subsystem which fires but has no offline implications set.") diff --git a/code/tests/test_subsystem_metric_sanity.dm b/code/tests/test_subsystem_metric_sanity.dm new file mode 100644 index 00000000000..932abf0835d --- /dev/null +++ b/code/tests/test_subsystem_metric_sanity.dm @@ -0,0 +1,22 @@ +// Unit test to ensure SS metrics are valid +/datum/game_test/subsystem_metric_sanity/Run() + for(var/datum/controller/subsystem/SS in Master.subsystems) + var/list/data = SS.get_metrics() + if(length(data) != 4) + TEST_FAIL("SS[SS.ss_id] has invalid metrics data!") + continue + if(isnull(data["cost"])) + TEST_FAIL("SS[SS.ss_id] has invalid metrics data! No 'cost' found in [json_encode(data)]") + continue + if(isnull(data["tick_usage"])) + TEST_FAIL("SS[SS.ss_id] has invalid metrics data! No 'tick_usage' found in [json_encode(data)]") + continue + if(isnull(data["custom"])) + TEST_FAIL("SS[SS.ss_id] has invalid metrics data! No 'custom' found in [json_encode(data)]") + continue + if(!islist(data["custom"])) + TEST_FAIL("SS[SS.ss_id] has invalid metrics data! 'custom' is not a list in [json_encode(data)]") + continue + if(isnull(data["sleep_count"])) + TEST_FAIL("SS[SS.ss_id] has invalid metrics data! No 'sleep_count' found in [json_encode(data)]") + continue diff --git a/code/tests/test_timer_sanity.dm b/code/tests/test_timer_sanity.dm new file mode 100644 index 00000000000..b5784b5ce03 --- /dev/null +++ b/code/tests/test_timer_sanity.dm @@ -0,0 +1,2 @@ +/datum/game_test/timer_sanity/Run() + TEST_ASSERT(SStimer.bucket_count >= 0, "SStimer is going into negative bucket count") diff --git a/paradise.dme b/paradise.dme index 1d532853d43..73f6eb1de97 100644 --- a/paradise.dme +++ b/paradise.dme @@ -3000,7 +3000,6 @@ #include "code\modules\tgui_input\say_modal\tgui_say_speech.dm" #include "code\modules\tgui_input\say_modal\tgui_say_typing.dm" #include "code\modules\tooltip\tooltip.dm" -#include "code\modules\unit_tests\_unit_tests.dm" #include "code\modules\vehicle\ambulance.dm" #include "code\modules\vehicle\atv.dm" #include "code\modules\vehicle\bicycle.dm" @@ -3028,6 +3027,7 @@ #include "code\modules\world_topic\pr_announce_topic.dm" #include "code\modules\world_topic\queue_status.dm" #include "code\modules\world_topic\status.dm" +#include "code\tests\game_tests.dm" #include "interface\interface.dm" #include "interface\skin.dmf" // END_INCLUDE diff --git a/tools/ci/unticked_files.py b/tools/ci/unticked_files.py index 5ab3f6d41ba..1ee042b30aa 100644 --- a/tools/ci/unticked_files.py +++ b/tools/ci/unticked_files.py @@ -21,7 +21,7 @@ import sys INCLUDER_FILES = [ 'paradise.dme', 'code/modules/tgs/includes.dm', - 'code/modules/unit_tests/_unit_tests.dm', + 'code/tests/game_tests.dm', ] IGNORE_FILES = {