Skip to content

Commit

Permalink
unit->game tests, /tg/ assertions and allocations (#27646)
Browse files Browse the repository at this point in the history
* unit->game tests, /tg/ assertions and allocations

* whoopsies

* fix lint
  • Loading branch information
warriorstar-orion authored Dec 21, 2024
1 parent 79bad42 commit bb60a02
Show file tree
Hide file tree
Showing 53 changed files with 304 additions and 243 deletions.
12 changes: 6 additions & 6 deletions code/_compile_options.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 *****/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion code/controllers/master.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion code/controllers/subsystem/SSdbcore.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/controllers/subsystem/SSredis.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/controllers/subsystem/SSticker.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion code/controllers/subsystem/SSverb_manager.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
Expand Down
2 changes: 1 addition & 1 deletion code/controllers/subsystem/non_firing/SSmapping.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
8 changes: 4 additions & 4 deletions code/game/world.dm
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion code/modules/mob/living/carbon/carbon_procs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
visible_message("<span class='notice'>[src] begins climbing into the ventilation system...</span>", \
"<span class='notice'>You begin climbing into the ventilation system...</span>")

#ifdef UNIT_TESTS
#ifdef GAME_TESTS
var/ventcrawl_delay = 0 SECONDS
#else
var/ventcrawl_delay = 4.5 SECONDS
Expand Down
2 changes: 1 addition & 1 deletion code/modules/mob/living/simple_animal/friendly/mouse.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
34 changes: 0 additions & 34 deletions code/modules/unit_tests/_unit_tests.dm

This file was deleted.

7 changes: 0 additions & 7 deletions code/modules/unit_tests/atmos/test_ventcrawl.dmm

This file was deleted.

5 changes: 0 additions & 5 deletions code/modules/unit_tests/crafting_lists.dm

This file was deleted.

4 changes: 0 additions & 4 deletions code/modules/unit_tests/rustg_version.dm

This file was deleted.

10 changes: 0 additions & 10 deletions code/modules/unit_tests/sql.dm

This file was deleted.

22 changes: 0 additions & 22 deletions code/modules/unit_tests/subsystem_metric_sanity.dm

This file was deleted.

3 changes: 0 additions & 3 deletions code/modules/unit_tests/timer_sanity.dm

This file was deleted.

40 changes: 0 additions & 40 deletions code/modules/unit_tests/unit_test.dm

This file was deleted.

96 changes: 96 additions & 0 deletions code/tests/_game_test.dm
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit bb60a02

Please sign in to comment.