Unit Test rework & Master/Ticker update (#17912)

* Unit Test rework & Master/Ticker update

* Fixes and working unit testing

* Fixes

* Test fixes and FA update

* Fixed runtimes

* Radio subsystem

* move that glob wherever later

* ident

* CIBUILDING compile option

* Fixed runtimes

* Some changes to the workflow

* CI Split

* More split

* Pathing

* Linters and Annotators

* ci dir fix

* Missing undef fixed

* Enable grep checks

* More test conversions

* More split

* Correct file

* Removes unneeded inputs

* oop

* More dependency changes

* More conversions

* Conversion fixes

* Fixes

* Some assert fixes

* Corrects start gate

* Converted some README.dms to README.mds

* Removes duplicate proc

* Removes unused defines

* Example configs

* fix dll access viol by double calling

* Post-rebase fixes

* Cleans up names global list

* Undef restart counter

* More code/game/ cleanup

* Statpanel update

* Skybox

* add

* Fix ticker

* Roundend fix

* Persistence dependency update

* Reordering

* Reordering

* Reordering

* Initstage fix

* .

* .

* Reorder

* Reorder

* Circle

* Mobs

* Air

* Test fix

* CI Script Fix

* Configs

* More ticker stuff

* This is now in 'reboot world'

* Restart world announcements

* no glob in PreInit

* to define

* Update

* Removed old include

* Make this file normal again

* moved

* test

* shared unit testing objects

* Updates batched_spritesheets and universal_icon

* .

* job data debug

* rm that

* init order

* show us

* .

* i wonder

* .

* .

* urg

* do we not have a job ID?

* .

* rm sleep for now

* updated rust-g linux binaries

* binaries update 2

* binaries update 3

* testing something

* change that

* test something

* .

* .

* .

* locavar

* test

* move that

* .

* debug

* don't run this test

* strack trace it

* cleaner

* .

* .

* cras again

* also comment this out

* return to official rust g

* Update robot_icons.dm

* monitor the generation

* .

---------

Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
Selis
2025-08-10 01:37:23 +02:00
committed by GitHub
parent 5e54e17ebd
commit f4bf017921
382 changed files with 10193 additions and 4421 deletions
-177
View File
@@ -1,177 +0,0 @@
/datum/unit_test/all_clothing_shall_be_valid
name = "CLOTHING: All clothing shall be valid"
var/signal_failed = FALSE
/datum/unit_test/all_clothing_shall_be_valid/start_test()
var/failed = 0
var/obj/storage = new()
var/list/scan = subtypesof(/obj/item/clothing)
scan -= typesof(/obj/item/clothing/head/hood) // These are part of clothing, need to be tested uniquely
// Remove material armors, as dev_warning cannot be used to set their name
scan -= /obj/item/clothing/suit/armor/material
scan -= /obj/item/clothing/head/helmet/material
scan -= /obj/item/clothing/ears/offear // This is used for equip logic, not ingame
scan -= /obj/item/clothing/mask/ai // Breaks unit test entirely TODO
var/i = 0
var/tenths = 1
var/a_tenth = scan.len / 10
for(var/path as anything in scan)
var/obj/item/clothing/C = new path(storage)
failed += test_clothing(C)
if(i > tenths * a_tenth)
log_unit_test("Clothing - Progress [tenths * 10]% - [i]/[scan.len]")
log_unit_test("---------------------------------------------------")
tenths++
if(istype(C,/obj/item/clothing/suit/storage/hooded))
var/obj/item/clothing/suit/storage/hooded/H = C
if(H.hood) // Testing hoods when they init
failed += test_clothing(H.hood,storage)
i++
qdel(C)
qdel(storage)
if(failed)
fail("One or more /obj/item/clothing items had invalid flags or icons")
else
pass("All /obj/item/clothing are valid.")
return 1
/datum/unit_test/all_clothing_shall_be_valid/proc/test_clothing(var/obj/item/clothing/C,var/obj/storage)
var/failed = FALSE
// Do not test base-types
if(C.name == DEVELOPER_WARNING_NAME)
return FALSE
// ID
if(!C.name)
log_unit_test("[C.type]: Clothing - Missing name.")
failed = TRUE
if(C.name == "")
log_unit_test("[C.type]: Clothing - Empty name.")
failed = TRUE
// Icons
if(!("[C.icon_state]" in cached_icon_states(C.icon)))
if(C.icon == initial(C.icon) && C.icon_state == initial(C.icon_state))
log_unit_test("[C.type]: Clothing - Icon_state \"[C.icon_state]\" is not present in [C.icon].")
else
log_unit_test("[C.type]: Clothing - Icon_state \"[C.icon_state]\" is not present in [C.icon]. This icon/state was changed by init. Initial icon \"[initial(C.icon)]\". initial icon_state \"[initial(C.icon_state)]\". Check code.")
failed = TRUE
// Disabled, as currently not working in a presentable way, spams the CI hard, do not enable unless fixed
#ifdef UNIT_TEST
// Time for the most brutal part. Dressing up some mobs with set species, and checking they have art
// An entire signal just for unittests had to be made for this!
var/list/body_types = list(SPECIES_HUMAN,SPECIES_VOX,SPECIES_TESHARI) // Otherwise we would be here for centuries
// **************************************************************************************************************************
body_types = list() // DISABLED FOR NOW, No single person can resolve how many sprites are missing.
// **************************************************************************************************************************
if(body_types.len)
if(C.species_restricted && C.species_restricted.len)
if(C.species_restricted[1] == "exclude")
for(var/B in body_types)
if(B in C.species_restricted)
body_types -= B
else
var/list/new_list = list()
for(var/B in body_types)
if(B in C.species_restricted)
new_list += B
body_types = new_list
// Get actual species that can use this, based on the mess of restricted/excluded logic above
var/obj/mob_storage = new()
var/mob/living/carbon/human/H = new(mob_storage)
RegisterSignal(H, COMSIG_UNITTEST_DATA, PROC_REF(get_signal_data))
for(var/B in body_types)
H.set_species(B)
// spawn the mob, signalize it, and then give it the item to see what it gets.
H.put_in_active_hand(C)
H.equip_to_appropriate_slot(C)
H.drop_from_inventory(C, storage)
UnregisterSignal(H, COMSIG_UNITTEST_DATA)
qdel(H)
qdel(mob_storage)
// We failed the mob check
if(signal_failed)
failed = TRUE
#endif
// Temps
if(C.min_cold_protection_temperature < 0)
log_unit_test("[C.type]: Clothing - Cold protection was lower than 0.")
failed = TRUE
if(C.max_heat_protection_temperature && C.min_cold_protection_temperature && C.max_heat_protection_temperature < C.min_cold_protection_temperature)
log_unit_test("[C.type]: Clothing - Maximum heat protection was greater than minimum cold protection.")
failed = TRUE
//var/valid_range = HEAD|UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
if(C.cold_protection)
if(islist(C.cold_protection))
log_unit_test("[C.type]: Clothing - cold_protection was defined as a list, when it is a bitflag.")
failed = TRUE
else if(!isnum(C.cold_protection))
log_unit_test("[C.type]: Clothing - cold_protection was defined as something other than a number, when it is a bitflag.")
failed = TRUE
else
if(C.cold_protection && C.cold_protection != FULL_BODY)
// Check flags that should be unused
if(C.cold_protection & FACE)
log_unit_test("[C.type]: Clothing - cold_protection uses FACE bitflag, this provides no protection, use HEAD.")
failed = TRUE
if(C.cold_protection & EYES)
log_unit_test("[C.type]: Clothing - cold_protection uses EYES bitflag, this provides no protection, use HEAD.")
failed = TRUE
if(C.heat_protection)
if(islist(C.heat_protection))
log_unit_test("[C.type]: Clothing - heat_protection was defined as a list, when it is a bitflag.")
failed = TRUE
else if(!isnum(C.heat_protection))
log_unit_test("[C.type]: Clothing - heat_protection was defined as something other than a number, when it is a bitflag.")
failed = TRUE
else
if(C.heat_protection && C.heat_protection != FULL_BODY)
// Check flags that should be unused
if(C.heat_protection & FACE)
log_unit_test("[C.type]: Clothing - heat_protection uses FACE bitflag, this provides no protection, use HEAD.")
failed = TRUE
if(C.heat_protection & EYES)
log_unit_test("[C.type]: Clothing - heat_protection uses EYES bitflag, this provides no protection, use HEAD.")
failed = TRUE
return failed
/datum/unit_test/all_clothing_shall_be_valid/get_signal_data(atom/source, list/data = list())
switch(data[1])
if("set_slot")
var/slot_name = data[2]
var/set_icon = data[3]
var/set_state = data[4]
//var/in_hands = data[5]
var/item_path = data[6]
var/species = data[7]
if(!species)
return
if(!set_icon)
return
if(!set_state)
return
// Ignore storage
if(slot_name == slot_l_hand_str)
return
if(slot_name == slot_r_hand_str)
return
// All that matters
if(!("[set_state]" in cached_icon_states(set_icon)))
log_unit_test("[item_path]: Clothing - Testing \"[species]\" state \"[set_state]\" for slot \"[slot_name]\", but it was not in dmi \"[set_icon]\"")
signal_failed = TRUE
return
-91
View File
@@ -1,91 +0,0 @@
/datum/unit_test/sprite_accessories_shall_be_unique
name = "COSMETICS: Entries shall have unique name."
/datum/unit_test/sprite_accessories_shall_be_unique/start_test()
var/failed = 0
failed += validate_accessory_list( /datum/sprite_accessory/ears)
failed += validate_accessory_list( /datum/sprite_accessory/facial_hair)
failed += validate_accessory_list( /datum/sprite_accessory/hair)
failed += validate_accessory_list( /datum/sprite_accessory/hair_accessory)
failed += validate_accessory_list( /datum/sprite_accessory/marking)
failed += validate_accessory_list( /datum/sprite_accessory/tail)
failed += validate_accessory_list( /datum/sprite_accessory/wing)
if(failed)
fail("One or more /datum/sprite_accessory definitions had invalid names, icon_states, or names were reused definitions")
else
pass("All /datum/sprite_accessory definitions had correct settings.")
return 1
/datum/unit_test/sprite_accessories_shall_be_unique/proc/validate_accessory_list(var/path)
var/failed = 0
var/total_good = 0
var/total_all = 0
var/list/collection = list()
for(var/SP in subtypesof(path))
total_all++
var/datum/sprite_accessory/A = new SP()
if(!A)
log_unit_test("[SP]: Cosmetic - Path resolved to null in list.")
continue
if(!A.name)
log_unit_test("[A] - [A.type]: Cosmetic - Missing name.")
failed = 1
if(A.name == DEVELOPER_WARNING_NAME)
continue
if(collection[A.name])
log_unit_test("[A] - [A.type]: Cosmetic - Name defined twice. Original def [collection[A.name]]")
failed = 1
else
collection[A.name] = A.type
if(istype(A,text2path("[path]/invisible")))
if(A.icon_state)
log_unit_test("[A] - [A.type]: Cosmetic - Invisible subtype has icon_state.")
failed = 1
else if(!A.icon_state)
log_unit_test("[A] - [A.type]: Cosmetic - Has no icon_state.")
failed = 1
else
// Check if valid icon
failed += validate_icons(A)
total_good++
qdel(A)
log_unit_test("[path]: Cosmetic - Total valid count: [total_good]/[total_all].")
return failed
/datum/unit_test/sprite_accessories_shall_be_unique/proc/validate_icons(var/datum/sprite_accessory/A)
var/failed = 0
var/actual_icon_state = A.icon_state
if(istype(A,/datum/sprite_accessory/hair))
actual_icon_state = "[A.icon_state]_s"
if(!(actual_icon_state in cached_icon_states(A.icon)))
log_unit_test("[A] - [A.type]: Cosmetic - Icon_state \"[actual_icon_state]\" is not present in [A.icon].")
failed = 1
if(istype(A,/datum/sprite_accessory/facial_hair))
actual_icon_state = "[A.icon_state]_s"
if(!(actual_icon_state in cached_icon_states(A.icon)))
log_unit_test("[A] - [A.type]: Cosmetic - Icon_state \"[actual_icon_state]\" is not present in [A.icon].")
failed = 1
if(istype(A,/datum/sprite_accessory/marking))
var/datum/sprite_accessory/marking/MA = A
for(var/BP in MA.body_parts)
if(!(BP in BP_ALL))
log_unit_test("[A] - [A.type]: Cosmetic - Has an illegal bodypart \"[BP]\". ONLY use parts listed in BP_ALL.")
failed = 1
actual_icon_state = "[A.icon_state]-[BP]"
if(!(actual_icon_state in cached_icon_states(A.icon)))
log_unit_test("[A] - [A.type]: Cosmetic - Icon_state \"[actual_icon_state]\" is not present in [A.icon].")
failed = 1
return failed
-26
View File
@@ -1,26 +0,0 @@
/datum/unit_test/emotes_shall_have_unique_keys
name = "DECLS: Emotes Shall Have Unique Keys"
/datum/unit_test/emotes_shall_have_unique_keys/start_test()
var/list/keys = list()
var/list/duplicates = list()
var/list/all_emotes = decls_repository.get_decls_of_subtype(/decl/emote)
for(var/etype in all_emotes)
var/decl/emote/emote = all_emotes[etype]
if(!emote.key)
continue
if(emote.key in keys)
if(!duplicates[emote.key])
duplicates[emote.key] = list()
duplicates[emote.key] += etype
else
keys += emote.key
if(length(duplicates))
fail("[length(duplicates)] emote\s had overlapping keys: [english_list(duplicates)].")
else
pass("All emotes had unique keys.")
return TRUE
-32
View File
@@ -1,32 +0,0 @@
/datum/unit_test/disease_must_be_valid
name = "DISEASE: All diseases must have valid data"
/datum/unit_test/disease_must_be_valid/start_test()
var/failed = FALSE
var/list/used_ids = list()
var/count = 0
for(var/datum/disease/D as anything in subtypesof(/datum/disease))
if(initial(D.name) == DEVELOPER_WARNING_NAME)
continue
count++
if(initial(D.medical_name) in used_ids)
log_unit_test("[D]: Disease - Had a reused medical name, this is used as an ID and must be unique.")
failed = TRUE
else
used_ids.Add(initial(D.medical_name))
if(!initial(D.name) || initial(D.name) == "")
log_unit_test("[D]: Disease - Lacks a name.")
failed = TRUE
if(!initial(D.desc) || initial(D.desc) == "")
log_unit_test("[D]: Disease - Lacks a description.")
failed = TRUE
if(failed)
fail("All diseases must have valid data.")
else
pass("All [count] diseases have proper data.")
return failed
-160
View File
@@ -1,160 +0,0 @@
/datum/unit_test/enough_free_gene_slots_must_be_available
name = "GENETICS: Enough free gene slots must be available."
/datum/unit_test/enough_free_gene_slots_must_be_available/start_test()
var/failed = FALSE
if(GLOB.dna_genes.len > (DNA_SE_LENGTH - 10)) // Based off of traitgenes scanned on startup
failed = TRUE
if(failed)
fail("Too few geneslots are empty, minimum 10. Increase DNA_SE_LENGTH.")
else
pass("DNA_SE_LENGTH has enough free space remaining.")
return failed
/datum/unit_test/enough_positive_genes_must_exist
name = "GENETICS: Must be at least one positive gene."
/datum/unit_test/enough_positive_genes_must_exist/start_test()
var/failed = FALSE
if(GLOB.dna_genes_good.len < 1) // Based off of traitgenes scanned on startup
failed = TRUE
if(failed)
fail("Must have at least one positive gene.")
else
pass("Has at least one positive gene.")
return failed
/datum/unit_test/enough_neutral_genes_must_exist
name = "GENETICS: Must be at least one neutral gene."
/datum/unit_test/enough_neutral_genes_must_exist/start_test()
var/failed = FALSE
if(GLOB.dna_genes_neutral.len < 1) // Based off of traitgenes scanned on startup
failed = TRUE
if(failed)
fail("Must have at least one neutral gene.")
else
pass("Has at least one neutral gene.")
return failed
/datum/unit_test/enough_bad_genes_must_exist
name = "GENETICS: Must be at least one bad gene."
/datum/unit_test/enough_bad_genes_must_exist/start_test()
var/failed = FALSE
if(GLOB.dna_genes_bad.len < 1) // Based off of traitgenes scanned on startup
failed = TRUE
if(failed)
fail("Must have at least one bad gene.")
else
pass("Has at least one bad gene.")
return failed
/datum/unit_test/all_dna_injectors_must_be_valid
name = "GENETICS: All dna injectors must be valid."
/datum/unit_test/all_dna_injectors_must_be_valid/start_test()
var/failed = FALSE
for(var/injector_path in subtypesof(/obj/item/dnainjector/set_trait))
var/obj/item/dnainjector/D = new injector_path()
if(!D.block)
log_unit_test("[injector_path]: Genetics - Injector could not resolve geneblock for trait. Missing traitgene?")
failed = TRUE
qdel(D)
if(failed)
fail("Dna injectors have traits that are not genetraits or are missing.")
else
pass("No invalid dna injectors.")
return failed
/datum/unit_test/all_genes_shall_have_unique_name
name = "GENETICS: All genes shall be init with unique names."
/datum/unit_test/all_genes_shall_have_unique_name/start_test()
var/failed = FALSE
var/collection = list()
for(var/datum/gene/G in GLOB.dna_genes)
if(collection[G.name])
log_unit_test("[G.name]: Genetics - Gene name was already in use.")
failed = TRUE
else
collection[G.name] = G.name
if(failed)
fail("Genes shared names. This should not be possible on init, all genes should have their blocknumber attached to them to ensure unique names.")
else
pass("All genes have unique names to use as list ids.")
return failed
/datum/unit_test/genetraits_should_have_valid_dna_bounds
name = "GENETICS: All genes should have valid activation bounds."
/datum/unit_test/genetraits_should_have_valid_dna_bounds/start_test()
var/failed = FALSE
for(var/datum/gene/trait/G in GLOB.trait_to_dna_genes)
if(!G.linked_trait)
log_unit_test("[G.name]: Genetics - Has missing linked trait.")
failed = TRUE
continue
if(!G.linked_trait.activity_bounds)
log_unit_test("[G.name]: Genetics - Has no activation bounds.")
failed = TRUE
continue
if(!G.linked_trait.activity_bounds.len)
log_unit_test("[G.name]: Genetics - Has empty activation bounds.")
failed = TRUE
continue
// DNA activation bounds. Usually they are in a list as follows:
// [1]DNA_OFF_LOWERBOUND = 1, begining of the threshold where a gene turns off.
// [2]DNA_OFF_UPPERBOUND = a number above 1, end of the treshold where a gene turns off.
// [3]DNA_ON_LOWERBOUND = a number above DNA_OFF_UPPERBOUND(even if just by 1), threshold where a gene turns on.
// [4]DNA_ON_UPPERBOUND = 4095, end of the threshold where a gene turns on.
var/list/bounds = G.linked_trait.activity_bounds
if(bounds[1] < 1) // lowest value a gene can be to turn off
log_unit_test("[G.name]: Genetics - DNA_OFF_LOWERBOUND, was smaller than 1.")
failed = TRUE
if(bounds[2] < bounds[1])
log_unit_test("[G.name]: Genetics - DNA_OFF_UPPERBOUND must be larger than DNA_OFF_LOWERBOUND, and never equal.")
failed = TRUE
if(bounds[2] >= bounds[3])
log_unit_test("[G.name]: Genetics - DNA_OFF_UPPERBOUND must be smaller than DNA_ON_LOWERBOUND, and never equal.")
failed = TRUE
if(bounds[3] > bounds[4])
log_unit_test("[G.name]: Genetics - DNA_ON_LOWERBOUND must be smaller than DNA_ON_UPPERBOUND, and never equal.")
failed = TRUE
if(bounds[4] > 4095) // highest value a gene can be to turn on
log_unit_test("[G.name]: Genetics - DNA_ON_UPPERBOUND, was larger than 4095.")
failed = TRUE
if(failed)
fail("Invalid activity bounds for one or more traitgenes")
else
pass("All traitgenes have activity bounds, and activity bounds are legal.")
return failed
-29
View File
@@ -1,29 +0,0 @@
/datum/unit_test/language_test_shall_have_distinct_names
name = "LANGUAGES: Entries shall have distinct names"
/datum/unit_test/language_test_shall_have_distinct_names/start_test()
if(length(GLOB.language_name_conflicts) != 0)
var/list/name_conflict_log = list()
for(var/conflicted_name in GLOB.language_name_conflicts)
name_conflict_log += "+[length(GLOB.language_name_conflicts[conflicted_name])] languages with name \"[conflicted_name]\"!"
for(var/datum/language/L in GLOB.language_name_conflicts[conflicted_name])
name_conflict_log += "+-+[L.type]"
fail("Some names are used by more than one language:\n" + name_conflict_log.Join("\n"))
else
pass("All languages have distinct names")
return 1
/datum/unit_test/language_test_shall_have_distinct_keys
name = "LANGUAGES: Entries shall have distinct keys"
/datum/unit_test/language_test_shall_have_distinct_keys/start_test()
if(length(GLOB.language_key_conflicts) != 0)
var/list/key_conflict_log = list()
for(var/conflicted_key in GLOB.language_key_conflicts)
key_conflict_log += "+[length(GLOB.language_key_conflicts[conflicted_key])] languages with key \"[conflicted_key]\"!"
for(var/datum/language/L in GLOB.language_key_conflicts[conflicted_key])
key_conflict_log += "+-+[L]([L.type])"
fail("Some keys are used by more than one language:\n" + key_conflict_log.Join("\n"))
else
pass("All languages in GLOB.all_languages have distinct keys")
return 1
-22
View File
@@ -1,22 +0,0 @@
/datum/unit_test/loadout_test_shall_have_name_cost_path
name = "LOADOUT: Entries shall have name, cost, and path definitions"
/datum/unit_test/loadout_test_shall_have_name_cost_path/start_test()
var/failed = 0
for(var/datum/gear/G as anything in subtypesof(/datum/gear))
if(!initial(G.display_name))
log_unit_test("[G]: Loadout - Missing display name.")
failed = 1
else if(isnull(initial(G.cost)))
log_unit_test("[G]: Loadout - Missing cost.")
failed = 1
else if(!initial(G.path))
log_unit_test("[G]: Loadout - Missing path definition.")
failed = 1
if(failed)
fail("One or more /datum/gear definitions had invalid display names, costs, or path definitions")
else
pass("All /datum/gear definitions had correct settings.")
return 1
-242
View File
@@ -1,242 +0,0 @@
/datum/unit_test/apc_area_test
name = "MAP: Area Test APC / Scrubbers / Vents (Defined Z-Levels)"
/datum/unit_test/apc_area_test/start_test()
var/list/bad_areas = list()
var/area_test_count = 0
var/list/exempt_areas = typesof(/area/space,
/area/syndicate_station,
/area/skipjack_station,
/area/solar,
/area/shuttle,
/area/holodeck,
/area/supply/station,
/area/mine,
/area/vacant/vacant_shop,
/area/turbolift,
/area/submap )
var/list/exempt_from_atmos = typesof(/area/maintenance,
/area/storage,
/area/engineering/atmos/storage,
/area/rnd/test_area,
/area/construction,
/area/server,
/area/mine,
/area/vacant/vacant_shop,
/area/rnd/research_storage, // This should probably be fixed,
/area/security/riot_control, // This should probably be fixed,
)
var/list/exempt_from_apc = typesof(/area/construction,
/area/medical/genetics,
/area/mine,
/area/vacant/vacant_shop
)
// Some maps have areas specific to the map, so include those.
exempt_areas += using_map.unit_test_exempt_areas.Copy()
exempt_from_atmos += using_map.unit_test_exempt_from_atmos.Copy()
exempt_from_apc += using_map.unit_test_exempt_from_apc.Copy()
var/list/zs_to_test = using_map.unit_test_z_levels || list(1) //Either you set it, or you just get z1
for(var/area/A in world)
if((A.z in zs_to_test) && !(A.type in exempt_areas))
area_test_count++
var/area_good = 1
var/bad_msg = "--------------- [A.name]([A.type])"
// Scan for areas with extra APCs
if(!(A.type in exempt_from_apc))
if(isnull(A.apc))
log_unit_test("[bad_msg] lacks an APC. (X[A.x]|Y[A.y]) - Z[A.z])")
area_good = 0
else
var/list/apc_list = list()
for(var/turf/T in get_current_area_turfs(A))
for(var/atom/S in T.contents)
if(istype(S,/obj/machinery/power/apc))
apc_list.Add(S)
if(apc_list.len > 1)
area_good = 0
for(var/obj/machinery/power/P in apc_list)
log_unit_test("[bad_msg] has too many APCs. (X[P.x]|Y[P.y]) - Z[P.z])")
if(!A.air_scrub_info.len && !(A.type in exempt_from_atmos))
log_unit_test("[bad_msg] lacks an Air scrubber. (X[A.x]|Y[A.y]) - (Z[A.z])")
area_good = 0
if(!A.air_vent_info.len && !(A.type in exempt_from_atmos))
log_unit_test("[bad_msg] lacks an Air vent. (X[A.x]|Y[A.y]) - (Z[A.z])")
area_good = 0
if(!area_good)
bad_areas.Add(A)
if(bad_areas.len)
fail("\[[bad_areas.len]/[area_test_count]\]Some areas lacked APCs, Air Scrubbers, or Air vents.")
else
pass("All \[[area_test_count]\] areas contained APCs, Air scrubbers, and Air vents.")
return 1
/datum/unit_test/wire_test
name = "MAP: Cable Test (Defined Z-Levels)"
/datum/unit_test/wire_test/start_test()
set background=1
var/wire_test_count = 0
var/bad_tests = 0
var/turf/T = null
var/obj/structure/cable/C = null
var/list/cable_turfs = list()
var/list/dirs_checked = list()
var/list/exempt_from_wires = list()
exempt_from_wires += using_map.unit_test_exempt_from_wires.Copy()
var/list/zs_to_test = using_map.unit_test_z_levels || list(1) //Either you set it, or you just get z1
for(var/color in GLOB.possible_cable_coil_colours)
cable_turfs = list()
for(C in world)
T = null
T = get_turf(C)
var/area/A = get_area(T)
if(T && (T.z in zs_to_test) && !(A.type in exempt_from_wires))
if(C.color == GLOB.possible_cable_coil_colours[color])
cable_turfs |= get_turf(C)
for(T in cable_turfs)
var/bad_msg = "--------------- [T.name] \[[T.x] / [T.y] / [T.z]\] [color]"
dirs_checked.Cut()
for(C in T)
wire_test_count++
var/combined_dir = "[C.d1]-[C.d2]"
if(combined_dir in dirs_checked)
bad_tests++
log_unit_test("[bad_msg] Contains multiple wires with same direction on top of each other.")
if(C.dir != SOUTH)
bad_tests++
log_unit_test("[bad_msg] Contains wire with dir set, wires MUST face south, use icon_states.")
dirs_checked.Add(combined_dir)
log_unit_test("[color] wires checked.")
if(bad_tests)
fail("\[[bad_tests] / [wire_test_count]\] Some turfs had overlapping wires going the same direction.")
else
pass("All \[[wire_test_count]\] wires had no overlapping cables going the same direction.")
return 1
/datum/unit_test/template_noops
name = "MAP: Template no-ops (all maps)"
/datum/unit_test/template_noops/start_test()
var/list/log = list()
var/turf_noop_count = 0
for(var/turf/template_noop/T in world)
turf_noop_count++
log += "+-- Template Turf @ [T.x], [T.y], [T.z] ([T.loc])"
var/area_noop_count = 0
for(var/area/template_noop/A in world)
area_noop_count++
log += "+-- Template Area"
if(turf_noop_count || area_noop_count)
fail("Map contained [turf_noop_count] template turfs and [area_noop_count] template areas at round-start.\n" + log.Join("\n"))
else
pass("No template turfs or areas.")
return 1
/datum/unit_test/active_edges
name = "MAP: Active edges (all maps)"
/datum/unit_test/active_edges/start_test()
var/active_edges = SSair.active_edges.len
var/list/edge_log = list()
if(active_edges)
for(var/connection_edge/E in SSair.active_edges)
var/a_temp = E.A.air.temperature
var/a_moles = E.A.air.total_moles
var/a_vol = E.A.air.volume
var/a_gas = ""
for(var/gas in E.A.air.gas)
a_gas += "[gas]=[E.A.air.gas[gas]]"
var/b_temp
var/b_moles
var/b_vol
var/b_gas = ""
// Two zones mixing
if(istype(E, /connection_edge/zone))
var/connection_edge/zone/Z = E
b_temp = Z.B.air.temperature
b_moles = Z.B.air.total_moles
b_vol = Z.B.air.volume
for(var/gas in Z.B.air.gas)
b_gas += "[gas]=[Z.B.air.gas[gas]]"
// Zone and unsimulated turfs mixing
if(istype(E, /connection_edge/unsimulated))
var/connection_edge/unsimulated/U = E
b_temp = U.B.temperature
b_moles = "Unsim"
b_vol = "Unsim"
for(var/gas in U.air.gas)
b_gas += "[gas]=[U.air.gas[gas]]"
edge_log += "Active Edge [E] ([E.type])"
edge_log += "Edge side A: T:[a_temp], Mol:[a_moles], Vol:[a_vol], Gas:[a_gas]"
edge_log += "Edge side B: T:[b_temp], Mol:[b_moles], Vol:[b_vol], Gas:[b_gas]"
for(var/turf/T in E.connecting_turfs)
edge_log += "+--- Connecting Turf [T] ([T.type]) @ [T.x], [T.y], [T.z] ([T.loc])"
if(active_edges)
fail("Maps contained [active_edges] active edges at round-start.\n" + edge_log.Join("\n"))
else
pass("No active edges.")
return 1
/datum/unit_test/ladder_test
name = "MAP: Ladder Test"
/datum/unit_test/ladder_test/start_test()
var/failed = FALSE
for(var/obj/structure/ladder/L in world)
var/turf/T = get_turf(L)
if(!T)
log_unit_test("[L.x].[L.y].[L.z]: Map - Ladder on invalid turf")
failed = TRUE
continue
if(L.allowed_directions & UP)
if(!L.target_up)
log_unit_test("[T.x].[T.y].[T.z]: Map - Ladder allows upward movement, but had no ladder above it")
failed = TRUE
if(L.allowed_directions & DOWN)
if(!L.target_down)
log_unit_test("[T.x].[T.y].[T.z]: Map - Ladder allows downward movement, but had no ladder beneath it")
failed = TRUE
if(T.density)
log_unit_test("[L.x].[L.y].[L.z]: Map - Ladder is inside a wall")
failed = TRUE
if(failed)
fail("Ladders were incorrectly placed, or missing connections.")
else
pass("All ladders were correctly placed and had connections.")
return failed
-19
View File
@@ -1,19 +0,0 @@
/datum/unit_test/materials_shall_have_names
name = "MATERIALS: Materials Shall Have All Names"
/datum/unit_test/materials_shall_have_names/start_test()
var/list/failures = list()
populate_material_list()
for(var/name in global.name_to_material)
var/datum/material/mat = global.name_to_material[name]
if(!mat)
continue // how did we get here?
if(!mat.display_name || !mat.use_name || !mat.sheet_singular_name || !mat.sheet_plural_name || !mat.sheet_collective_name)
failures[name] = mat.type
if(length(failures))
fail("[length(failures)] material\s had missing name strings: [english_list(failures)].")
else
pass("All materials had all their name strings.")
return TRUE
-24
View File
@@ -1,24 +0,0 @@
/datum/unit_test/posters_shall_have_legal_states
name = "POSTERS: All poster decls shall have valid icon and icon overrides"
/datum/unit_test/posters_shall_have_legal_states/start_test()
var/failed = 0
var/list/all_posters = decls_repository.get_decls_of_type(/decl/poster)
all_posters -= decls_repository.get_decl(/decl/poster/lewd) // Dumb exclusion for now. This really needs to become a valid poster instead of an illegaly made base type
for(var/path in all_posters)
var/decl/poster/D = all_posters[path]
var/obj/structure/sign/poster/P = /obj/structure/sign/poster // The base poster shows ALL subtypes except /lewd, so all posters should function here regardless!
var/icon/I = initial(P.icon)
if(D.icon_override)
I = D.icon_override
if(!(D.icon_state in cached_icon_states(I)))
failed += 1
log_unit_test("[D.type]: Poster - missing icon_state \"[D.icon_state]\" in \"[I]\", as [D.icon_override ? "override" : "base"] dmi.")
if(failed)
fail("[failed] posters had missing icon_states or bad icon overrides.")
else
pass("All [all_posters.len] posters have their icon_states and overrides set correctly.")
return TRUE
-378
View File
@@ -1,378 +0,0 @@
/datum/unit_test/reagent_shall_have_unique_name_and_id
name = "REAGENTS: Reagent IDs and names shall be unique"
/datum/unit_test/reagent_shall_have_unique_name_and_id/start_test()
var/failed = FALSE
var/collection_name = list()
var/collection_id = list()
for(var/Rpath in subtypesof(/datum/reagent))
var/datum/reagent/R = new Rpath()
if(R.name == REAGENT_DEVELOPER_WARNING) // Ignore these types as they are meant to be overridden
continue
if(R.name == "")
log_unit_test("[Rpath]: Reagents - reagent name blank.")
failed = TRUE
if(R.id == REAGENT_ID_DEVELOPER_WARNING)
log_unit_test("[Rpath]: Reagents - reagent ID not set.")
failed = TRUE
if(R.id == "")
log_unit_test("[Rpath]: Reagents - reagent ID blank.")
failed = TRUE
if(R.id != lowertext(R.id))
log_unit_test("[Rpath]: Reagents - Reagent ID must be all lowercase.")
failed = TRUE
if(!(R.wiki_flag & WIKI_SPOILER)) // If wiki hidden then don't conflict test it against name, used for intentionally copied names like beer2's
if(collection_name[R.name])
log_unit_test("[Rpath]: Reagents - reagent name \"[R.name]\" is not unique, used first in [collection_name[R.name]].")
failed = TRUE
collection_name[R.name] = R.type
if(collection_id[R.id])
log_unit_test("[Rpath]: Reagents - reagent ID \"[R.id]\" is not unique, used first in [collection_id[R.id]].")
failed = TRUE
collection_id[R.id] = R.type
if(!R.supply_conversion_value)
log_unit_test("[Rpath]: Reagents - reagent ID \"[R.id]\" does not have supply_conversion_value set.")
failed = TRUE
if(!R.industrial_use || !R.industrial_use == "")
log_unit_test("[Rpath]: Reagents - reagent ID \"[R.id]\" does not have industrial_use set.")
failed = TRUE
if(R.description == REAGENT_DESC_DEVELOPER_WARNING)
log_unit_test("[Rpath]: Reagents - reagent description unset.")
failed = TRUE
qdel(R)
if(failed)
fail("One or more /datum/reagent subtypes had invalid definitions.")
else
pass("All /datum/reagent subtypes had correct settings.")
return TRUE
/datum/unit_test/chemical_reactions_shall_use_and_produce_valid_reagents
name = "REAGENTS: Chemical Reactions shall use and produce valid reagents"
/datum/unit_test/chemical_reactions_shall_use_and_produce_valid_reagents/start_test()
var/failed = FALSE
var/list/collection_id = list()
var/list/all_reactions = decls_repository.get_decls_of_subtype(/decl/chemical_reaction)
for(var/rtype in all_reactions)
var/decl/chemical_reaction/CR = all_reactions[rtype]
if(CR.name == REAGENT_DEVELOPER_WARNING) // Ignore these types as they are meant to be overridden
continue
if(!CR.name)
log_unit_test("[CR.type]: Reagents - chemical reaction had null name.")
failed = TRUE
if(CR.name == "")
log_unit_test("[CR.type]: Reagents - chemical reaction had blank name.")
failed = TRUE
if(!CR.id)
log_unit_test("[CR.type]: Reagents - chemical reaction had invalid ID.")
failed = TRUE
if(CR.id != lowertext(CR.id))
log_unit_test("[CR.type]: Reagents - chemical reaction ID must be all lowercase.")
failed = TRUE
if(CR.id in collection_id)
log_unit_test("[CR.type]: Reagents - chemical reaction ID \"[CR.name]\" is not unique, used first in [collection_id[CR.id]].")
failed = TRUE
else
collection_id[CR.id] = CR.type
if(CR.result_amount < 0)
log_unit_test("[CR.type]: Reagents - chemical reaction ID \"[CR.name]\" had less than 0 as as result_amount?")
failed = TRUE
if(CR.required_reagents && CR.required_reagents.len)
for(var/RR in CR.required_reagents)
if(!SSchemistry.chemical_reagents[RR])
log_unit_test("[CR.type]: Reagents - chemical reaction had invalid required reagent ID \"[RR]\".")
failed = TRUE
if(CR.required_reagents[RR] <= 0)
log_unit_test("[CR.type]: Reagents - chemical reaction had invalid required reagent amount or in invalid format \"[CR.required_reagents[RR]]\".")
failed = TRUE
if(CR.catalysts && CR.catalysts.len)
for(var/RR in CR.catalysts)
if(!SSchemistry.chemical_reagents[RR])
log_unit_test("[CR.type]: Reagents - chemical reaction had invalid required reagent ID \"[RR]\".")
failed = TRUE
if(CR.catalysts[RR] <= 0)
log_unit_test("[CR.type]: Reagents - chemical reaction had invalid catalysts amount or in invalid format \"[CR.catalysts[RR]]\".")
failed = TRUE
if(CR.inhibitors && CR.inhibitors.len)
for(var/RR in CR.inhibitors)
if(!SSchemistry.chemical_reagents[RR])
log_unit_test("[CR.type]: Reagents - chemical reaction had invalid required reagent ID \"[RR]\".")
failed = TRUE
if(CR.inhibitors[RR] <= 0)
log_unit_test("[CR.type]: Reagents - chemical reaction had invalid inhibitors amount or in invalid format \"[CR.inhibitors[RR]]\".")
failed = TRUE
if(CR.result)
if(!SSchemistry.chemical_reagents[CR.result])
log_unit_test("[CR.type]: Reagents - chemical reaction had invalid result reagent ID \"[CR.result]\".")
failed = TRUE
if(failed)
fail("One or more /decl/chemical_reaction subtypes had invalid results or components.")
else
pass("All /decl/chemical_reaction subtypes had correct settings.")
return TRUE
/datum/unit_test/prefilled_reagent_containers_shall_have_valid_reagents
name = "REAGENTS: Prefilled reagent containers shall have valid reagents"
/datum/unit_test/prefilled_reagent_containers_shall_have_valid_reagents/start_test()
var/failed = FALSE
var/obj/container = new /obj
for(var/RC in subtypesof(/obj/item/reagent_containers/glass))
var/obj/item/reagent_containers/glass/R = new RC(container)
if(R.prefill && R.prefill.len)
for(var/ID in R.prefill)
if(!SSchemistry.chemical_reagents[ID])
log_unit_test("[RC]: Reagents - reagent prefill had invalid reagent ID \"[ID]\".")
failed = TRUE
qdel(R)
for(var/DC in subtypesof(/obj/item/reagent_containers/chem_disp_cartridge))
var/obj/item/reagent_containers/chem_disp_cartridge/D = new DC(container)
if(D.spawn_reagent)
if(!SSchemistry.chemical_reagents[D.spawn_reagent])
log_unit_test("[DC]: Reagents - chemical dispenser cartridge had invalid reagent ID \"[D.spawn_reagent]\".")
failed = TRUE
qdel(D)
qdel(container)
if(failed)
fail("One or more /obj/item/reagent_containers had an invalid prefill reagent.")
else
pass("All /obj/item/reagent_containers had valid prefill reagents.")
return TRUE
/datum/unit_test/chemical_reactions_shall_not_conflict
name = "REAGENTS: Chemical Reactions shall not conflict"
var/obj/fake_beaker = null
var/list/result_reactions = list()
/datum/unit_test/chemical_reactions_shall_not_conflict/start_test()
var/failed = FALSE
#ifdef UNIT_TEST
var/list/all_reactions = decls_repository.get_decls_of_subtype(/decl/chemical_reaction)
for(var/rtype in all_reactions)
var/decl/chemical_reaction/CR = all_reactions[rtype]
if(CR.name == REAGENT_DEVELOPER_WARNING) // Ignore these types as they are meant to be overridden
continue
if(!CR.name || CR.name == "" || !CR.id || CR.id == "")
continue
if(CR.result_amount <= 0) //Makes nothing anyway, or maybe an effect/explosion!
continue
if(!CR.result) // Cannot check for this
continue
if(istype(CR, /decl/chemical_reaction/instant/slime))
// slime time
var/decl/chemical_reaction/instant/slime/SR = CR
if(!SR.required)
continue
var/obj/item/slime_extract/E = new SR.required()
qdel_swap(fake_beaker, E)
fake_beaker.reagents.maximum_volume = 5000
else if(istype(CR, /decl/chemical_reaction/distilling))
// distilling
var/obj/distilling_tester/D = new()
qdel_swap(fake_beaker, D)
fake_beaker.reagents.maximum_volume = 5000
else
// regular beaker
qdel_swap(fake_beaker, new /obj/item/reagent_containers/glass/beaker())
fake_beaker.reagents.maximum_volume = 5000
// Perform test! If it fails once, it will perform a deeper check trying to use the inhibitors of anything in the beaker
RegisterSignal(fake_beaker.reagents, COMSIG_UNITTEST_DATA, PROC_REF(get_signal_data))
if(perform_reaction(CR))
// Check if we failed the test with inhibitors in use, if so we absolutely couldn't make it...
// Uncomment the UNIT_TEST section in code\modules\reagents\reactions\_reactions.dm if you require more info
log_unit_test("[CR.type]: Reagents - chemical reaction did not produce \"[CR.result]\". CONTAINS: \"[fake_beaker.reagents.get_reagents()]\"")
failed = TRUE
UnregisterSignal(fake_beaker.reagents, COMSIG_UNITTEST_DATA)
qdel_null(fake_beaker)
#endif
if(failed)
fail("One or more /decl/chemical_reaction subtypes conflict with another reaction.")
else
pass("All /decl/chemical_reaction subtypes had no conflicts.")
return TRUE
/datum/unit_test/chemical_reactions_shall_not_conflict/proc/perform_reaction(var/decl/chemical_reaction/CR, var/list/inhib = list())
var/scale = 1
if(CR.result_amount < 1)
scale = 1 / CR.result_amount // Create at least 1 unit
// Weird loop here, but this is used to test both instant and distilling reactions
// Instants will meet the while() condition on the first loop and go to the next stuff
// but distilling will repeat over and over until the temperature test is finished!
var/temp_test = 0
do
// clear for inhibitor searches
fake_beaker.reagents.clear_reagents()
result_reactions.Cut()
if(inhib.len) // taken from argument and not reaction! Put in FIRST!
for(var/RR in inhib)
fake_beaker.reagents.add_reagent(RR, inhib[RR] * scale)
if(CR.catalysts) // Required for reaction
for(var/RR in CR.catalysts)
fake_beaker.reagents.add_reagent(RR, CR.catalysts[RR] * scale)
if(CR.required_reagents)
for(var/RR in CR.required_reagents)
fake_beaker.reagents.add_reagent(RR, CR.required_reagents[RR] * scale)
if(!istype(CR, /decl/chemical_reaction/distilling))
break // Skip the next section if we're not distilling
// Check distillation at 10 points along its temperature range!
// This is so multiple reactions with the same requirements, but different temps, can be tested.
temp_test += 0.1
var/obj/distilling_tester/DD = fake_beaker
DD.test_distilling(CR,temp_test)
if(fake_beaker.reagents.has_reagent(CR.result))
return FALSE // Distilling success
while(temp_test > 1)
// Check beaker to see if we reached our goal!
if(fake_beaker.reagents.has_reagent(CR.result))
return FALSE // INSTANT SUCCESS!
if(inhib.len)
// We've checked with inhibitors, so we're already in inhibitor checking phase.
// So we've absolutely failed this time. There is no way to make this...
return TRUE
if(!result_reactions.len)
// Nothing to check for inhibitors...
for(var/decl/chemical_reaction/test_react in result_reactions)
log_unit_test("[CR.type]: Reagents - Used [test_react] but failed.")
return TRUE
// Otherwise we check the resulting reagents and use their inhibitor this time!
for(var/decl/chemical_reaction/test_react in result_reactions)
if(!test_react)
continue
if(!test_react.inhibitors.len)
continue
// Test one by one
for(var/each in test_react.inhibitors)
if(!perform_reaction(CR, list("[each]" = test_react.inhibitors["[each]"])))
return FALSE // SUCCESS using an inhibitor!
// Test all at once
if(!perform_reaction(CR, test_react.inhibitors))
return FALSE // SUCCESS using all inhibitors!
// No inhibiting reagent worked...
for(var/decl/chemical_reaction/test_react in result_reactions)
log_unit_test("[CR.type]: Reagents - Used [test_react] but failed.")
return TRUE
/datum/unit_test/chemical_reactions_shall_not_conflict/get_signal_data(atom/source, list/data = list())
result_reactions.Add(data[1]) // Append the reactions that happened, then use that to check their inhibitors
/datum/unit_test/chemical_grinding_must_produce_valid_results
name = "REAGENTS: Chemical Grinding Must Have Valid Results"
/datum/unit_test/chemical_grinding_must_produce_valid_results/start_test()
var/failed = FALSE
for(var/grind in GLOB.sheet_reagents + GLOB.ore_reagents)
var/list/results = GLOB.sheet_reagents[grind]
if(!results)
results = GLOB.ore_reagents[grind]
if(!results || !islist(results))
log_unit_test("[grind]: Reagents - Grinding result had invalid list.")
failed = TRUE
continue
if(!results.len)
log_unit_test("[grind]: Reagents - Grinding result had empty.")
failed = TRUE
continue
for(var/reg_id in results)
if(!SSchemistry.chemical_reagents[reg_id])
log_unit_test("[grind]: Reagents - Grinding result had invalid reagent id \"[reg_id]\".")
failed = TRUE
if(failed)
fail("One or more grindable sheet or ore entries had invalid reagents or lists.")
else
pass("All grindable sheet or ore entries had valid lists and reagents.")
return TRUE
// Used to test distillations without hacking the other machinery's code up
/obj/distilling_tester
icon = 'icons/obj/weapons.dmi'
icon_state = "cartridge"
var/datum/gas_mixture/GM = new()
var/current_temp = 0
/obj/distilling_tester/Initialize(mapload)
create_reagents(5000,/datum/reagents/distilling)
. = ..()
/obj/distilling_tester/return_air()
return GM
/obj/distilling_tester/proc/test_distilling(var/decl/chemical_reaction/distilling/D, var/temp_prog)
qdel_swap(GM,new())
if(D.require_xgm_gas)
GM.gas[D.require_xgm_gas] = 100
else
if(D.rejects_xgm_gas == GAS_N2)
GM.gas[GAS_O2] = 100
else
GM.gas[GAS_N2] = 100
if(D.minimum_xgm_pressure)
GM.temperature = (D.minimum_xgm_pressure * CELL_VOLUME) / (GM.gas[D.require_xgm_gas] * R_IDEAL_GAS_EQUATION)
// Try this 10 times, We need to know if something is blocking at multiple temps.
// If it passes unit test, it might still be awful to make though, gotta find the right gas mix!
current_temp = LERP( D.temp_range[1], D.temp_range[2], temp_prog)
reagents.handle_reactions()
/obj/distilling_tester/Destroy(force, ...)
qdel_null(GM)
. = ..()
-29
View File
@@ -1,29 +0,0 @@
/datum/unit_test/recipe_test_shall_have_valid_result_and_quantity
name = "RECIPES: Recipes shall have valid result and result_quantity definitions"
/datum/unit_test/recipe_test_shall_have_valid_result_and_quantity/start_test()
var/failed = FALSE
for(var/datum/recipe/R in subtypesof(/datum/recipe))
var/our_result = initial(R.result)
var/our_amount = initial(R.result_quantity)
if(!our_result)
log_unit_test("[R]: Recipes - Missing result.")
failed = TRUE
else if(!ispath(our_result, /atom/movable))
log_unit_test("[R]: Recipes - Improper result; [our_result] is not an obj or mob.")
failed = TRUE
if(isnull(our_amount))
log_unit_test("[R]: Recipes - result_quantity must be set.")
failed = TRUE
if(our_amount <= 0)
log_unit_test("[R]: Recipes - result_quantity must be greater than zero.")
failed = TRUE
else if(!ISINTEGER(our_amount))
log_unit_test("[R]: Recipes - result_quantity must be an integer.")
failed = TRUE
if(failed)
fail("One or more /datum/recipe subtypes had invalid results or result_quantity definitions.")
else
pass("All /datum/recipe subtypes had correct settings.")
return TRUE
-30
View File
@@ -1,30 +0,0 @@
/datum/unit_test/recycler_vendor_entry_invalid
name = "RECYCLER: Recycler Vendor Entry shall have valid definitions"
/datum/unit_test/recycler_vendor_entry_invalid/start_test()
var/failed = FALSE
for(var/datum/maint_recycler_vendor_entry/R in subtypesof(/datum/maint_recycler_vendor_entry))
var/item_to_spawn = initial(R.object_type_to_spawn)
var/item_cost = initial(R.item_cost)
var/global_item_cap = initial(R.per_round_cap)
var/individual_item_cap = initial(R.per_person_cap)
var/is_scam = initial(R.is_scam)
if(!item_to_spawn && !is_scam)
log_unit_test("[R] : Vendor Entry - Missing Object Type on non-scam entry")
failed = TRUE
if(item_cost < 0)
log_unit_test("[R] : Vendor Entry - Negative Cost")
failed = TRUE
if((item_cost == 0) && ((global_item_cap < 0)&&(individual_item_cap < 0))) //nobody SHOULD set the cost to 0 but it's possible i guess. maybe for future scams
log_unit_test("[R] : Vendor Entry - Infinite Item Spawning due to no individual or global item cap")
failed = TRUE
if(failed)
fail("One or more /datum/maint_recycler_vendor_entry had invalid results or definitions.")
else
pass("All /datum/maint_recycler_vendor_entry subtypes had correct settings.")
return TRUE
-224
View File
@@ -1,224 +0,0 @@
/datum/unit_test/all_robot_sprites_must_be_valid
name = "ROBOTS: Robot sprites must be valid"
var/signal_failed = FALSE
/datum/unit_test/all_robot_sprites_must_be_valid/start_test()
var/failed = 0
for(var/sprite in subtypesof(/datum/robot_sprite))
var/datum/robot_sprite/RS = new sprite()
if(!RS.name) // Parent type, ignore me
continue
if(!RS.sprite_icon)
log_unit_test("[RS.type]: Robots - Robot sprite \"[RS.name]\", missing sprite_icon.")
failed = TRUE
continue
var/list/checks = list(
"[ROBOT_HAS_SPEED_SPRITE]" = "-roll",
"[ROBOT_HAS_SHIELD_SPRITE]" = "-shield",
"[ROBOT_HAS_SHIELD_SPEED_SPRITE]" = "-speed_shield",
"[ROBOT_HAS_MELEE_SPRITE]" = "-melee",
"[ROBOT_HAS_DAGGER_SPRITE]" = "-dagger",
"[ROBOT_HAS_BLADE_SPRITE]" = "-blade",
"[ROBOT_HAS_GUN_SPRITE]" = "-gun",
"[ROBOT_HAS_LASER_SPRITE]" = "-laser",
"[ROBOT_HAS_TASER_SPRITE]" = "-taser",
"[ROBOT_HAS_DISABLER_SPRITE]" = "-disabler"
)
for(var/C in checks)
if(RS.sprite_flag_check(text2num(C)))
if(check_state(RS,checks[C]))
failed = TRUE
// eyes, lights, markings
if(RS.has_eye_sprites)
if(check_state(RS,"-eyes"))
failed = TRUE
if(RS.has_eye_light_sprites)
if(check_state(RS,"-lights"))
failed = TRUE
if(LAZYLEN(RS.sprite_decals))
for(var/decal in RS.sprite_decals)
if(check_state(RS,"-[decal]"))
failed = TRUE
if(LAZYLEN(RS.sprite_animations))
for(var/animation in RS.sprite_animations)
if(check_state(RS,"-[animation]"))
failed = TRUE
// Control panel
if(RS.has_custom_open_sprites)
if(check_state(RS,"-openpanel_nc"))
failed = TRUE
if(check_state(RS,"-openpanel_c"))
failed = TRUE
if(check_state(RS,"-openpanel_w"))
failed = TRUE
// Glow State
if(RS.has_glow_sprites)
if(check_state(RS,"-glow"))
failed = TRUE
// Bellies
if(RS.has_vore_belly_sprites && !RS.belly_capacity_list)
if(RS.has_sleeper_light_indicator)
// belly r/g light
if(check_state(RS,"-sleeper-r"))
failed = TRUE
if(check_state(RS,"-sleeper-g"))
failed = TRUE
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
if(check_state(RS,"-sleeper-r-[rest_style]"))
failed = TRUE
if(check_state(RS,"-sleeper-g-[rest_style]"))
failed = TRUE
// struggling
if(RS.has_vore_struggle_sprite)
if(check_state(RS,"-sleeper-r-struggle"))
failed = TRUE
if(check_state(RS,"-sleeper-g-struggle"))
failed = TRUE
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
if(check_state(RS,"-sleeper-r-[rest_style]-struggle"))
failed = TRUE
if(check_state(RS,"-sleeper-g-[rest_style]-struggle"))
failed = TRUE
else
// belly
if(check_state(RS,"-sleeper"))
failed = TRUE
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
if(check_state(RS,"-sleeper-[rest_style]"))
failed = TRUE
// struggling
if(RS.has_vore_struggle_sprite)
if(check_state(RS,"-sleeper-struggle"))
failed = TRUE
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
if(check_state(RS,"-sleeper-[rest_style]-struggle"))
failed = TRUE
else if (RS.belly_capacity_list)
for(var/belly in RS.belly_capacity_list)
for(var/num = 1 to RS.belly_capacity_list[belly])
// big belly
if(check_state(RS,"-[belly]-[num]"))
failed = TRUE
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
if(check_state(RS,"-[belly]-[num]-[rest_style]"))
failed = TRUE
// struggling
if(RS.has_vore_struggle_sprite)
if(check_state(RS,"-[belly]-[num]-struggle"))
failed = TRUE
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
if(check_state(RS,"-[belly]-[num]-[rest_style]-struggle"))
failed = TRUE
if(RS.belly_light_list)
for(var/belly in RS.belly_light_list)
for(var/num = 1 to RS.belly_light_list[belly])
// multi belly r/g light
if(check_state(RS,"-[belly]-[num]-r"))
failed = TRUE
if(check_state(RS,"-[belly]-[num]-g"))
failed = TRUE
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
if(check_state(RS,"-[belly]-[num]-r-[rest_style]"))
failed = TRUE
if(check_state(RS,"-[belly]-[num]-g-[rest_style]"))
failed = TRUE
// struggling
if(RS.has_vore_struggle_sprite)
if(check_state(RS,"-[belly]-[num]-r-struggle"))
failed = TRUE
if(check_state(RS,"-[belly]-[num]-g-struggle"))
failed = TRUE
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
if(check_state(RS,"-[belly]-[num]-r-[rest_style]-struggle"))
failed = TRUE
if(check_state(RS,"-[belly]-[num]-g-[rest_style]-struggle"))
failed = TRUE
// reseting
for(var/rest_style in RS.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
if(check_state(RS,"-[rest_style]"))
failed = TRUE
if(RS.has_glow_sprites)
if(check_state(RS,"-[rest_style]-glow"))
failed = TRUE
if(RS.has_rest_lights_sprites)
if(check_state(RS,"-[rest_style]-lights"))
failed = TRUE
if(RS.has_rest_eyes_sprites)
if(check_state(RS,"-[rest_style]-eyes"))
failed = TRUE
// death
if(RS.has_dead_sprite)
if(check_state(RS,"-wreck"))
failed = TRUE
if(RS.has_dead_sprite_overlay) // Only one per dmi
if(!("wreck-overlay" in cached_icon_states(RS.sprite_icon)))
log_unit_test("[RS.type]: Robots - Robot sprite \"[RS.name]\", missing icon_state wreck-overlay, in dmi \"[RS.sprite_icon]\".")
failed = TRUE
// offset
var/icon/I = new(RS.sprite_icon)
if(RS.icon_x != I.Width())
log_unit_test("[RS.type]: Robots - Robot sprite \"[RS.name]\", icon_x \"[RS.icon_x]\" did not match dmi configured width \"[I.Width()]\"")
failed = TRUE
if(RS.icon_y != I.Height())
log_unit_test("[RS.type]: Robots - Robot sprite \"[RS.name]\", icon_y \"[RS.icon_y]\" did not match dmi configured height \"[I.Height()]\"")
failed = TRUE
if(RS.icon_y != RS.vis_height)
log_unit_test("[RS.type]: Robots - Robot sprite \"[RS.name]\", vis_height \"[RS.vis_height]\" did not match icon_y \"[RS.icon_y]\"")
failed = TRUE
var/legal_offset = (I.Width() - world.icon_size) / 2
if(RS.pixel_x != -legal_offset)
log_unit_test("[RS.type]: Robots - Robot sprite \"[RS.name]\", pixel_x \"[RS.pixel_x]\" did not have correct offset, should be \"[-legal_offset]\"")
failed = TRUE
qdel(I)
qdel(RS)
if(failed)
fail("One or more /datum/robot_sprite items had invalid flags or icons")
else
pass("All /datum/robot_sprite are valid.")
return 1
/datum/unit_test/all_robot_sprites_must_be_valid/proc/check_state(var/datum/robot_sprite/RS,var/append)
var/check_state = "[RS.sprite_icon_state][append]"
if(!(check_state in cached_icon_states(RS.sprite_icon)))
log_unit_test("[RS.type]: Robots - Robot sprite \"[RS.name]\", enabled but missing icon_state \"[check_state]\", in dmi \"[RS.sprite_icon]\".")
return TRUE
return FALSE
-69
View File
@@ -1,69 +0,0 @@
/datum/unit_test/sqlite
name = "SQLite template" // Template has to be in the name or this test will be ran, and fail.
var/database/stub_sqlite_db = null
/datum/unit_test/sqlite/proc/setup_stub_db()
fdel("data/sqlite/testing_[name].db") // In case any remain from a previous local test, so we can have a clean new database.
stub_sqlite_db = new("data/sqlite/testing_[name].db") // Unfortunately, byond doesn't like having working sqlite stuff w/o a file existing.
SSsqlite.init_schema(stub_sqlite_db)
// Feedback table tests.
/datum/unit_test/sqlite/feedback/insert
name = "SQLITE FEEDBACK: Insert and Retrieve Data"
/datum/unit_test/sqlite/feedback/insert/start_test()
// Arrange.
setup_stub_db()
var/test_author = "alice"
var/test_topic = "Test"
var/test_content = "Bob is lame."
// Act.
SSsqlite.insert_feedback(author = test_author, topic = test_topic, content = test_content, sqlite_object = stub_sqlite_db)
var/database/query/Q = new("SELECT * FROM [SQLITE_TABLE_FEEDBACK]")
Q.Execute(stub_sqlite_db)
SSsqlite.sqlite_check_for_errors(Q, "Sqlite Insert Unit Test")
Q.NextRow()
// Assert.
var/list/row_data = Q.GetRowData()
if(row_data[SQLITE_FEEDBACK_COLUMN_AUTHOR] == test_author && row_data[SQLITE_FEEDBACK_COLUMN_TOPIC] == test_topic && row_data[SQLITE_FEEDBACK_COLUMN_CONTENT] == test_content)
pass("No issues found.")
else
fail("Data insert and loading failed to have matching information.")
return TRUE
/datum/unit_test/sqlite/feedback/cooldown
name = "SQLITE FEEDBACK: Cooldown"
/datum/unit_test/sqlite/feedback/cooldown/start_test()
// Arrange.
setup_stub_db()
var/days_to_wait = 1
var/issues = 0
// Act.
SSsqlite.insert_feedback(author = "Alice", topic = "Testing", content = "This is a test.", sqlite_object = stub_sqlite_db)
var/alice_cooldown_block = SSsqlite.get_feedback_cooldown("Alice", days_to_wait, stub_sqlite_db)
var/bob_cooldown = SSsqlite.get_feedback_cooldown("Bob", days_to_wait, stub_sqlite_db)
days_to_wait = 0
var/alice_cooldown_allow = SSsqlite.get_feedback_cooldown("Alice", days_to_wait, stub_sqlite_db)
// Assert.
if(alice_cooldown_block <= 0)
issues++
log_unit_test("User 'Alice' did not receive a cooldown, when they were supposed to.")
if(bob_cooldown > 0)
issues++
log_unit_test("User 'Bob' did receive a cooldown, when they did not do anything.")
if(alice_cooldown_allow > 0)
issues++
log_unit_test("User 'Alice' did receive a cooldown, when no cooldown is supposed to be enforced.")
if(issues)
fail("[issues] issues were found.")
else
pass("No issues found.")
return TRUE
-46
View File
@@ -1,46 +0,0 @@
/datum/unit_test/subsystem_shall_be_initialized
name = "SUBSYSTEM - INIT: Subsystems shall be initalized"
/datum/unit_test/subsystem_shall_be_initialized/start_test()
var/list/bad_subsystems = list()
for(var/datum/controller/subsystem/SS in Master.subsystems)
if (SS.flags & SS_NO_INIT)
continue
if(!SS.subsystem_initialized)
bad_subsystems += SS.type
if(bad_subsystems.len)
fail("Found improperly initialized subsystems: [english_list(bad_subsystems)]")
else
pass("All susbsystems have initialized properly")
return 1
/* Uncomment when all atoms init properly
/datum/unit_test/subsystem_atom_shall_have_no_bad_init_calls
name = "SUBSYSTEM - ATOMS: Shall have no bad init calls"
/datum/unit_test/subsystem_atom_shall_have_no_bad_init_calls/start_test()
if(SSatoms.BadInitializeCalls.len)
log_bad(jointext(SSatoms.InitLog(), null))
fail("[SSatoms] had bad initialization calls.")
else
pass("[SSatoms] had no bad initialization calls.")
return 1
/datum/unit_test/all_atoms_shall_be_initialized
name = "SUBSYSTEM - ATOMS: All atoms shall be initialized."
/datum/unit_test/all_atoms_shall_be_initialized/start_test()
set background = TRUE // avoid infinite loop warning; SS will still wait for us.
var/fail = FALSE
for(var/atom/atom in world)
if(!(atom.initialized))
log_bad("Uninitialized atom: [atom.log_info_line()]")
fail = TRUE
if(fail)
fail("There were uninitialized atoms.")
else
pass("All atoms were initialized")
return 1
*/
-58
View File
@@ -1,58 +0,0 @@
/datum/unit_test/all_traits_unique_names
name = "TRAITS: All traits shall have unique names"
/datum/unit_test/all_traits_unique_names/start_test()
var/failed = FALSE
var/list/used_named = list()
for(var/traitpath in GLOB.all_traits)
var/datum/trait/T = GLOB.all_traits[traitpath]
if(T.name in used_named)
log_unit_test("[T.type]: Trait - The name \"[T.name]\" is already in use.")
failed = TRUE
else
used_named.Add(T.name)
if(failed)
fail("One or more traits shared a name.")
else
pass("All [GLOB.all_traits.len] have unique names.")
return failed
/datum/unit_test/autohiss_shall_be_exclusive
name = "TRAITS: Autohiss traits shall be exclusive"
/datum/unit_test/autohiss_shall_be_exclusive/start_test()
var/failed = FALSE
var/list/hiss_list = list()
for(var/traitpath in GLOB.all_traits)
var/datum/trait/T = GLOB.all_traits[traitpath]
if(!T.var_changes)
continue
if(!islist(T.var_changes["autohiss_basic_map"]))
continue
hiss_list += T
for(var/datum/trait/T in hiss_list)
if(T.type in T.excludes)
log_unit_test("[T.type]: Trait - Autohiss excludes itself.")
failed = TRUE
if(!T.excludes)
log_unit_test("[T.type]: Trait - Autohiss missing exclusion list.")
failed = TRUE
continue
var/list/exempt_list = hiss_list.Copy() - T // MUST exclude all others except itself
for(var/datum/trait/EX in exempt_list)
if(!(EX.type in T.excludes))
log_unit_test("[T.type]: Trait - Autohiss missing exclusion for [EX].")
failed = TRUE
if(failed)
fail("One or more autohiss traits allow another autohiss to be chosen with it.")
else
pass("All [hiss_list.len] autohiss traits are properly exclusive.")
return failed
-114
View File
@@ -1,114 +0,0 @@
var/all_unit_tests_passed = 1
var/failed_unit_tests = 0
var/total_unit_tests = 0
/datum/unit_test
var/name = "template"
var/disabled = ""
var/async = 0
var/reported = 0
/datum/unit_test/proc/fail(var/message)
all_unit_tests_passed = 0
failed_unit_tests++
reported = 1
log_unit_test("[ASCII_RED]!! FAILURE !! \[[name]\]: [message][ASCII_RESET]")
/datum/unit_test/proc/pass(var/message)
reported = 1
log_unit_test("[ASCII_GREEN]** SUCCESS ** \[[name]\]: [message][ASCII_RESET]")
/datum/unit_test/proc/start_test()
fail("No test proc.")
/datum/unit_test/proc/check_result()
fail("No check results proc.")
return 1
/proc/initialize_unit_tests()
log_unit_test("Initializing Unit Testing")
if(!ticker)
stack_trace("No Ticker")
world.Del()
var/said_msg = 0
while(!Master.current_runlevel) // Make sure the initial startup is complete.
if(!said_msg)
said_msg = 1
log_unit_test("Waiting for subystems initilization to finish.")
stoplag(10)
world.save_mode("extended")
sleep(1)
ticker.current_state = GAME_STATE_SETTING_UP
Master.SetRunLevel(RUNLEVEL_SETUP)
log_unit_test("Round has been started. Waiting 10 seconds to start tests.")
sleep(100)
var/list/test_datums = typesof(/datum/unit_test)
var/list/async_test = list()
var/list/started_tests = list()
log_unit_test("Testing Started.")
for(var/test in test_datums)
var/datum/unit_test/d = new test()
if(d.disabled)
d.pass("[ASCII_RED]Check Disabled: [d.disabled][ASCII_RESET]")
continue
if(findtext(d.name, "template"))
continue
if(isnull(d.start_test())) // Start the test.
d.fail("Test Runtimed")
if(d.async) // If it's async then we'll need to check back on it later.
async_test.Add(d)
total_unit_tests++
//
// Check the async tests to see if they are finished.
//
while(async_test.len)
for(var/datum/unit_test/test in async_test)
if(test.check_result())
async_test.Remove(test)
sleep(1)
//
// Make sure all Unit Tests reported a result
//
for(var/datum/unit_test/test in started_tests)
if(!test.reported)
test.fail("Test failed to report a result.")
if(all_unit_tests_passed)
log_unit_test("[ASCII_GREEN]*** All Unit Tests Passed \[[total_unit_tests]\] ***[ASCII_RESET]")
else
log_unit_test("[ASCII_RED]!!! \[[failed_unit_tests]\\[total_unit_tests]\] Unit Tests Failed !!![ASCII_RESET]")
log_unit_test("Caught [GLOB.total_runtimes] Runtime\s.")
world.Del()
/datum/unit_test/proc/get_standard_turf()
return locate(20,20,1)
/datum/unit_test/proc/log_bad(var/message)
log_unit_test("[ASCII_RED]\[[name]\]: [message][ASCII_RESET]")
/datum/unit_test/proc/log_debug(var/message)
log_unit_test("[ASCII_YELLOW]--- DEBUG --- \[[name]\]: [message][ASCII_RESET]")
// This signal should never be possible to call if the game is not compiled for unit tests
/datum/unit_test/proc/get_signal_data(atom/source, list/data = list())
SIGNAL_HANDLER
return
-27
View File
@@ -1,27 +0,0 @@
//
// VOREStation extensions to unit test framework.
//
/datum/unit_test
var/static/default_mobloc = null
/datum/unit_test/proc/create_test_mob(var/turf/mobloc = null, var/mobtype = /mob/living/carbon/human, var/with_mind = FALSE)
if(isnull(mobloc))
if(!default_mobloc)
for(var/turf/simulated/floor/tiled/T in world)
var/pressure = T.zone.air.return_pressure()
if(90 < pressure && pressure < 120) // Find a turf between 90 and 120
default_mobloc = T
break
mobloc = default_mobloc
if(!mobloc)
fail("Unable to find a location to create test mob")
return 0
var/mob/living/carbon/human/H = new mobtype(mobloc)
H.init_vore()
if(with_mind)
H.mind_initialize("TestKey[rand(0,10000)]")
return H
-186
View File
@@ -1,186 +0,0 @@
/datum/unit_test/belly_nonsuffocation
name = "MOB: human mob does not suffocate in a belly"
var/startLifeTick
var/startOxyloss
var/endOxyloss
var/mob/living/carbon/human/pred
var/mob/living/carbon/human/prey
async = 1
/datum/unit_test/belly_nonsuffocation/start_test()
pred = create_test_mob()
if(!istype(pred))
return 0
prey = create_test_mob(pred.loc)
if(!istype(prey))
return 0
return 1
/datum/unit_test/belly_nonsuffocation/check_result()
// Unfortuantely we need to wait for the pred's belly to initialize. (Currently after a spawn())
if(!pred.vore_organs || !pred.vore_organs.len)
return 0
// Now that pred belly exists, we can eat the prey.
if(!pred.vore_selected)
fail("[pred] has no vore_selected.")
return 1
// Attempt to eat the prey
if(prey.loc != pred.vore_selected)
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]")
return 1
// Okay, we succeeded in eating them, now lets wait a bit
startLifeTick = pred.life_tick
startOxyloss = prey.getOxyLoss()
return 0
if(pred.life_tick < (startLifeTick + 10))
return 0 // Wait for them to breathe a few times
// Alright lets check it!
endOxyloss = prey.getOxyLoss()
if(startOxyloss < endOxyloss)
fail("Prey takes oxygen damage in a pred's belly! (Before: [startOxyloss]; after: [endOxyloss])")
else
pass("Prey is not taking oxygen damage in pred's belly. (Before: [startOxyloss]; after: [endOxyloss])")
qdel(prey)
qdel(pred)
return 1
////////////////////////////////////////////////////////////////
/datum/unit_test/belly_spacesafe
name = "MOB: human mob protected from space in a belly"
var/startLifeTick
var/startOxyloss
var/endOxyloss
var/mob/living/carbon/human/pred
var/mob/living/carbon/human/prey
async = 1
/datum/unit_test/belly_spacesafe/start_test()
pred = create_test_mob()
if(!istype(pred))
return 0
prey = create_test_mob(pred.loc)
if(!istype(prey))
return 0
return 1
/datum/unit_test/belly_spacesafe/check_result()
// Unfortuantely we need to wait for the pred's belly to initialize. (Currently after a spawn())
if(!pred.vore_organs || !pred.vore_organs.len)
return 0
// Now that pred belly exists, we can eat the prey.
if(!pred.vore_selected)
fail("[pred] has no vore_selected.")
return 1
// Attempt to eat the prey
if(prey.loc != pred.vore_selected)
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]")
return 1
else
// Get an empty space level instead of just picking a random space turf
var/empty_z = using_map.get_empty_zlevel()
if(!empty_z)
fail("Unable to get empty z-level for vore space protection test!")
return 1
// Away from map edges so they don't transit while we're testing
var/mid_w = round(world.maxx*0.5)
var/mid_h = round(world.maxy*0.5)
var/turf/T = locate(mid_w, mid_h, empty_z)
if(!T)
fail("Unable to get turf for vore space protection test!")
return 1
else
pred.forceMove(T)
// Okay, we succeeded in eating them, now lets wait a bit
startLifeTick = pred.life_tick
startOxyloss = prey.getOxyLoss()
return 0
if(pred.life_tick < (startLifeTick + 10))
return 0 // Wait for them to breathe a few times
// Alright lets check it!
endOxyloss = prey.getOxyLoss()
if(startOxyloss < endOxyloss)
fail("Prey takes oxygen damage in space! (Before: [startOxyloss]; after: [endOxyloss])")
else
pass("Prey is not taking oxygen damage in space. (Before: [startOxyloss]; after: [endOxyloss])")
qdel(prey)
qdel(pred)
return 1
////////////////////////////////////////////////////////////////
/datum/unit_test/belly_damage
name = "MOB: human mob takes damage from digestion"
var/startLifeTick
var/startBruteBurn
var/endBruteBurn
var/mob/living/carbon/human/pred
var/mob/living/carbon/human/prey
async = 1
/datum/unit_test/belly_damage/start_test()
pred = create_test_mob()
if(!istype(pred))
return 0
prey = create_test_mob(pred.loc)
if(!istype(prey))
return 0
return 1
/datum/unit_test/belly_damage/check_result()
// Unfortuantely we need to wait for the pred's belly to initialize. (Currently after a spawn())
if(!pred.vore_organs || !pred.vore_organs.len)
return 0
// Now that pred belly exists, we can eat the prey.
if(!pred.vore_selected)
fail("[pred] has no vore_selected.")
return 1
// Attempt to eat the prey
if(prey.loc != pred.vore_selected)
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]")
return 1
// Okay, we succeeded in eating them, now lets wait a bit
pred.vore_selected.digest_mode = DM_DIGEST
startLifeTick = pred.life_tick
startBruteBurn = prey.getBruteLoss() + prey.getFireLoss()
return 0
if(pred.life_tick < (startLifeTick + 10))
return 0 // Wait a few ticks for damage to happen
// 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])")
else
pass("Prey is taking damage in pred's belly. (Before: [startBruteBurn]; after: [endBruteBurn])")
qdel(prey)
qdel(pred)
return 1
-30
View File
@@ -1,30 +0,0 @@
///NOTE: LEAVE THIS FILE UNTICKED AND THE SEGMENTS OF CODE COMMENTED OUT UNLESS YOU NEED TO DO SOMETHING SPECIFIC / TRACK SOMETHING SPECIFIC.
/*
/// Testing if DNA & Species datums get destoryed or not. Use this as a basis to test for memory leaks.
/// This happens if DNA is copied from somewhere but not qdel_swap()'d. WARNING: Not every time it's copied should be qdel_swap()'d.
/// But if you are doing anything DNA copy related, turn this on and make sure it's not infintely storing a datum somewhere.
GLOBAL_VAR(total_dna_counters)
GLOBAL_VAR(total_species_counters)
/datum/dna/New()
. = ..()
GLOB.total_dna_counters += 1
/datum/dna/Destroy()
. = ..()
GLOB.total_dna_counters -= 1
/datum/species/New()
. = ..()
GLOB.total_species_counters += 1
/datum/species/Destroy()
. = ..()
GLOB.total_species_counters -= 1
/proc/delete_all_monkeys()
for(var/mob/living/carbon/human/monkey/monkeys in world)
qdel(monkeys)
*/