mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2026-07-21 20:53:36 +01:00
unit test
this is the easy part, believe it or not
This commit is contained in:
@@ -3,9 +3,18 @@
|
||||
|
||||
#if defined(UNIT_TESTS) || defined(SPACEMAN_DMM)
|
||||
|
||||
/// 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"]") }
|
||||
#define TEST_ASSERT(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
|
||||
@@ -13,7 +22,7 @@
|
||||
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]" : ""]"); \
|
||||
return Fail("Expected [isnull(lhs) ? "null" : lhs] to be equal to [isnull(rhs) ? "null" : rhs].[message ? " [message]" : ""]", __FILE__, __LINE__); \
|
||||
} \
|
||||
} while (FALSE)
|
||||
|
||||
@@ -23,7 +32,7 @@
|
||||
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]" : ""]"); \
|
||||
return Fail("Expected [isnull(lhs) ? "null" : lhs] to not be equal to [isnull(rhs) ? "null" : rhs].[message ? " [message]" : ""]", __FILE__, __LINE__); \
|
||||
} \
|
||||
} while (FALSE)
|
||||
|
||||
@@ -37,8 +46,25 @@
|
||||
#define UNIT_TEST_FAILED 1
|
||||
#define UNIT_TEST_SKIPPED 2
|
||||
|
||||
#define TEST_PRE 0
|
||||
#define TEST_DEFAULT 1
|
||||
#define TEST_DEL_WORLD INFINITY
|
||||
/// After most test steps, used for tests that run long so shorter issues can be noticed faster
|
||||
#define TEST_LONGER 10
|
||||
/// This must be the last test to run due to the inherent nature of the test iterating every single tangible atom in the game and qdeleting all of them (while taking long sleeps to make sure the garbage collector fires properly) taking a large amount of time.
|
||||
#define TEST_CREATE_AND_DESTROY INFINITY
|
||||
|
||||
/// Change color to red on ANSI terminal output, if enabled with -DANSICOLORS.
|
||||
#ifdef ANSICOLORS
|
||||
#define TEST_OUTPUT_RED(text) "\x1B\x5B1;31m[text]\x1B\x5B0m"
|
||||
#else
|
||||
#define TEST_OUTPUT_RED(text) (text)
|
||||
#endif
|
||||
/// Change color to green on ANSI terminal output, if enabled with -DANSICOLORS.
|
||||
#ifdef ANSICOLORS
|
||||
#define TEST_OUTPUT_GREEN(text) "\x1B\x5B1;32m[text]\x1B\x5B0m"
|
||||
#else
|
||||
#define TEST_OUTPUT_GREEN(text) (text)
|
||||
#endif
|
||||
|
||||
/// A trait source when adding traits through unit tests
|
||||
#define TRAIT_SOURCE_UNIT_TESTS "unit_tests"
|
||||
@@ -55,7 +81,7 @@
|
||||
// #include "connect_loc.dm"
|
||||
// #include "confusion.dm"
|
||||
// #include "crayons.dm"
|
||||
// #include "create_and_destroy.dm"
|
||||
#include "create_and_destroy.dm"
|
||||
// #include "designs.dm"
|
||||
#include "dynamic_ruleset_sanity.dm"
|
||||
// #include "egg_glands.dm"
|
||||
@@ -105,12 +131,12 @@
|
||||
/// SANDSTORM TESTS
|
||||
#include "interactions.dm" //No regrets
|
||||
|
||||
#ifdef REFERENCE_TRACKING //Don't try and parse this file if ref tracking isn't turned on. IE: don't parse ref tracking please mr linter
|
||||
#ifdef REFERENCE_TRACKING_DEBUG //Don't try and parse this file if ref tracking isn't turned on. IE: don't parse ref tracking please mr linter
|
||||
#include "find_reference_sanity.dm"
|
||||
#endif
|
||||
|
||||
#undef TEST_ASSERT
|
||||
#undef TEST_ASSERT_EQUAL
|
||||
#undef TEST_ASSERT_NOTEQUAL
|
||||
#undef TEST_FOCUS
|
||||
//#undef TEST_FOCUS - This define is used by vscode unit test extension to pick specific unit tests to run and appended later so needs to be used out of scope here
|
||||
#endif
|
||||
|
||||
@@ -4,6 +4,4 @@
|
||||
var/mob/M = i
|
||||
if(initial(M.anchored))
|
||||
L += "[i]"
|
||||
if(!L.len)
|
||||
return //passed!
|
||||
Fail("The following mobs are defined as anchored. This is incompatible with the new move force/resist system and needs to be revised.: [L.Join(" ")]")
|
||||
TEST_ASSERT(!L.len, "The following mobs are defined as anchored. This is incompatible with the new move force/resist system and needs to be revised.: [L.Join(" ")]")
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
for(var/i in subtypesof(/datum/element))
|
||||
var/datum/element/faketype = i
|
||||
if((initial(faketype.element_flags) & ELEMENT_BESPOKE) && initial(faketype.id_arg_index) == base_index)
|
||||
Fail("A bespoke element was not configured with a proper id_arg_index: [faketype]")
|
||||
TEST_FAIL("A bespoke element was not configured with a proper id_arg_index: [faketype]")
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/datum/unit_test/card_mismatch
|
||||
|
||||
/datum/unit_test/card_mismatch/Run()
|
||||
var/message = checkCardpacks(SStrading_card_game.card_packs)
|
||||
message += checkCardDatums()
|
||||
if(message)
|
||||
Fail(message)
|
||||
var/message = SStrading_card_game.check_cardpacks(SStrading_card_game.card_packs)
|
||||
message += SStrading_card_game.check_card_datums()
|
||||
TEST_ASSERT(!message, message)
|
||||
|
||||
@@ -41,22 +41,15 @@
|
||||
|
||||
// Walk normally to the left, make sure we're still a chain
|
||||
alice.Move(locate(run_loc_floor_bottom_left.x + 1, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z))
|
||||
if (bob.x != run_loc_floor_bottom_left.x + 2)
|
||||
return Fail("During normal move, Bob was not at the correct x ([bob.x])")
|
||||
if (charlie.x != run_loc_floor_bottom_left.x + 3)
|
||||
return Fail("During normal move, Charlie was not at the correct x ([charlie.x])")
|
||||
TEST_ASSERT_EQUAL(bob.x, run_loc_floor_bottom_left.x + 2, "During normal move, Bob was not at the correct x ([bob.x])")
|
||||
TEST_ASSERT_EQUAL(charlie.x, run_loc_floor_bottom_left.x + 3, "During normal move, Charlie was not at the correct x ([charlie.x])")
|
||||
|
||||
// We're going through the space turf now that should teleport us
|
||||
alice.Move(run_loc_floor_bottom_left)
|
||||
if (alice.z != space_tile.destination_z)
|
||||
return Fail("Alice did not teleport to the destination z-level. Current location: ([alice.x], [alice.y], [alice.z])")
|
||||
TEST_ASSERT_EQUAL(alice.z, space_tile.destination_z, "Alice did not teleport to the destination z-level. Current location: ([alice.x], [alice.y], [alice.z])")
|
||||
|
||||
if (bob.z != space_tile.destination_z)
|
||||
return Fail("Bob did not teleport to the destination z-level. Current location: ([bob.x], [bob.y], [bob.z])")
|
||||
if (!bob.Adjacent(alice))
|
||||
return Fail("Bob is not adjacent to Alice. Bob is at [bob.x], Alice is at [alice.x]")
|
||||
TEST_ASSERT_EQUAL(bob.z, space_tile.destination_z, "Bob did not teleport to the destination z-level. Current location: ([bob.x], [bob.y], [bob.z])")
|
||||
TEST_ASSERT(bob.Adjacent(alice), "Bob is not adjacent to Alice. Bob is at [bob.x], Alice is at [alice.x]")
|
||||
|
||||
if (charlie.z != space_tile.destination_z)
|
||||
return Fail("Charlie did not teleport to the destination z-level. Current location: ([charlie.x], [charlie.y], [charlie.z])")
|
||||
if (!charlie.Adjacent(bob))
|
||||
return Fail("Charlie is not adjacent to Bob. Charlie is at [charlie.x], Bob is at [bob.x]")
|
||||
TEST_ASSERT_EQUAL(charlie.z, space_tile.destination_z, "Charlie did not teleport to the destination z-level. Current location: ([charlie.x], [charlie.y], [charlie.z])")
|
||||
TEST_ASSERT(charlie.Adjacent(bob), "Charlie is not adjacent to Bob. Charlie is at [charlie.x], Bob is at [bob.x]")
|
||||
|
||||
@@ -12,17 +12,17 @@
|
||||
P.save_character()
|
||||
P.load_character()
|
||||
if(P.features["flavor_text"] != UNIT_TEST_SAVING_FLAVOR_TEXT)
|
||||
Fail("Flavor text is failing to save.")
|
||||
TEST_FAIL("Flavor text is failing to save.")
|
||||
if(P.features["silicon_flavor_text"] != UNIT_TEST_SAVING_SILICON_FLAVOR_TEXT)
|
||||
Fail("Silicon flavor text is failing to save.")
|
||||
TEST_FAIL("Silicon flavor text is failing to save.")
|
||||
if(P.features["ooc_notes"] != UNIT_TEST_SAVING_OOC_NOTES)
|
||||
Fail("OOC text is failing to save.")
|
||||
TEST_FAIL("OOC text is failing to save.")
|
||||
P.save_character()
|
||||
P.load_character()
|
||||
if((P.features["flavor_text"] != UNIT_TEST_SAVING_FLAVOR_TEXT) || (P.features["silicon_flavor_text"] != UNIT_TEST_SAVING_SILICON_FLAVOR_TEXT) || (P.features["ooc_notes"] != UNIT_TEST_SAVING_OOC_NOTES))
|
||||
Fail("Repeated saving and loading possibly causing save deletion.")
|
||||
TEST_FAIL("Repeated saving and loading possibly causing save deletion.")
|
||||
catch(var/exception/e)
|
||||
Fail("Failed to save and load character due to exception [e.file]:[e.line], [e.name]")
|
||||
TEST_FAIL("Failed to save and load character due to exception [e.file]:[e.line], [e.name]")
|
||||
|
||||
#undef UNIT_TEST_SAVING_FLAVOR_TEXT
|
||||
#undef UNIT_TEST_SAVING_SILICON_FLAVOR_TEXT
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
var/dupe_type = initial(comp.dupe_type)
|
||||
if(dupe_type && !ispath(dupe_type))
|
||||
bad_dts += t
|
||||
if(length(bad_dms) || length(bad_dts))
|
||||
Fail("Components with invalid dupe modes: ([bad_dms.Join(",")]) ||| Components with invalid dupe types: ([bad_dts.Join(",")])")
|
||||
TEST_ASSERT(!length(bad_dms) && !length(bad_dts),
|
||||
"Components with invalid dupe modes: ([bad_dms.Join(",")]) ||| Components with invalid dupe types: ([bad_dts.Join(",")])")
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
for(var/i in GLOB.crafting_recipes)
|
||||
var/datum/crafting_recipe/R = i
|
||||
if(!R.subcategory)
|
||||
Fail("Invalid subcategory on [R] ([R.type]).")
|
||||
TEST_FAIL("Invalid subcategory on [R] ([R.type]).")
|
||||
if(!R.category && (R.category != CAT_NONE))
|
||||
Fail("Invalid category on [R] ([R.type])")
|
||||
TEST_FAIL("Invalid category on [R] ([R.type])")
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
///Delete one of every type, sleep a while, then check to see if anything has gone fucky
|
||||
/datum/unit_test/create_and_destroy
|
||||
//You absolutely must run last
|
||||
priority = TEST_CREATE_AND_DESTROY
|
||||
|
||||
GLOBAL_VAR_INIT(running_create_and_destroy, FALSE)
|
||||
/datum/unit_test/create_and_destroy/Run()
|
||||
//We'll spawn everything here
|
||||
var/turf/spawn_at = run_loc_floor_bottom_left
|
||||
var/list/ignore = list(
|
||||
//Never meant to be created, errors out the ass for mobcode reasons
|
||||
/mob/living/carbon,
|
||||
//Nother template type, doesn't like being created with no seed
|
||||
// /obj/item/food/grown,
|
||||
//And another
|
||||
/obj/item/slimecross/recurring,
|
||||
//This should be obvious
|
||||
/obj/machinery/doomsday_device,
|
||||
//Yet more templates
|
||||
// /obj/machinery/restaurant_portal,
|
||||
//Template type
|
||||
/obj/effect/mob_spawn,
|
||||
//Template type
|
||||
// /obj/structure/holosign/robot_seat,
|
||||
//Singleton
|
||||
/mob/dview,
|
||||
//Template type
|
||||
/obj/item/bodypart,
|
||||
//This is meant to fail extremely loud every single time it occurs in any environment in any context, and it falsely alarms when this unit test iterates it. Let's not spawn it in.
|
||||
// /obj/merge_conflict_marker,
|
||||
//briefcase launchpads erroring
|
||||
/obj/machinery/launchpad/briefcase,
|
||||
)
|
||||
//Say it with me now, type template
|
||||
ignore += typesof(/obj/effect/mapping_helpers)
|
||||
//This turf existing is an error in and of itself
|
||||
ignore += typesof(/turf/baseturf_skipover)
|
||||
ignore += typesof(/turf/baseturf_bottom)
|
||||
//This demands a borg, so we'll let if off easy
|
||||
// ignore += typesof(/obj/item/modular_computer/pda/silicon)
|
||||
//This one demands a computer, ditto
|
||||
ignore += typesof(/obj/item/modular_computer/processor)
|
||||
//Very finiky, blacklisting to make things easier
|
||||
ignore += typesof(/obj/item/poster/wanted)
|
||||
//This expects a seed, we can't pass it
|
||||
// ignore += typesof(/obj/item/food/grown)
|
||||
//Needs clients / mobs to observe it to exist. Also includes hallucinations.
|
||||
// ignore += typesof(/obj/effect/client_image_holder)
|
||||
//Same to above. Needs a client / mob / hallucination to observe it to exist.
|
||||
// ignore += typesof(/obj/projectile/hallucination)
|
||||
// ignore += typesof(/obj/item/hallucinated)
|
||||
//Can't pass in a thing to glow
|
||||
ignore += typesof(/obj/effect/abstract/eye_lighting)
|
||||
//We don't have a pod
|
||||
ignore += typesof(/obj/effect/pod_landingzone_effect)
|
||||
ignore += typesof(/obj/effect/pod_landingzone)
|
||||
//We have a baseturf limit of 10, adding more than 10 baseturf helpers will kill CI, so here's a future edge case to fix.
|
||||
ignore += typesof(/obj/effect/baseturf_helper)
|
||||
//No tauma to pass in
|
||||
ignore += typesof(/mob/camera/imaginary_friend)
|
||||
//No pod to gondola
|
||||
ignore += typesof(/mob/living/simple_animal/pet/gondola/gondolapod)
|
||||
//No heart to give
|
||||
// ignore += typesof(/obj/structure/ethereal_crystal)
|
||||
//No linked console
|
||||
// ignore += typesof(/mob/camera/ai_eye/remote/base_construction)
|
||||
//See above
|
||||
// ignore += typesof(/mob/camera/ai_eye/remote/shuttle_docker)
|
||||
//Hangs a ref post invoke async, which we don't support. Could put a qdeleted check but it feels hacky
|
||||
ignore += typesof(/obj/effect/anomaly/grav/high)
|
||||
//See above
|
||||
ignore += typesof(/obj/effect/timestop)
|
||||
//Invoke async in init, skippppp
|
||||
// ignore += typesof(/mob/living/silicon/robot/model)
|
||||
//This lad also sleeps
|
||||
ignore += typesof(/obj/item/hilbertshotel)
|
||||
//this boi spawns turf changing stuff, and it stacks and causes pain. Let's just not
|
||||
ignore += typesof(/obj/effect/sliding_puzzle)
|
||||
//Stacks baseturfs, can't be tested here
|
||||
ignore += typesof(/obj/effect/temp_visual/lava_warning)
|
||||
//Stacks baseturfs, can't be tested here
|
||||
// ignore += typesof(/obj/effect/landmark/ctf)
|
||||
//Our system doesn't support it without warning spam from unregister calls on things that never registered
|
||||
ignore += typesof(/obj/docking_port)
|
||||
//Asks for a shuttle that may not exist, let's leave it alone
|
||||
ignore += typesof(/obj/item/pinpointer/shuttle)
|
||||
//This spawns beams as a part of init, which can sleep past an async proc. This hangs a ref, and fucks us. It's only a problem here because the beam sleeps with CHECK_TICK
|
||||
// ignore += typesof(/obj/structure/alien/resin/flower_bud)
|
||||
//Needs a linked mecha
|
||||
ignore += typesof(/obj/effect/skyfall_landingzone)
|
||||
//Expects a mob to holderize, we have nothing to give
|
||||
ignore += typesof(/obj/item/clothing/head/mob_holder)
|
||||
//Needs cards passed into the initilazation args
|
||||
ignore += typesof(/obj/item/toy/cards/cardhand)
|
||||
//Needs cards passed into the initilazation args
|
||||
ignore += typesof(/obj/item/toy/cards/cardhand)
|
||||
//Needs a holodeck area linked to it which is not guarenteed to exist and technically is supposed to have a 1:1 relationship with computer anyway.
|
||||
ignore += typesof(/obj/machinery/computer/holodeck)
|
||||
//runtimes if not paired with a landmark
|
||||
// ignore += typesof(/obj/structure/industrial_lift)
|
||||
// Runtimes if the associated machinery does not exist, but not the base type
|
||||
// ignore += subtypesof(/obj/machinery/airlock_controller)
|
||||
|
||||
var/list/cached_contents = spawn_at.contents.Copy()
|
||||
var/original_turf_type = spawn_at.type
|
||||
var/original_baseturfs = islist(spawn_at.baseturfs) ? spawn_at.baseturfs.Copy() : spawn_at.baseturfs
|
||||
var/original_baseturf_count = length(original_baseturfs)
|
||||
|
||||
GLOB.running_create_and_destroy = TRUE
|
||||
for(var/type_path in typesof(/atom/movable, /turf) - ignore) //No areas please
|
||||
if(ispath(type_path, /turf))
|
||||
spawn_at.ChangeTurf(type_path)
|
||||
//We change it back to prevent baseturfs stacking and hitting the limit
|
||||
spawn_at.ChangeTurf(original_turf_type, original_baseturfs)
|
||||
if(original_baseturf_count != length(spawn_at.baseturfs))
|
||||
TEST_FAIL("[type_path] changed the amount of baseturfs from [original_baseturf_count] to [length(spawn_at.baseturfs)]; [english_list(original_baseturfs)] to [islist(spawn_at.baseturfs) ? english_list(spawn_at.baseturfs) : spawn_at.baseturfs]")
|
||||
//Warn if it changes again
|
||||
original_baseturfs = islist(spawn_at.baseturfs) ? spawn_at.baseturfs.Copy() : spawn_at.baseturfs
|
||||
original_baseturf_count = length(original_baseturfs)
|
||||
else
|
||||
var/atom/creation = new type_path(spawn_at)
|
||||
if(QDELETED(creation))
|
||||
continue
|
||||
//Go all in
|
||||
qdel(creation, force = TRUE)
|
||||
//This will hold a ref to the last thing we process unless we set it to null
|
||||
//Yes byond is fucking sinful
|
||||
creation = null
|
||||
|
||||
//There's a lot of stuff that either spawns stuff in on create, or removes stuff on destroy. Let's cut it all out so things are easier to deal with
|
||||
var/list/to_del = spawn_at.contents - cached_contents
|
||||
if(length(to_del))
|
||||
for(var/atom/to_kill in to_del)
|
||||
qdel(to_kill)
|
||||
|
||||
GLOB.running_create_and_destroy = FALSE
|
||||
//Hell code, we're bound to have ended the round somehow so let's stop if from ending while we work
|
||||
SSticker.delay_end = TRUE
|
||||
//Prevent the garbage subsystem from harddeling anything, if only to save time
|
||||
SSgarbage.collection_timeout[GC_QUEUE_HARDDELETE] = 10000 HOURS
|
||||
//Clear it, just in case
|
||||
cached_contents.Cut()
|
||||
|
||||
//Now that we've qdel'd everything, let's sleep until the gc has processed all the shit we care about
|
||||
var/time_needed = SSgarbage.collection_timeout[GC_QUEUE_CHECK]
|
||||
var/start_time = world.time
|
||||
var/garbage_queue_processed = FALSE
|
||||
|
||||
sleep(time_needed)
|
||||
while(!garbage_queue_processed)
|
||||
var/list/queue_to_check = SSgarbage.queues[GC_QUEUE_CHECK]
|
||||
//How the hell did you manage to empty this? Good job!
|
||||
if(!length(queue_to_check))
|
||||
garbage_queue_processed = TRUE
|
||||
break
|
||||
|
||||
var/list/oldest_packet = queue_to_check[1]
|
||||
//Pull out the time we deld at
|
||||
var/qdeld_at = oldest_packet[1]
|
||||
//If we've found a packet that got del'd later then we finished, then all our shit has been processed
|
||||
if(qdeld_at > start_time)
|
||||
garbage_queue_processed = TRUE
|
||||
break
|
||||
|
||||
if(world.time > start_time + time_needed + 30 MINUTES) //If this gets us gitbanned I'm going to laugh so hard
|
||||
TEST_FAIL("Something has gone horribly wrong, the garbage queue has been processing for well over 30 minutes. What the hell did you do")
|
||||
break
|
||||
|
||||
//Immediately fire the gc right after
|
||||
SSgarbage.next_fire = 1
|
||||
//Unless you've seriously fucked up, queue processing shouldn't take "that" long. Let her run for a bit, see if anything's changed
|
||||
sleep(20 SECONDS)
|
||||
|
||||
//Alright, time to see if anything messed up
|
||||
var/list/cache_for_sonic_speed = SSgarbage.items
|
||||
for(var/path in cache_for_sonic_speed)
|
||||
var/datum/qdel_item/item = cache_for_sonic_speed[path]
|
||||
if(item.failures)
|
||||
TEST_FAIL("[item.name] hard deleted [item.failures] times out of a total del count of [item.qdels]")
|
||||
if(item.no_respect_force)
|
||||
TEST_FAIL("[item.name] failed to respect force deletion [item.no_respect_force] times out of a total del count of [item.qdels]")
|
||||
if(item.no_hint)
|
||||
TEST_FAIL("[item.name] failed to return a qdel hint [item.no_hint] times out of a total del count of [item.qdels]")
|
||||
|
||||
cache_for_sonic_speed = SSatoms.BadInitializeCalls
|
||||
for(var/path in cache_for_sonic_speed)
|
||||
var/fails = cache_for_sonic_speed[path]
|
||||
if(fails & BAD_INIT_NO_HINT)
|
||||
TEST_FAIL("[path] didn't return an Initialize hint")
|
||||
if(fails & BAD_INIT_QDEL_BEFORE)
|
||||
TEST_FAIL("[path] qdel'd in New()")
|
||||
if(fails & BAD_INIT_SLEPT)
|
||||
TEST_FAIL("[path] slept during Initialize()")
|
||||
|
||||
SSticker.delay_end = FALSE
|
||||
//This shouldn't be needed, but let's be polite
|
||||
SSgarbage.collection_timeout[GC_QUEUE_HARDDELETE] = 10 SECONDS
|
||||
@@ -9,9 +9,9 @@
|
||||
var/is_lone = initial(ruleset.flags) & (LONE_RULESET | HIGH_IMPACT_RULESET)
|
||||
|
||||
if (has_scaling_cost && is_lone)
|
||||
Fail("[ruleset] has a scaling_cost, but is also a lone/highlander ruleset.")
|
||||
TEST_FAIL("[ruleset] has a scaling_cost, but is also a lone/highlander ruleset.")
|
||||
else if (!has_scaling_cost && !is_lone)
|
||||
Fail("[ruleset] has no scaling cost, but is also not a lone/highlander ruleset.")
|
||||
TEST_FAIL("[ruleset] has no scaling cost, but is also not a lone/highlander ruleset.")
|
||||
|
||||
/// Verifies that dynamic rulesets have unique antag_flag.
|
||||
/datum/unit_test/dynamic_unique_antag_flags
|
||||
@@ -26,11 +26,11 @@
|
||||
var/antag_flag = initial(ruleset.antag_flag)
|
||||
|
||||
if (isnull(antag_flag))
|
||||
Fail("[ruleset] has a null antag_flag!")
|
||||
TEST_FAIL("[ruleset] has a null antag_flag!")
|
||||
continue
|
||||
|
||||
if (antag_flag in known_antag_flags)
|
||||
Fail("[ruleset] has a non-unique antag_flag [antag_flag] (used by [known_antag_flags[antag_flag]])!")
|
||||
TEST_FAIL("[ruleset] has a non-unique antag_flag [antag_flag] (used by [known_antag_flags[antag_flag]])!")
|
||||
continue
|
||||
|
||||
known_antag_flags[antag_flag] = ruleset
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
/datum/unit_test/find_reference_sanity
|
||||
|
||||
/atom/movable/ref_holder
|
||||
var/static/atom/movable/ref_test/static_test
|
||||
var/atom/movable/ref_test/test
|
||||
var/list/test_list = list()
|
||||
var/list/test_assoc_list = list()
|
||||
|
||||
/atom/movable/ref_holder/Destroy()
|
||||
test = null
|
||||
static_test = null
|
||||
test_list.Cut()
|
||||
test_assoc_list.Cut()
|
||||
return ..()
|
||||
@@ -25,6 +27,12 @@
|
||||
SSgarbage.should_save_refs = TRUE
|
||||
|
||||
//Sanity check
|
||||
/*
|
||||
#if DM_VERSION >= 515
|
||||
var/refcount = refcount(victim)
|
||||
TEST_ASSERT_EQUAL(refcount, 3, "Should be: test references: 0 + baseline references: 3 (victim var,loc,allocated list)")
|
||||
#endif
|
||||
*/
|
||||
victim.DoSearchVar(testbed, "Sanity Check", search_time = 1) //We increment search time to get around an optimization
|
||||
TEST_ASSERT(!victim.found_refs.len, "The ref-tracking tool found a ref where none existed")
|
||||
SSgarbage.should_save_refs = FALSE
|
||||
@@ -39,6 +47,12 @@
|
||||
testbed.test_list += victim
|
||||
testbed.test_assoc_list["baseline"] = victim
|
||||
|
||||
/*
|
||||
#if DM_VERSION >= 515
|
||||
var/refcount = refcount(victim)
|
||||
TEST_ASSERT_EQUAL(refcount, 6, "Should be: test references: 3 + baseline references: 3 (victim var,loc,allocated list)")
|
||||
#endif
|
||||
*/
|
||||
victim.DoSearchVar(testbed, "First Run", search_time = 2)
|
||||
|
||||
TEST_ASSERT(victim.found_refs["test"], "The ref-tracking tool failed to find a regular value")
|
||||
@@ -56,6 +70,12 @@
|
||||
testbed.vis_contents += victim
|
||||
testbed.test_assoc_list[victim] = TRUE
|
||||
|
||||
/*
|
||||
#if DM_VERSION >= 515
|
||||
var/refcount = refcount(victim)
|
||||
TEST_ASSERT_EQUAL(refcount, 6, "Should be: test references: 3 + baseline references: 3 (victim var,loc,allocated list)")
|
||||
#endif
|
||||
*/
|
||||
victim.DoSearchVar(testbed, "Second Run", search_time = 3)
|
||||
|
||||
//This is another sanity check
|
||||
@@ -76,6 +96,12 @@
|
||||
var/list/to_find_assoc = list(victim)
|
||||
testbed.test_assoc_list["Nesting"] = to_find_assoc
|
||||
|
||||
/*
|
||||
#if DM_VERSION >= 515
|
||||
var/refcount = refcount(victim)
|
||||
TEST_ASSERT_EQUAL(refcount, 6, "Should be: test references: 3 + baseline references: 3 (victim var,loc,allocated list)")
|
||||
#endif
|
||||
*/
|
||||
victim.DoSearchVar(victim, "Third Run Self", search_time = 4)
|
||||
victim.DoSearchVar(testbed, "Third Run Testbed", search_time = 4)
|
||||
TEST_ASSERT(victim.found_refs["self_ref"], "The ref-tracking tool failed to find a self reference")
|
||||
@@ -90,7 +116,12 @@
|
||||
|
||||
//Calm before the storm
|
||||
testbed.test_assoc_list = list(null = victim)
|
||||
|
||||
/*
|
||||
#if DM_VERSION >= 515
|
||||
var/refcount = refcount(victim)
|
||||
TEST_ASSERT_EQUAL(refcount, 4, "Should be: test references: 1 + baseline references: 3 (victim var,loc,allocated list)")
|
||||
#endif
|
||||
*/
|
||||
victim.DoSearchVar(testbed, "Fourth Run", search_time = 5)
|
||||
TEST_ASSERT(testbed.test_assoc_list, "The ref-tracking tool failed to find a null key'd assoc list entry")
|
||||
|
||||
@@ -105,7 +136,39 @@
|
||||
var/list/to_find_null_assoc_nested = list(victim)
|
||||
testbed.test_assoc_list[null] = to_find_null_assoc_nested
|
||||
|
||||
/*
|
||||
#if DM_VERSION >= 515
|
||||
var/refcount = refcount(victim)
|
||||
TEST_ASSERT_EQUAL(refcount, 5, "Should be: test references: 2 + baseline references: 3 (victim var,loc,allocated list)")
|
||||
#endif
|
||||
*/
|
||||
victim.DoSearchVar(testbed, "Fifth Run", search_time = 6)
|
||||
TEST_ASSERT(victim.found_refs[to_find_in_key], "The ref-tracking tool failed to find a nested assoc list key")
|
||||
TEST_ASSERT(victim.found_refs[to_find_null_assoc_nested], "The ref-tracking tool failed to find a null key'd nested assoc list entry")
|
||||
SSgarbage.should_save_refs = FALSE
|
||||
|
||||
/datum/unit_test/find_reference_static_inPvestigation/Run()
|
||||
var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test)
|
||||
var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder)
|
||||
pass(testbed)
|
||||
SSgarbage.should_save_refs = TRUE
|
||||
|
||||
//Lets check static vars now, since those can be a real headache
|
||||
testbed.static_test = victim
|
||||
|
||||
//Yes we do actually need to do this. The searcher refuses to read weird lists
|
||||
//And global.vars is a really weird list
|
||||
var/global_vars = list()
|
||||
for(var/key in global.vars)
|
||||
global_vars[key] = global.vars[key]
|
||||
|
||||
/*
|
||||
#if DM_VERSION >= 515
|
||||
var/refcount = refcount(victim)
|
||||
TEST_ASSERT_EQUAL(refcount, 5, "Should be: test references: 2 + baseline references: 3 (victim var,loc,allocated list)")
|
||||
#endif
|
||||
*/
|
||||
victim.DoSearchVar(global_vars, "Sixth Run", search_time = 7)
|
||||
|
||||
TEST_ASSERT(victim.found_refs[global_vars], "The ref-tracking tool failed to find a natively global variable")
|
||||
SSgarbage.should_save_refs = FALSE
|
||||
|
||||
@@ -18,4 +18,4 @@
|
||||
var/list/unreachables = all_possible_knowledge - list_to_check
|
||||
for(var/X in unreachables)
|
||||
var/datum/eldritch_knowledge/eldritch_knowledge = X
|
||||
Fail("[initial(eldritch_knowledge.name)] is unreachable by players! Add it to the blacklist in /code/modules/unit_tests/heretic_knowledge.dm if it is purposeful!")
|
||||
TEST_FAIL("[initial(eldritch_knowledge.name)] is unreachable by players! Add it to the blacklist in /code/modules/unit_tests/heretic_knowledge.dm if it is purposeful!")
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
var/datum/keybinding/KB = i
|
||||
if(initial(KB.keybind_signal) || !initial(KB.name))
|
||||
continue
|
||||
Fail("[initial(KB.name)] does not have a keybind signal defined.")
|
||||
TEST_FAIL("[KB.name] does not have a keybind signal defined.")
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
for(var/stackpath in paths)
|
||||
var/obj/item/stack/stack = new stackpath
|
||||
if(!stack.merge_type)
|
||||
Fail("([stack]) lacks set merge_type variable!")
|
||||
TEST_FAIL("([stack]) lacks set merge_type variable!")
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
H.equip_to_slot_or_del(new outfit.##outfit_key(H), ##slot_name, TRUE); \
|
||||
/* We don't check the result of equip_to_slot_or_del because it returns false for random jumpsuits, as they delete themselves on init */ \
|
||||
if (!H.get_item_by_slot(##slot_name)) { \
|
||||
Fail("[outfit.name]'s [#outfit_key] is invalid!"); \
|
||||
TEST_FAIL("[outfit.name]'s [#outfit_key] is invalid!"); \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -51,6 +51,6 @@
|
||||
var/number = backpack_contents[path] || 1
|
||||
for (var/_ in 1 to number)
|
||||
if (!H.equip_to_slot_or_del(new path(H), ITEM_SLOT_BACKPACK, TRUE))
|
||||
Fail("[outfit.name]'s backpack_contents are invalid! Couldn't add [path] to backpack.")
|
||||
TEST_FAIL("[outfit.name]'s backpack_contents are invalid! Couldn't add [path] to backpack.")
|
||||
|
||||
#undef CHECK_OUTFIT_SLOT
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
for(var/i in 1 to seed.growthstages)
|
||||
if("[seed.icon_grow][i]" in states)
|
||||
continue
|
||||
Fail("[seed.name] ([seed.type]) lacks the [seed.icon_grow][i] icon!")
|
||||
TEST_FAIL("[seed.name] ([seed.type]) lacks the [seed.icon_grow][i] icon!")
|
||||
|
||||
if(!(seed.icon_dead in states))
|
||||
Fail("[seed.name] ([seed.type]) lacks the [seed.icon_dead] icon!")
|
||||
TEST_FAIL("[seed.name] ([seed.type]) lacks the [seed.icon_dead] icon!")
|
||||
|
||||
if(seed.icon_harvest) // mushrooms have no grown sprites, same for items with no product
|
||||
if(!(seed.icon_harvest in states))
|
||||
Fail("[seed.name] ([seed.type]) lacks the [seed.icon_harvest] icon!")
|
||||
TEST_FAIL("[seed.name] ([seed.type]) lacks the [seed.icon_harvest] icon!")
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
for(var/path in typesof(/obj/item/projectile))
|
||||
var/obj/item/projectile/projectile = path
|
||||
if(initial(projectile.movement_type) & PHASING)
|
||||
Fail("[path] has default movement type PHASING. Piercing projectiles should be done using the projectile piercing system, not movement_types!")
|
||||
TEST_FAIL("[path] has default movement type PHASING. Piercing projectiles should be done using the projectile piercing system, not movement_types!")
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
var/test_info = G.test()
|
||||
if(!test_info["success"])
|
||||
var/message = test_info["message"]
|
||||
Fail("Gas reaction [G.name] is failing its unit test with the following message: [message]")
|
||||
TEST_FAIL("Gas reaction [G.name] is failing its unit test with the following message: [message]")
|
||||
|
||||
@@ -11,4 +11,4 @@
|
||||
var/datum/chemical_reaction/R = V
|
||||
for(var/id in (R.required_reagents + R.required_catalysts))
|
||||
if(!GLOB.chemical_reagents_list[id])
|
||||
Fail("Unknown chemical id \"[id]\" in recipe [R.type]")
|
||||
TEST_FAIL("Unknown chemical id \"[id]\" in recipe [R.type]")
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
var/datum/chemical_reaction/r1 = reactions[i]
|
||||
var/datum/chemical_reaction/r2 = reactions[i2]
|
||||
if(chem_recipes_do_conflict(r1, r2))
|
||||
Fail("Chemical recipe conflict between [r1.type] and [r2.type]")
|
||||
TEST_FAIL("Chemical recipe conflict between [r1.type] and [r2.type]")
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
for(var/typepath in subtypesof(/datum/species))
|
||||
var/datum/species/S = typepath
|
||||
if(initial(S.changesource_flags) == NONE)
|
||||
Fail("A species type was detected with no changesource flags: [S]")
|
||||
TEST_FAIL("A species type was detected with no changesource flags: [S]")
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
if(ss.flags & SS_NO_INIT)
|
||||
continue
|
||||
if(!ss.initialized)
|
||||
Fail("[ss]([ss.type]) is a subsystem meant to initialize but doesn't get set as initialized.")
|
||||
TEST_FAIL("[ss]([ss.type]) is a subsystem meant to initialize but doesn't get set as initialized.")
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
/datum/unit_test/timer_sanity/Run()
|
||||
if(SStimer.bucket_count < 0)
|
||||
Fail("SStimer is going into negative bucket count from something")
|
||||
TEST_ASSERT(SStimer.bucket_count >= 0,
|
||||
"SStimer is going into negative bucket count from something")
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Usage:
|
||||
Override /Run() to run your test code
|
||||
|
||||
Call Fail() to fail the test (You should specify a reason)
|
||||
Call TEST_FAIL() to fail the test (You should specify a reason)
|
||||
|
||||
You may use /New() and /Destroy() for setup/teardown respectively
|
||||
|
||||
@@ -15,6 +15,18 @@ GLOBAL_DATUM(current_test, /datum/unit_test)
|
||||
GLOBAL_VAR_INIT(failed_any_test, FALSE)
|
||||
GLOBAL_VAR(test_log)
|
||||
|
||||
/// A list of every test that is currently focused.
|
||||
/// Use the PERFORM_ALL_TESTS macro instead.
|
||||
GLOBAL_VAR_INIT(focused_tests, focused_tests())
|
||||
|
||||
/proc/focused_tests()
|
||||
var/list/focused_tests = list()
|
||||
for (var/datum/unit_test/unit_test as anything in subtypesof(/datum/unit_test))
|
||||
if (initial(unit_test.focus))
|
||||
focused_tests += unit_test
|
||||
|
||||
return focused_tests.len > 0 ? focused_tests : null
|
||||
|
||||
/datum/unit_test
|
||||
//Bit of metadata for the future maybe
|
||||
var/list/procs_tested
|
||||
@@ -62,15 +74,15 @@ GLOBAL_VAR(test_log)
|
||||
return ..()
|
||||
|
||||
/datum/unit_test/proc/Run()
|
||||
Fail("Run() called parent or not implemented")
|
||||
TEST_FAIL("Run() called parent or not implemented")
|
||||
|
||||
/datum/unit_test/proc/Fail(reason = "No reason")
|
||||
/datum/unit_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, reason)
|
||||
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
|
||||
@@ -80,16 +92,71 @@ GLOBAL_VAR(test_log)
|
||||
arguments = list(run_loc_floor_bottom_left)
|
||||
else if (arguments[1] == null)
|
||||
arguments[1] = run_loc_floor_bottom_left
|
||||
var/instance = new type(arglist(arguments))
|
||||
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()
|
||||
allocated += instance
|
||||
return instance
|
||||
|
||||
/*
|
||||
/datum/unit_test/proc/test_screenshot(name, icon/icon)
|
||||
if (!istype(icon))
|
||||
TEST_FAIL("[icon] is not an icon.")
|
||||
return
|
||||
|
||||
var/path_prefix = replacetext(replacetext("[type]", "/datum/unit_test/", ""), "/", "_")
|
||||
name = replacetext(name, "/", "_")
|
||||
|
||||
var/filename = "code/modules/unit_tests/screenshots/[path_prefix]_[name].png"
|
||||
|
||||
if (fexists(filename))
|
||||
var/data_filename = "data/screenshots/[path_prefix]_[name].png"
|
||||
fcopy(icon, data_filename)
|
||||
log_test("\t[path_prefix]_[name] was found, putting in data/screenshots")
|
||||
else if (fexists("code"))
|
||||
// We are probably running in a local build
|
||||
fcopy(icon, filename)
|
||||
TEST_FAIL("Screenshot for [name] did not exist. One has been created.")
|
||||
else
|
||||
// We are probably running in real CI, so just pretend it worked and move on
|
||||
fcopy(icon, "data/screenshots_new/[path_prefix]_[name].png")
|
||||
|
||||
log_test("\t[path_prefix]_[name] was put in data/screenshots_new")
|
||||
|
||||
/// Helper for screenshot tests to take an image of an atom from all directions and insert it into one icon
|
||||
/datum/unit_test/proc/get_flat_icon_for_all_directions(atom/thing, no_anim = TRUE)
|
||||
var/icon/output = icon('icons/effects/effects.dmi', "nothing")
|
||||
|
||||
for (var/direction in GLOB.cardinals)
|
||||
var/icon/partial = getFlatIcon(thing, defdir = direction, no_anim = no_anim)
|
||||
output.Insert(partial, dir = direction)
|
||||
|
||||
return output
|
||||
*/
|
||||
/// Logs a test message. Will use GitHub action syntax found at https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions
|
||||
/datum/unit_test/proc/log_for_test(text, priority, file, line)
|
||||
var/map_name = SSmapping.config.map_name
|
||||
|
||||
// Need to escape the text to properly support newlines.
|
||||
var/annotation_text = replacetext(text, "%", "%25")
|
||||
annotation_text = replacetext(annotation_text, "\n", "%0A")
|
||||
|
||||
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
|
||||
var/duration = REALTIMEOFDAY
|
||||
|
||||
log_world("::group::[test_path]")
|
||||
test.Run()
|
||||
|
||||
duration = REALTIMEOFDAY - duration
|
||||
@@ -99,11 +166,28 @@ GLOBAL_VAR(test_log)
|
||||
var/list/log_entry = list("[test.succeeded ? "PASS" : "FAIL"]: [test_path] [duration / 10]s")
|
||||
var/list/fail_reasons = test.fail_reasons
|
||||
|
||||
for(var/J in 1 to LAZYLEN(fail_reasons))
|
||||
log_entry += "\tREASON #[J]: [fail_reasons[J]]"
|
||||
for(var/reasonID in 1 to LAZYLEN(fail_reasons))
|
||||
var/text = fail_reasons[reasonID][1]
|
||||
var/file = fail_reasons[reasonID][2]
|
||||
var/line = fail_reasons[reasonID][3]
|
||||
|
||||
test.log_for_test(text, "error", file, line)
|
||||
|
||||
// Normal log message
|
||||
log_entry += "\tREASON #[reasonID]: [text] at [file]:[line]"
|
||||
|
||||
var/message = log_entry.Join("\n")
|
||||
log_test(message)
|
||||
|
||||
var/test_output_desc = "[test_path] [duration / 10]s"
|
||||
if (test.succeeded)
|
||||
log_world("[TEST_OUTPUT_GREEN("PASS")] [test_output_desc]")
|
||||
|
||||
log_world("::endgroup::")
|
||||
|
||||
if (!test.succeeded)
|
||||
log_world("::error::[TEST_OUTPUT_RED("FAIL")] [test_output_desc]")
|
||||
|
||||
test_results[test_path] = list("status" = test.succeeded ? UNIT_TEST_PASSED : UNIT_TEST_FAILED, "message" = message, "name" = test_path)
|
||||
|
||||
qdel(test)
|
||||
@@ -112,11 +196,13 @@ GLOBAL_VAR(test_log)
|
||||
CHECK_TICK
|
||||
|
||||
var/list/tests_to_run = subtypesof(/datum/unit_test)
|
||||
var/list/focused_tests = list()
|
||||
for (var/_test_to_run in tests_to_run)
|
||||
var/datum/unit_test/test_to_run = _test_to_run
|
||||
if (initial(test_to_run.focus))
|
||||
tests_to_run = list(test_to_run)
|
||||
break
|
||||
focused_tests += test_to_run
|
||||
if(length(focused_tests))
|
||||
tests_to_run = focused_tests
|
||||
|
||||
tests_to_run = sortTim(tests_to_run, /proc/cmp_unit_test_priority)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
break
|
||||
mobloc = default_mobloc
|
||||
if(!mobloc)
|
||||
Fail("Unable to find a location to create test mob")
|
||||
TEST_FAIL("Unable to find a location to create test mob")
|
||||
return FALSE
|
||||
|
||||
var/mob/living/carbon/human/H = new mobtype(mobloc)
|
||||
@@ -44,7 +44,7 @@
|
||||
endOxyloss = H.getOxyLoss()
|
||||
|
||||
if(!startOxyloss < endOxyloss)
|
||||
Fail("Human mob is not taking oxygen damage in space. (Before: [startOxyloss]; after: [endOxyloss])")
|
||||
TEST_FAIL("Human mob is not taking oxygen damage in space. (Before: [startOxyloss]; after: [endOxyloss])")
|
||||
|
||||
qdel(H)
|
||||
return 1
|
||||
@@ -74,7 +74,7 @@
|
||||
|
||||
// Now that pred belly exists, we can eat the prey.
|
||||
if(!pred.vore_selected)
|
||||
Fail("[pred] has no vore_selected.")
|
||||
TEST_FAIL("[pred] has no vore_selected.")
|
||||
return TRUE
|
||||
|
||||
// Attempt to eat the prey
|
||||
@@ -82,7 +82,7 @@
|
||||
pred.vore_selected.nom_mob(prey)
|
||||
|
||||
if(prey.loc != pred.vore_selected)
|
||||
Fail("[pred.vore_selected].nom_mob([prey]) did not put prey inside [pred]")
|
||||
TEST_FAIL("[pred.vore_selected].nom_mob([prey]) did not put prey inside [pred]")
|
||||
return TRUE
|
||||
|
||||
// Okay, we succeeded in eating them, now lets wait a bit
|
||||
@@ -96,7 +96,7 @@
|
||||
// Alright lets check it!
|
||||
endOxyloss = prey.getOxyLoss()
|
||||
if(startOxyloss < endOxyloss)
|
||||
Fail("Prey takes oxygen damage in a pred's belly! (Before: [startOxyloss]; after: [endOxyloss])")
|
||||
TEST_FAIL("Prey takes oxygen damage in a pred's belly! (Before: [startOxyloss]; after: [endOxyloss])")
|
||||
qdel(prey)
|
||||
qdel(pred)
|
||||
return TRUE
|
||||
@@ -128,7 +128,7 @@
|
||||
|
||||
// Now that pred belly exists, we can eat the prey.
|
||||
if(!pred.vore_selected)
|
||||
Fail("[pred] has no vore_selected.")
|
||||
TEST_FAIL("[pred] has no vore_selected.")
|
||||
return TRUE
|
||||
|
||||
// Attempt to eat the prey
|
||||
@@ -136,12 +136,12 @@
|
||||
pred.vore_selected.nom_mob(prey)
|
||||
|
||||
if(prey.loc != pred.vore_selected)
|
||||
Fail("[pred.vore_selected].nom_mob([prey]) did not put prey inside [pred]")
|
||||
TEST_FAIL("[pred.vore_selected].nom_mob([prey]) did not put prey inside [pred]")
|
||||
return TRUE
|
||||
else
|
||||
var/turf/T = locate(/turf/open/space)
|
||||
if(!T)
|
||||
Fail("could not find a space turf for testing")
|
||||
TEST_FAIL("could not find a space turf for testing")
|
||||
return TRUE
|
||||
else
|
||||
pred.forceMove(T)
|
||||
@@ -159,7 +159,7 @@
|
||||
endOxyloss = prey.getOxyLoss()
|
||||
endBruteloss = prey.getBruteLoss()
|
||||
if(startBruteloss < endBruteloss)
|
||||
Fail("Prey takes brute damage in space! (Before: [startBruteloss]; after: [endBruteloss])")
|
||||
TEST_FAIL("Prey takes brute damage in space! (Before: [startBruteloss]; after: [endBruteloss])")
|
||||
qdel(prey)
|
||||
qdel(pred)
|
||||
return TRUE
|
||||
@@ -189,7 +189,7 @@
|
||||
|
||||
// Now that pred belly exists, we can eat the prey.
|
||||
if(!pred.vore_selected)
|
||||
Fail("[pred] has no vore_selected.")
|
||||
TEST_FAIL("[pred] has no vore_selected.")
|
||||
return TRUE
|
||||
|
||||
// Attempt to eat the prey
|
||||
@@ -197,7 +197,7 @@
|
||||
pred.vore_selected.nom_mob(prey)
|
||||
|
||||
if(prey.loc != pred.vore_selected)
|
||||
Fail("[pred.vore_selected].nom_mob([prey]) did not put prey inside [pred]")
|
||||
TEST_FAIL("[pred.vore_selected].nom_mob([prey]) did not put prey inside [pred]")
|
||||
return TRUE
|
||||
|
||||
// Okay, we succeeded in eating them, now lets wait a bit
|
||||
@@ -212,7 +212,7 @@
|
||||
// Alright lets check it!
|
||||
endBruteBurn = prey.getBruteLoss() + prey.getFireLoss()
|
||||
if(startBruteBurn >= endBruteBurn)
|
||||
Fail("Prey doesn't take damage in digesting belly! (Before: [startBruteBurn]; after: [endBruteBurn])")
|
||||
TEST_FAIL("Prey doesn't take damage in digesting belly! (Before: [startBruteBurn]; after: [endBruteBurn])")
|
||||
qdel(prey)
|
||||
qdel(pred)
|
||||
return TRUE
|
||||
|
||||
Reference in New Issue
Block a user