Allows for offloaded tests to be focused (#71719)

Focusing a test through TEST_FOCUS allows you to only run that one test.
It's very useful for developing. However, we have a bunch of offloaded
tests in `#ifdef UNIT_TESTS` that would not be focused. This changes it
so that those tests will now only run if either no test is focused, or
if their specific "focus only" test is focused.

This is done through a `PERFORM_ALL_TESTS` macro that replaces the
`#ifdef UNIT_TESTS`. This is completely free because `if (FALSE)`
constant folds.

Hide whitespace mode recommended.
This commit is contained in:
Mothblocks
2022-12-07 02:58:33 -08:00
committed by GitHub
parent ae758adc33
commit 8ace31bde4
11 changed files with 88 additions and 48 deletions
-7
View File
@@ -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
+20
View File
@@ -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
+7 -7
View File
@@ -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))
+4 -6
View File
@@ -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
+4 -6
View File
@@ -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
@@ -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
+11 -12
View File
@@ -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)
+1
View File
@@ -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"
@@ -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
+13
View File
@@ -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
+1
View File
@@ -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"