Files
Bubberstation/code/modules/unit_tests/heretic_knowledge.dm
Tastyfish bca463316b Makes integration test results be in color and have annotations (#66649)
About The Pull Request

    Separated compiling the integration tests and running them as separate steps for organization purposes.
    Added a TEST_ASSERT_NULL(value, reason) and TEST_ASSERT_NOTNULL(value, reason) because those are conceptually simple tests.
    Makes the PASS and FAIL prefixes in the integration test log be green and red for better readability.
    Failure reasons now display the file and line number.
        In order to achieve this, direct calls to Fail() are now wrapped in a macro, TEST_FAIL(), as Fail() itself needs preprocessor stuff passed to it.
        In the midst of updating it, I noticed multiple cases of tests directly calling Fail() and returning when they should have used a better macro, so those were updated. There was at least one case where it appeared that the code assumed that the test ended at Fail(), but made no attempt to do so, such as with the RCD test.
        Feel free to double check all of the changed unit tests in case I made a functional behavior change, but they currently pass.
    To take advantage of the previous change, failures are now marked as annotations. Note that outside of github, this creates an ugly-looking line but the primary environment is as a github action.

Examples with intentionally botched unit test:

image

image

image
Why It's Good For The Game

Makes inspecting failed unit tests significantly easier.
Changelog

N/A
2022-05-04 13:19:01 +12:00

93 lines
4.5 KiB
Plaintext

/*
* This test checks all heretic knowledge nodes and validates they are setup correctly.
* We check that all knowledge is reachable by players (through the research tree)
* and that all knowledge have a valid next_knowledge list.
*/
/datum/unit_test/heretic_knowledge
/datum/unit_test/heretic_knowledge/Run()
// First, we get a list of all knowledge types
// EXCLUDING types which have route unset / set to null.
// (Types without a route set are assumed to be abstract or purposefully unreachable)
var/list/all_possible_knowledge = typesof(/datum/heretic_knowledge)
for(var/datum/heretic_knowledge/knowledge_type as anything in all_possible_knowledge)
if(isnull(initial(knowledge_type.route)))
all_possible_knowledge -= knowledge_type
// Now, let's build a list of all researchable knowledge
// from the ground up. We start with all starting knowledge,
// then add the next possible knowledges back into the list
// repeatedly, until we run out of knowledges to add.
var/list/list_to_check = GLOB.heretic_start_knowledge.Copy()
var/i = 0
while(i < length(list_to_check))
var/datum/heretic_knowledge/path_to_create = list_to_check[++i]
if(!ispath(path_to_create))
TEST_FAIL("Heretic Knowlege: Got a non-heretic knowledge datum (Got: [path_to_create]) in the list knowledges!")
var/datum/heretic_knowledge/instantiated_knowledge = new path_to_create()
// Next knowledge is a list of typepaths.
for(var/datum/heretic_knowledge/next_knowledge as anything in instantiated_knowledge.next_knowledge)
if(!ispath(next_knowledge))
TEST_FAIL("Heretic Knowlege: [next_knowledge.type] has a [isnull(next_knowledge) ? "null":"invalid path"] in its next_knowledge list!")
continue
if(next_knowledge in list_to_check)
continue
list_to_check += next_knowledge
qdel(instantiated_knowledge)
// We now have a list that SHOULD contain all knowledges with a path set (list_to_check).
// Let's compare it to our original list (all_possible_knowledge). If they're not identical,
// then somewhere we missed a knowledge somewhere, and should throw a fail.
if(length(all_possible_knowledge) != length(all_possible_knowledge & list_to_check))
// Unreachables is a list of typepaths - all paths that cannot be obtained.
var/list/unreachables = all_possible_knowledge - list_to_check
for(var/datum/heretic_knowledge/lost_knowledge as anything in unreachables)
TEST_FAIL("Heretic Knowlege: [lost_knowledge] is unreachable by players! Add it to another knowledge's 'next_knowledge' list. If it is purposeful, set its route to 'null'.")
/*
* This test checks that all main heretic paths are of the same length.
*
* If any two main paths are not equal length, the test will fail and quit, reporting
* which two paths did not match. Then, whichever is erroneous can be determined manually.
*/
/datum/unit_test/heretic_main_paths
/datum/unit_test/heretic_main_paths/Run()
// A list of path strings we don't need to check.
var/list/paths_we_dont_check = list(PATH_SIDE, PATH_START)
// An assoc list of [path string] to [number of nodes we found of that path].
var/list/paths = list()
// The starting knowledge node, we use this to deduce what main paths we have.
var/datum/heretic_knowledge/spell/basic/starter_node = new()
// Go through and determine what paths exist from our base node.
for(var/datum/heretic_knowledge/possible_path as anything in starter_node.next_knowledge)
paths[initial(possible_path.route)] = 0
qdel(starter_node) // Get rid of that starter node, we don't need it anymore.
// Now go through all the knowledges and record how many of each main path exist.
for(var/datum/heretic_knowledge/knowledge as anything in subtypesof(/datum/heretic_knowledge))
var/knowledge_route = initial(knowledge.route)
// null (abstract), side paths, and start paths we can skip
if(isnull(knowledge_route) || (knowledge_route in paths_we_dont_check))
continue
if(isnull(paths[knowledge_route]))
TEST_FAIL("Heretic Knowledge: An invalid knowledge route ([knowledge_route]) was found on [knowledge].")
continue
paths[knowledge_route]++
// Now all entries in the paths list should have an equal value.
// If any two entries do not match, then one of them is incorrect, and the test fails.
for(var/main_path in paths)
for(var/other_main_path in (paths - main_path))
TEST_ASSERT(paths[main_path] == paths[other_main_path], \
"Heretic Knowledge: [main_path] had [paths[main_path]] knowledges, \
which was not equal to [other_main_path]'s [paths[other_main_path]] knowledges. \
All main paths should have the same number of knowledges!")