Files
Aurora.3/code/unit_tests/ss_test.dm
Fluffy 8cf49025ae CI improvements (#17632)
* the boulder must be pushed

* awk?

* sadfasdf

* sadf

* sadfsda

* asdf

* sdfasafd

* asdfsad

* asdf

* dsfafa

* despair

* sadfsda

* sdfsadf

* 1589

* sdaf

* sadfasd

* asdf

* safsadf

* fdsafsa

* add create and destroy

* fdsa

* sdafasfsda

* pods for away sites

* oopsie

* hgdfs

* i am speed

* sdafas

* hopefully this works first trAHAHAHAH

* asdf

* dfsadasf

* sfasdf

* 6461

* dsfasfd

* sfda

* fsd

* sdfas

* fsdaf

* sadf

* safasf

* sudo

* sadfsad

* dfsdf
2023-11-13 21:25:33 +01:00

251 lines
7.0 KiB
Plaintext

// This file exists so unit tests work with SMC.
// It initializes last in the subsystem order, and queues
// the tests to start about 20 seconds after init is done.
/**
* Wondering if you should change this to run the tests? NO!
* Because the preproc checks for this in other areas too, set it in code\__defines\manual_unit_testing.dm instead!
*/
#ifdef UNIT_TEST
/*
The Unit Tests Configuration subsystem
*/
var/datum/controller/subsystem/unit_tests_config/SSunit_tests_config = new
/datum/controller/subsystem/unit_tests_config
name = "Unit Test Config"
init_order = SS_INIT_PERSISTENT_CONFIG
flags = SS_NO_FIRE
var/datum/unit_test/UT = new // Logging/output
///What is our identifier, what pod are we, and hence what are we supposed to run
var/identifier = null
///The configuration, decoded from `config/unit_test/ut_pods_configuration.json`, specific for our identifier
var/list/config = list()
///Boolean, if the tests should fast fail (Anything fails = the pod shuts down)
var/fail_fast = FALSE
///How many times can the pod retries before the unit test is considered failed
var/retries = 0
/datum/controller/subsystem/unit_tests_config/New()
. = ..()
world.fps = 10
//Acquire our identifier, or enter Hopper mode if failing to do so
try
src.identifier = rustg_file_read("config/unit_test/identifier.txt")
if(isnull(src.identifier))
UT.fail("**** This UT is being run without an identifier! Aborting... ****")
del world
catch()
UT.fail("**** Exception encountered while trying to acquire an identifier for this UT! ***")
del world
ASSERT(!isnull(src.identifier))
//Try to acquire our configuration
try
src.config = json_decode(rustg_file_read("config/unit_test/ut_pods_configuration.json"))
UT.debug("Pods configuration file read as: [json_encode(src.config)]")
UT.debug("Will extract the pod configuration for pod with identifier: [src.identifier]")
for(var/k in src.config)
UT.debug("The following pod configuration is defined: [k]")
src.config = src.config[src.identifier]
UT.debug("Pods configuration extrapolated as: [json_encode(src.config)]")
if(isnull(src.config))
UT.fail("**** This UT is being run without a config, it's null! Aborting... ****")
del world
if(!src.config.len)
UT.fail("**** This UT is being run without a config! Aborting... ****")
del world
catch(var/exception/e)
UT.fail("**** Exception encountered while trying to acquire the config for this UT! Identifier: [identifier] ***")
UT.fail("**** Exception encountered: [e] ***")
. = ..(e)
del world
refresh_retries(FALSE)
refresh_fail_fast()
/**
* Refresh the `retries` variable from the environment variables
*
* * decrement - A boolean, if `TRUE` it decrements the environment variable that holds the retries left
*/
/datum/controller/subsystem/unit_tests_config/proc/refresh_retries(decrement = FALSE)
src.retries = text2num(world.GetConfig("env", "CI_MAX_RETRIES"))
if(decrement && src.retries)
world.SetConfig("env", "CI_MAX_RETRIES", num2text((src.retries - 1)))
/**
* Refresh the `fail_fast` variable depending on the CI trigger reason
*/
/datum/controller/subsystem/unit_tests_config/proc/refresh_fail_fast()
//Off by default, so only need to flip it on when we wish it to
if(world.GetConfig("env", "CI_TRIGGER_REASON") == "merge_group")
src.fail_fast = TRUE
/*
The Unit Tests subsystem
*/
/datum/controller/subsystem/unit_tests
name = "Unit Tests"
var/datum/unit_test/UT = new // Use this to log things from outside where a specific unit_test is defined
init_order = -1e6 // last.
var/list/queue = list()
var/list/async_tests = list()
var/list/current_async
var/stage = 0
wait = 2 SECONDS
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY | RUNLEVEL_INIT
/datum/controller/subsystem/unit_tests/Initialize(timeofday)
UT.notice("Initializing Unit Testing", __FILE__, __LINE__)
//
//Start the Round.
//
for(var/thing in subtypesof(/datum/unit_test) - typecacheof(current_map.excluded_test_types))
var/datum/unit_test/D = new thing
if(findtext(D.name, "template"))
qdel(D)
continue
if(!length(D.groups))
UT.fail("**** Unit Test has no group assigned! [D.name] ****")
del world
for(var/group in D.groups)
if((group in SSunit_tests_config.config["unit_test_groups"]) || (SSunit_tests_config.config["unit_test_groups"][1] == "*"))
queue += D
break
UT.notice("[queue.len] unit tests loaded.", __FILE__, __LINE__)
..()
/datum/controller/subsystem/unit_tests/proc/start_game()
if (SSticker.current_state == GAME_STATE_PREGAME)
SSticker.current_state = GAME_STATE_SETTING_UP
UT.debug("Round has been started.", __FILE__, __LINE__)
stage++
else
UT.fail("Unable to start testing; SSticker.current_state=[SSticker.current_state]!", __FILE__, __LINE__)
del world
/datum/controller/subsystem/unit_tests/proc/handle_tests()
var/list/curr = queue
while (curr.len)
var/datum/unit_test/test = curr[curr.len]
curr.len--
if (test.map_path && current_map && current_map.path != test.map_path)
test.pass("[ascii_red]Check Disabled: This test is not allowed to run on this map.", __FILE__, __LINE__)
if (MC_TICK_CHECK)
return
continue
if (test.disabled)
test.pass("[ascii_red]Check Disabled: [test.why_disabled]", __FILE__, __LINE__)
if (MC_TICK_CHECK)
return
continue
TEST_GROUP_OPEN("[test.name]")
if (test.start_test() == null) // Runtimed.
test.fail("Test Runtimed: [test.name]", __FILE__, __LINE__)
TEST_GROUP_CLOSE("[test.name]")
if (test.async)
async_tests += test
total_unit_tests++
if(unit_tests_failures && SSunit_tests_config.fail_fast)
UT.fail("**** Fail fast is enabled and an unit test failed! Aborting... ****", __FILE__, __LINE__)
handle_tests_ending(TRUE)
break
if (MC_TICK_CHECK)
return
if (!curr.len)
stage++
/datum/controller/subsystem/unit_tests/proc/handle_async(resumed = 0)
if (!resumed)
current_async = async_tests.Copy()
var/list/async = current_async
while (async.len)
var/datum/unit_test/test = current_async[current_async.len]
current_async.len--
TEST_GROUP_OPEN("[test.name]")
if (test.check_result())
async_tests -= test
TEST_GROUP_CLOSE("[test.name]")
if (MC_TICK_CHECK)
return
if (!async.len)
stage++
/datum/controller/subsystem/unit_tests/fire(resumed = 0)
switch (stage)
if (0)
start_game()
if (1)
// wait a moment
stage++
if (2) // do normal tests
handle_tests()
if (3)
handle_async(resumed)
if (4) // Finalization.
if(all_unit_tests_passed)
UT.pass("**** All Unit Tests Passed \[[total_unit_tests]\] ****", __FILE__, __LINE__)
handle_tests_ending(FALSE)
else
UT.fail("**** \[[unit_tests_failures]\] Errors Encountered! Read the logs above! ****", __FILE__, __LINE__)
handle_tests_ending(TRUE)
/datum/controller/subsystem/unit_tests/proc/handle_tests_ending(is_failure = FALSE)
if(is_failure && SSunit_tests_config.retries)
SSunit_tests_config.refresh_retries(TRUE)
world.Reboot("Restarting for another UT try, remaining tries: [SSunit_tests_config.retries]", TRUE)
else
del world
#endif