diff --git a/code/__DEFINES/_helpers.dm b/code/__DEFINES/_helpers.dm index 89ebac7ba1b..bf723659588 100644 --- a/code/__DEFINES/_helpers.dm +++ b/code/__DEFINES/_helpers.dm @@ -24,10 +24,3 @@ /// : because of the embedded typecheck #define text_ref(datum) (isdatum(datum) ? (datum:cached_ref ||= "\ref[datum]") : ("\ref[datum]")) -/// ASSERT(), but it only actually does anything during unit tests -#ifdef UNIT_TESTS -#define TEST_ONLY_ASSERT(test, explanation) if(!(test)) {CRASH(explanation)} -#else -#define TEST_ONLY_ASSERT(test, explanation) -#endif - diff --git a/code/__DEFINES/unit_tests.dm b/code/__DEFINES/unit_tests.dm new file mode 100644 index 00000000000..92aee0ee8f4 --- /dev/null +++ b/code/__DEFINES/unit_tests.dm @@ -0,0 +1,20 @@ +/// Are tests enabled with no focus? +/// Use this when performing test assertions outside of a unit test, +/// since a focused test means that you're trying to run a test quickly. +/// If a parameter is provided, will check if the focus is on that test name. +/// For example, PERFORM_ALL_TESTS(log_mapping) will only run if either +/// no test is focused, or the focus is log_mapping. +#ifdef UNIT_TESTS +// Bit of a trick here, if focus isn't passed in then it'll check for /datum/unit_test/, which is never the case. +#define PERFORM_ALL_TESTS(focus...) (isnull(GLOB.focused_test) || GLOB.focused_test == /datum/unit_test/##focus) +#else +// UNLINT necessary here so that if (PERFORM_ALL_TESTS()) works +#define PERFORM_ALL_TESTS(...) UNLINT(FALSE) +#endif + +/// ASSERT(), but it only actually does anything during unit tests +#ifdef UNIT_TESTS +#define TEST_ONLY_ASSERT(test, explanation) if(!(test)) {CRASH(explanation)} +#else +#define TEST_ONLY_ASSERT(test, explanation) +#endif diff --git a/code/controllers/subsystem/overlays.dm b/code/controllers/subsystem/overlays.dm index 5021ffa3ece..1f60a9e71e0 100644 --- a/code/controllers/subsystem/overlays.dm +++ b/code/controllers/subsystem/overlays.dm @@ -45,14 +45,14 @@ SUBSYSTEM_DEF(overlays) build_overlays -= overlay continue if (istext(overlay)) -#ifdef UNIT_TESTS // This is too expensive to run normally but running it during CI is a good test - var/list/icon_states_available = icon_states(icon) - if(!(overlay in icon_states_available)) - var/icon_file = "[icon]" || "Unknown Generated Icon" - stack_trace("Invalid overlay: Icon object '[icon_file]' [REF(icon)] used in '[src]' [type] is missing icon state [overlay].") - continue -#endif + if (PERFORM_ALL_TESTS(focus_only/invalid_overlays)) + var/list/icon_states_available = icon_states(icon) + if(!(overlay in icon_states_available)) + var/icon_file = "[icon]" || "Unknown Generated Icon" + stack_trace("Invalid overlay: Icon object '[icon_file]' [REF(icon)] used in '[src]' [type] is missing icon state [overlay].") + continue + var/index = build_overlays.Find(overlay) build_overlays[index] = iconstate2appearance(icon, overlay) else if(isicon(overlay)) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 9f18ac73d86..f2bfa4cbc44 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -259,16 +259,14 @@ update_light() if (length(smoothing_groups)) - #ifdef UNIT_TESTS - assert_sorted(smoothing_groups, "[type].smoothing_groups") - #endif + if (PERFORM_ALL_TESTS(focus_only/sorted_smoothing_groups)) + assert_sorted(smoothing_groups, "[type].smoothing_groups") SET_BITFLAG_LIST(smoothing_groups) if (length(canSmoothWith)) - #ifdef UNIT_TESTS - assert_sorted(canSmoothWith, "[type].canSmoothWith") - #endif + if (PERFORM_ALL_TESTS(focus_only/sorted_smoothing_groups)) + assert_sorted(canSmoothWith, "[type].canSmoothWith") if(canSmoothWith[length(canSmoothWith)] > MAX_S_TURF) //If the last element is higher than the maximum turf-only value, then it must scan turf contents for smoothing targets. smoothing_flags |= SMOOTH_OBJ diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index bdc3473cc74..1967796ea47 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -134,15 +134,13 @@ GLOBAL_LIST_EMPTY(station_turfs) levelupdate() if (length(smoothing_groups)) - #ifdef UNIT_TESTS - assert_sorted(smoothing_groups, "[type].smoothing_groups") - #endif + if (PERFORM_ALL_TESTS(focus_only/sorted_smoothing_groups)) + assert_sorted(smoothing_groups, "[type].smoothing_groups") SET_BITFLAG_LIST(smoothing_groups) if (length(canSmoothWith)) - #ifdef UNIT_TESTS - assert_sorted(canSmoothWith, "[type].canSmoothWith") - #endif + if (PERFORM_ALL_TESTS(focus_only/sorted_smoothing_groups)) + assert_sorted(canSmoothWith, "[type].canSmoothWith") if(canSmoothWith[length(canSmoothWith)] > MAX_S_TURF) //If the last element is higher than the maximum turf-only value, then it must scan turf contents for smoothing targets. smoothing_flags |= SMOOTH_OBJ diff --git a/code/modules/asset_cache/assets/research_designs.dm b/code/modules/asset_cache/assets/research_designs.dm index c457ca43c6e..62a03040517 100644 --- a/code/modules/asset_cache/assets/research_designs.dm +++ b/code/modules/asset_cache/assets/research_designs.dm @@ -13,11 +13,10 @@ if(initial(D.research_icon) && initial(D.research_icon_state)) //If the design has an icon replacement skip the rest icon_file = initial(D.research_icon) icon_state = initial(D.research_icon_state) - #ifdef UNIT_TESTS - if(!(icon_state in icon_states(icon_file))) - stack_trace("design [D] with icon '[icon_file]' missing state '[icon_state]'") - continue - #endif + if (PERFORM_ALL_TESTS(focus_only/invalid_research_designs)) + if(!(icon_state in icon_states(icon_file))) + stack_trace("design [D] with icon '[icon_file]' missing state '[icon_state]'") + continue I = icon(icon_file, icon_state, SOUTH) else @@ -46,11 +45,10 @@ icon_file = initial(item.icon) icon_state = initial(item.icon_state) - #ifdef UNIT_TESTS - if(!(icon_state in icon_states(icon_file))) - stack_trace("design [D] with icon '[icon_file]' missing state '[icon_state]'") - continue - #endif + if (PERFORM_ALL_TESTS(focus_only/invalid_research_designs)) + if(!(icon_state in icon_states(icon_file))) + stack_trace("design [D] with icon '[icon_file]' missing state '[icon_state]'") + continue I = icon(icon_file, icon_state, SOUTH) // computers (and snowflakes) get their screen and keyboard sprites diff --git a/code/modules/asset_cache/assets/vending.dm b/code/modules/asset_cache/assets/vending.dm index 574836f0c12..e8b92e59392 100644 --- a/code/modules/asset_cache/assets/vending.dm +++ b/code/modules/asset_cache/assets/vending.dm @@ -14,19 +14,18 @@ icon_file = initial(item.icon) var/icon_state = initial(item.icon_state) - #ifdef UNIT_TESTS - var/icon_states_list = icon_states(icon_file) - if (!(icon_state in icon_states_list)) - var/icon_states_string - for (var/an_icon_state in icon_states_list) - if (!icon_states_string) - icon_states_string = "[json_encode(an_icon_state)]([text_ref(an_icon_state)])" - else - icon_states_string += ", [json_encode(an_icon_state)]([text_ref(an_icon_state)])" + if (PERFORM_ALL_TESTS(focus_only/invalid_vending_machine_icon_states)) + var/icon_states_list = icon_states(icon_file) + if (!(icon_state in icon_states_list)) + var/icon_states_string + for (var/an_icon_state in icon_states_list) + if (!icon_states_string) + icon_states_string = "[json_encode(an_icon_state)]([text_ref(an_icon_state)])" + else + icon_states_string += ", [json_encode(an_icon_state)]([text_ref(an_icon_state)])" - stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)]([text_ref(icon_state)]), icon_states=[icon_states_string]") - continue - #endif + stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)]([text_ref(icon_state)]), icon_states=[icon_states_string]") + continue var/icon/I = icon(icon_file, icon_state, SOUTH) var/c = initial(item.color) diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index d2f9d7029b6..f926bf6af7b 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -105,6 +105,7 @@ #include "dynamic_ruleset_sanity.dm" #include "egg_glands.dm" #include "emoting.dm" +#include "focus_only_tests.dm" #include "food_edibility_check.dm" #include "gas_transfer.dm" #include "get_turf_pixel.dm" diff --git a/code/modules/unit_tests/focus_only_tests.dm b/code/modules/unit_tests/focus_only_tests.dm new file mode 100644 index 00000000000..43f7d9ef5a3 --- /dev/null +++ b/code/modules/unit_tests/focus_only_tests.dm @@ -0,0 +1,19 @@ +/// These tests perform no behavior of their own, and have their tests offloaded onto other procs. +/// This is useful in cases like in build_appearance_list where we want to know if any fail, +/// but is not useful to right a test for. +/// This file exists so that you can change any of these to TEST_FOCUS and only check for that test. +/// For example, change /datum/unit_test/focus_only/invalid_overlays to TEST_FOCUS(/datum/unit_test/focus_only/invalid_overlays), +/// and you will only test the check for invalid overlays in appearance building. +/datum/unit_test/focus_only + +/// Checks that every overlay passed into build_appearance_list exists in the icon +/datum/unit_test/focus_only/invalid_overlays + +/// Checks that every icon sent to the research_designs spritesheet is valid +/datum/unit_test/focus_only/invalid_research_designs + +/// Checks that every icon sent to vending machines is valid +/datum/unit_test/focus_only/invalid_vending_machine_icon_states + +/// Checks that smoothing_groups and canSmoothWith are properly sorted in /atom/Initialize +/datum/unit_test/focus_only/sorted_smoothing_groups diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm index fddcaa74f08..06347954e7e 100644 --- a/code/modules/unit_tests/unit_test.dm +++ b/code/modules/unit_tests/unit_test.dm @@ -17,6 +17,16 @@ GLOBAL_VAR(test_log) /// When unit testing, all logs sent to log_mapping are stored here and retrieved in log_mapping unit test. GLOBAL_LIST_EMPTY(unit_test_mapping_logs) +/// The name of the test that is currently focused. +/// Use the PERFORM_ALL_TESTS macro instead. +GLOBAL_VAR_INIT(focused_test, focused_test()) + +/proc/focused_test() + for (var/datum/unit_test/unit_test as anything in subtypesof(/datum/unit_test)) + if (initial(unit_test.focus)) + return unit_test + return null + /datum/unit_test //Bit of metadata for the future maybe var/list/procs_tested @@ -125,6 +135,9 @@ GLOBAL_LIST_EMPTY(unit_test_mapping_logs) log_world("::[priority] file=[file],line=[line],title=[map_name]: [type]::[annotation_text]") /proc/RunUnitTest(test_path, list/test_results) + if (ispath(test_path, /datum/unit_test/focus_only)) + return + var/datum/unit_test/test = new test_path GLOB.current_test = test diff --git a/tgstation.dme b/tgstation.dme index 374eb2b0768..032826f0540 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -205,6 +205,7 @@ #include "code\__DEFINES\turbine_defines.dm" #include "code\__DEFINES\turfs.dm" #include "code\__DEFINES\typeids.dm" +#include "code\__DEFINES\unit_tests.dm" #include "code\__DEFINES\uplink.dm" #include "code\__DEFINES\vehicles.dm" #include "code\__DEFINES\verb_manager.dm"