///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_DEL_WORLD /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, ) //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/tablet/integrated) //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) //Nothing to hallucinate if there's nothing to hallicinate ignore += typesof(/obj/effect/hallucination) //These want fried food to take on the shape of, we can't pass that in ignore += typesof(/obj/item/food/deepfryholder) //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) //There's no shapeshift to hold ignore += typesof(/obj/shapeshift_holder) //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) //SKYRAT EDIT ADDITION - OUR IGNORES DOWN HERE //Not designed to be spawned without a turf. ignore += typesof(/obj/effect/abstract/liquid_turf) //Not designed to be spawned individually. ignore += typesof(/obj/structure/biohazard_blob) //Unused - not supposed to be spawned without SSliquids ignore += typesof(/turf/open/openspace/ocean) //Baseturf editors can only go up to ten, stop this. ignore += typesof(/obj/effect/baseturf_helper) //SKYRAT EDIT END //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) var/list/cached_contents = spawn_at.contents.Copy() var/baseturf_count = length(spawn_at.baseturfs) for(var/type_path in typesof(/atom/movable, /turf) - ignore) //No areas please if(ispath(type_path, /turf)) spawn_at.ChangeTurf(type_path, /turf/baseturf_skipover) //We change it back to prevent pain, please don't ask spawn_at.ChangeTurf(/turf/open/floor/wood, /turf/baseturf_skipover) if(baseturf_count != length(spawn_at.baseturfs)) TEST_FAIL("[type_path] changed the amount of baseturfs we have [baseturf_count] -> [length(spawn_at.baseturfs)]") baseturf_count = length(spawn_at.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) //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