[MIRROR] Increased cached_icon_states usage and tweaks to unit testing (#11445)

Co-authored-by: Drathek <76988376+Drulikar@users.noreply.github.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-08-16 12:05:13 -07:00
committed by GitHub
parent 403a7e0267
commit 14fed02701
15 changed files with 160 additions and 153 deletions

View File

@@ -13,48 +13,44 @@
validate_accessory_list(/datum/sprite_accessory/tail)
validate_accessory_list(/datum/sprite_accessory/wing)
/datum/unit_test/sprite_accessories_shall_be_unique/proc/validate_accessory_list(var/path)
/datum/unit_test/sprite_accessories_shall_be_unique/proc/validate_accessory_list(path)
var/list/collection = list()
for(var/SP in subtypesof(path))
var/datum/sprite_accessory/A = new SP()
TEST_ASSERT(A, "[SP]: Cosmetic - Path resolved to null in list.")
if(!A)
for(var/datum/sprite_accessory/accessory as anything in subtypesof(path))
if(accessory::name == DEVELOPER_WARNING_NAME)
continue
TEST_ASSERT(A.name, "[A] - [A.type]: Cosmetic - Missing name.")
TEST_ASSERT(accessory::name, "[accessory::name] - [accessory::type]: Cosmetic - Missing name.")
if(A.name == DEVELOPER_WARNING_NAME)
continue
TEST_ASSERT(!collection[accessory::name], "[accessory::name] - [accessory::type]: Cosmetic - Name defined twice. Original def [collection[accessory::name]]")
if(!collection[accessory::name])
collection[accessory::name] = accessory::type
TEST_ASSERT(!collection[A.name], "[A] - [A.type]: Cosmetic - Name defined twice. Original def [collection[A.name]]")
if(!collection[A.name])
collection[A.name] = A.type
if(istype(A,text2path("[path]/invisible")))
TEST_ASSERT(!A.icon_state, "[A] - [A.type]: Cosmetic - Invisible subtype has icon_state.")
else if(!A.icon_state)
TEST_ASSERT(A.icon_state, "[A] - [A.type]: Cosmetic - Has no icon_state.")
if(ispath(accessory, text2path("[path]/invisible")))
TEST_ASSERT(!accessory::icon_state, "[accessory::name] - [accessory::type]: Cosmetic - Invisible subtype has icon_state.")
else if(!accessory::icon_state)
TEST_FAIL("[accessory::name] - [accessory::type]: Cosmetic - Has no icon_state.")
else
// Check if valid icon
validate_icons(A)
validate_icons(accessory)
qdel(A)
/datum/unit_test/sprite_accessories_shall_be_unique/proc/validate_icons(datum/sprite_accessory/accessory)
var/actual_icon_state = accessory::icon_state
/datum/unit_test/sprite_accessories_shall_be_unique/proc/validate_icons(datum/sprite_accessory/A)
var/actual_icon_state = A.icon_state
if(istype(accessory, /datum/sprite_accessory/hair))
actual_icon_state += "_s"
TEST_ASSERT(actual_icon_state in cached_icon_states(accessory::icon), "[accessory::name] - [accessory::type]: Cosmetic - Icon_state \"[actual_icon_state]\" is not present in [accessory::icon].")
return
if(istype(A,/datum/sprite_accessory/hair))
actual_icon_state = "[A.icon_state]_s"
TEST_ASSERT(actual_icon_state in cached_icon_states(A.icon), "[A] - [A.type]: Cosmetic - Icon_state \"[actual_icon_state]\" is not present in [A.icon].")
if(istype(accessory, /datum/sprite_accessory/facial_hair))
actual_icon_state += "_s"
TEST_ASSERT(actual_icon_state in cached_icon_states(accessory::icon), "[accessory::name] - [accessory::type]: Cosmetic - Icon_state \"[actual_icon_state]\" is not present in [accessory::icon].")
return
if(istype(A,/datum/sprite_accessory/facial_hair))
actual_icon_state = "[A.icon_state]_s"
TEST_ASSERT(actual_icon_state in cached_icon_states(A.icon), "[A] - [A.type]: Cosmetic - Icon_state \"[actual_icon_state]\" is not present in [A.icon].")
if(istype(accessory, /datum/sprite_accessory/marking))
var/datum/sprite_accessory/marking/marking = accessory
for(var/body_part in marking.body_parts)
TEST_ASSERT(body_part in BP_ALL, "[accessory::name] - [accessory::type]: Cosmetic - Has an illegal bodypart \"[body_part]\". ONLY use parts listed in BP_ALL.")
if(istype(A,/datum/sprite_accessory/marking))
var/datum/sprite_accessory/marking/MA = A
for(var/BP in MA.body_parts)
TEST_ASSERT(BP in BP_ALL, "[A] - [A.type]: Cosmetic - Has an illegal bodypart \"[BP]\". ONLY use parts listed in BP_ALL.")
actual_icon_state = "[A.icon_state]-[BP]"
TEST_ASSERT(actual_icon_state in cached_icon_states(A.icon), "[A] - [A.type]: Cosmetic - Icon_state \"[actual_icon_state]\" is not present in [A.icon].")
actual_icon_state = "[accessory::icon_state]-[body_part]"
TEST_ASSERT(actual_icon_state in cached_icon_states(accessory::icon), "[accessory::name] - [accessory::type]: Cosmetic - Icon_state \"[actual_icon_state]\" is not present in [accessory::icon].")
return

View File

@@ -6,15 +6,16 @@
var/failed = 0
/datum/unit_test/all_robot_sprites_must_be_valid/Run()
for(var/sprite in subtypesof(/datum/robot_sprite))
var/datum/robot_sprite/RS = new sprite()
if(!RS.name) // Parent type, ignore me
for(var/datum/robot_sprite/robot_type as anything in subtypesof(/datum/robot_sprite))
if(!robot_type::name) // Parent type, ignore me
continue
TEST_ASSERT(RS.sprite_icon, "[RS.type]: Robots - Robot sprite \"[RS.name]\", missing sprite_icon.")
if(!RS.sprite_icon)
if(!robot_type::sprite_icon)
TEST_FAIL("[robot_type]: Robots - Robot sprite \"[robot_type::name]\", missing sprite_icon.")
continue
var/datum/robot_sprite/robot = new robot_type()
var/list/checks = list(
"[ROBOT_HAS_SPEED_SPRITE]" = "-roll",
"[ROBOT_HAS_SHIELD_SPRITE]" = "-shield",
@@ -27,143 +28,143 @@
"[ROBOT_HAS_TASER_SPRITE]" = "-taser",
"[ROBOT_HAS_DISABLER_SPRITE]" = "-disabler"
)
for(var/C in checks)
if(RS.sprite_flag_check(text2num(C)))
check_state(RS,checks[C])
for(var/check in checks)
if(robot.sprite_flag_check(text2num(check)))
check_state(robot,checks[check])
// eyes, lights, markings
if(RS.has_eye_sprites)
check_state(RS,"-eyes")
if(RS.has_eye_light_sprites)
check_state(RS,"-lights")
if(LAZYLEN(RS.sprite_decals))
for(var/decal in RS.sprite_decals)
check_state(RS,"-[decal]")
if(LAZYLEN(RS.sprite_animations))
for(var/animation in RS.sprite_animations)
check_state(RS,"-[animation]")
if(robot.has_eye_sprites)
check_state(robot,"-eyes")
if(robot.has_eye_light_sprites)
check_state(robot,"-lights")
if(LAZYLEN(robot.sprite_decals))
for(var/decal in robot.sprite_decals)
check_state(robot,"-[decal]")
if(LAZYLEN(robot.sprite_animations))
for(var/animation in robot.sprite_animations)
check_state(robot,"-[animation]")
// Control panel
if(RS.has_custom_open_sprites)
check_state(RS,"-openpanel_nc")
check_state(RS,"-openpanel_c")
check_state(RS,"-openpanel_w")
if(robot.has_custom_open_sprites)
check_state(robot,"-openpanel_nc")
check_state(robot,"-openpanel_c")
check_state(robot,"-openpanel_w")
// Glow State
if(RS.has_glow_sprites)
check_state(RS,"-glow")
if(robot.has_glow_sprites)
check_state(robot,"-glow")
// Bellies
if(RS.has_vore_belly_sprites && !RS.belly_capacity_list)
if(RS.has_sleeper_light_indicator)
if(robot.has_vore_belly_sprites && !robot.belly_capacity_list)
if(robot.has_sleeper_light_indicator)
// belly r/g light
check_state(RS,"-sleeper-r")
check_state(RS,"-sleeper-g")
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
check_state(robot,"-sleeper-r")
check_state(robot,"-sleeper-g")
if(robot.has_vore_belly_resting_sprites)
for(var/rest_style in robot.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
check_state(RS,"-sleeper-r-[rest_style]")
check_state(RS,"-sleeper-g-[rest_style]")
check_state(robot,"-sleeper-r-[rest_style]")
check_state(robot,"-sleeper-g-[rest_style]")
// struggling
if(RS.has_vore_struggle_sprite)
check_state(RS,"-sleeper-r-struggle")
check_state(RS,"-sleeper-g-struggle")
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
if(robot.has_vore_struggle_sprite)
check_state(robot,"-sleeper-r-struggle")
check_state(robot,"-sleeper-g-struggle")
if(robot.has_vore_belly_resting_sprites)
for(var/rest_style in robot.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
check_state(RS,"-sleeper-r-[rest_style]-struggle")
check_state(RS,"-sleeper-g-[rest_style]-struggle")
check_state(robot,"-sleeper-r-[rest_style]-struggle")
check_state(robot,"-sleeper-g-[rest_style]-struggle")
else
// belly
check_state(RS,"-sleeper")
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
check_state(robot,"-sleeper")
if(robot.has_vore_belly_resting_sprites)
for(var/rest_style in robot.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
check_state(RS,"-sleeper-[rest_style]")
check_state(robot,"-sleeper-[rest_style]")
// struggling
if(RS.has_vore_struggle_sprite)
check_state(RS,"-sleeper-struggle")
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
if(robot.has_vore_struggle_sprite)
check_state(robot,"-sleeper-struggle")
if(robot.has_vore_belly_resting_sprites)
for(var/rest_style in robot.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
check_state(RS,"-sleeper-[rest_style]-struggle")
else if (RS.belly_capacity_list)
for(var/belly in RS.belly_capacity_list)
for(var/num = 1 to RS.belly_capacity_list[belly])
check_state(robot,"-sleeper-[rest_style]-struggle")
else if(robot.belly_capacity_list)
for(var/belly in robot.belly_capacity_list)
for(var/num = 1 to robot.belly_capacity_list[belly])
// big belly
check_state(RS,"-[belly]-[num]")
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
check_state(robot,"-[belly]-[num]")
if(robot.has_vore_belly_resting_sprites)
for(var/rest_style in robot.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
check_state(RS,"-[belly]-[num]-[rest_style]")
check_state(robot,"-[belly]-[num]-[rest_style]")
// struggling
if(RS.has_vore_struggle_sprite)
check_state(RS,"-[belly]-[num]-struggle")
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
if(robot.has_vore_struggle_sprite)
check_state(robot,"-[belly]-[num]-struggle")
if(robot.has_vore_belly_resting_sprites)
for(var/rest_style in robot.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
check_state(RS,"-[belly]-[num]-[rest_style]-struggle")
if(RS.belly_light_list)
for(var/belly in RS.belly_light_list)
for(var/num = 1 to RS.belly_light_list[belly])
check_state(robot,"-[belly]-[num]-[rest_style]-struggle")
if(robot.belly_light_list)
for(var/belly in robot.belly_light_list)
for(var/num = 1 to robot.belly_light_list[belly])
// multi belly r/g light
check_state(RS,"-[belly]-[num]-r")
check_state(RS,"-[belly]-[num]-g")
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
check_state(robot,"-[belly]-[num]-r")
check_state(robot,"-[belly]-[num]-g")
if(robot.has_vore_belly_resting_sprites)
for(var/rest_style in robot.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
check_state(RS,"-[belly]-[num]-r-[rest_style]")
check_state(RS,"-[belly]-[num]-g-[rest_style]")
check_state(robot,"-[belly]-[num]-r-[rest_style]")
check_state(robot,"-[belly]-[num]-g-[rest_style]")
// struggling
if(RS.has_vore_struggle_sprite)
check_state(RS,"-[belly]-[num]-r-struggle")
check_state(RS,"-[belly]-[num]-g-struggle")
if(RS.has_vore_belly_resting_sprites)
for(var/rest_style in RS.rest_sprite_options)
if(robot.has_vore_struggle_sprite)
check_state(robot,"-[belly]-[num]-r-struggle")
check_state(robot,"-[belly]-[num]-g-struggle")
if(robot.has_vore_belly_resting_sprites)
for(var/rest_style in robot.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
check_state(RS,"-[belly]-[num]-r-[rest_style]-struggle")
check_state(RS,"-[belly]-[num]-g-[rest_style]-struggle")
check_state(robot,"-[belly]-[num]-r-[rest_style]-struggle")
check_state(robot,"-[belly]-[num]-g-[rest_style]-struggle")
// reseting
for(var/rest_style in RS.rest_sprite_options)
for(var/rest_style in robot.rest_sprite_options)
rest_style = lowertext(rest_style)
if(rest_style == "default")
rest_style = "rest"
check_state(RS,"-[rest_style]")
if(RS.has_glow_sprites)
check_state(RS,"-[rest_style]-glow")
if(RS.has_rest_lights_sprites)
check_state(RS,"-[rest_style]-lights")
if(RS.has_rest_eyes_sprites)
check_state(RS,"-[rest_style]-eyes")
check_state(robot,"-[rest_style]")
if(robot.has_glow_sprites)
check_state(robot,"-[rest_style]-glow")
if(robot.has_rest_lights_sprites)
check_state(robot,"-[rest_style]-lights")
if(robot.has_rest_eyes_sprites)
check_state(robot,"-[rest_style]-eyes")
// death
if(RS.has_dead_sprite)
check_state(RS,"-wreck")
if(RS.has_dead_sprite_overlay) // Only one per dmi
TEST_ASSERT("wreck-overlay" in cached_icon_states(RS.sprite_icon), "[RS.type]: Robots - Robot sprite \"[RS.name]\", missing icon_state wreck-overlay, in dmi \"[RS.sprite_icon]\".")
if(robot.has_dead_sprite)
check_state(robot,"-wreck")
if(robot.has_dead_sprite_overlay) // Only one per dmi
TEST_ASSERT("wreck-overlay" in cached_icon_states(robot.sprite_icon), "[robot.type]: Robots - Robot sprite \"[robot.name]\", missing icon_state wreck-overlay, in dmi \"[robot.sprite_icon]\".")
// offset
var/icon/I = new(RS.sprite_icon)
TEST_ASSERT_EQUAL(RS.icon_x, I.Width(), "[RS.type]: Robots - Robot sprite \"[RS.name]\", icon_x \"[RS.icon_x]\" did not match dmi configured width \"[I.Width()]\"")
TEST_ASSERT_EQUAL(RS.icon_y, I.Height(), "[RS.type]: Robots - Robot sprite \"[RS.name]\", icon_y \"[RS.icon_y]\" did not match dmi configured height \"[I.Height()]\"")
TEST_ASSERT_EQUAL(RS.icon_y, RS.vis_height, "[RS.type]: Robots - Robot sprite \"[RS.name]\", vis_height \"[RS.vis_height]\" did not match icon_y \"[RS.icon_y]\"")
var/icon/I = new(robot.sprite_icon)
TEST_ASSERT_EQUAL(robot.icon_x, I.Width(), "[robot.type]: Robots - Robot sprite \"[robot.name]\", icon_x \"[robot.icon_x]\" did not match dmi configured width \"[I.Width()]\"")
TEST_ASSERT_EQUAL(robot.icon_y, I.Height(), "[robot.type]: Robots - Robot sprite \"[robot.name]\", icon_y \"[robot.icon_y]\" did not match dmi configured height \"[I.Height()]\"")
TEST_ASSERT_EQUAL(robot.icon_y, robot.vis_height, "[robot.type]: Robots - Robot sprite \"[robot.name]\", vis_height \"[robot.vis_height]\" did not match icon_y \"[robot.icon_y]\"")
var/legal_offset = (I.Width() - world.icon_size) / 2
TEST_ASSERT_EQUAL(RS.pixel_x, -legal_offset, "[RS.type]: Robots - Robot sprite \"[RS.name]\", pixel_x \"[RS.pixel_x]\" did not have correct offset, should be \"[-legal_offset]\"")
TEST_ASSERT_EQUAL(robot.pixel_x, -legal_offset, "[robot.type]: Robots - Robot sprite \"[robot.name]\", pixel_x \"[robot.pixel_x]\" did not have correct offset, should be \"[-legal_offset]\"")
qdel(I)
qdel(RS)
qdel(robot)
/datum/unit_test/all_robot_sprites_must_be_valid/proc/check_state(datum/robot_sprite/RS, append)
var/check_state = "[RS.sprite_icon_state][append]"
TEST_ASSERT(check_state in cached_icon_states(RS.sprite_icon), "[RS.type]: Robots - Robot sprite \"[RS.name]\", enabled but missing icon_state \"[check_state]\", in dmi \"[RS.sprite_icon]\".")
/datum/unit_test/all_robot_sprites_must_be_valid/proc/check_state(datum/robot_sprite/robot, append)
var/check_state = "[robot.sprite_icon_state][append]"
TEST_ASSERT(check_state in cached_icon_states(robot.sprite_icon), "[robot.type]: Robots - Robot sprite \"[robot.name]\", enabled but missing icon_state \"[check_state]\", in dmi \"[robot.sprite_icon]\".")

View File

@@ -2,23 +2,30 @@
/datum/unit_test/spritesheets
/datum/unit_test/spritesheets/Run()
var/regex/valid_css_class = new(@"^([\l_][\w\-]|[\l_\-][\l_])")
for(var/datum/asset/spritesheet/sheet as anything in subtypesof(/datum/asset/spritesheet))
if(!initial(sheet.name)) //Ignore abstract types
continue
if (sheet == initial(sheet._abstract))
if(sheet == initial(sheet._abstract))
continue
sheet = get_asset_datum(sheet)
for(var/sprite_name in sheet.sprites)
if(!sprite_name)
TEST_FAIL("Spritesheet [sheet.type] has a nameless icon state.")
if(!valid_css_class.Find(sprite_name))
// https://www.w3.org/TR/CSS2/syndata.html#value-def-identifier
TEST_FAIL("Spritesheet [sheet.type] has a icon state that doesn't comply with css standards: '[sprite_name]'.")
// Test IconForge generated sheets as well
for(var/datum/asset/spritesheet_batched/sheet as anything in subtypesof(/datum/asset/spritesheet_batched))
if(!initial(sheet.name)) //Ignore abstract types
continue
if (sheet == initial(sheet._abstract))
if(sheet == initial(sheet._abstract))
continue
sheet = get_asset_datum(sheet)
for(var/sprite_name in sheet.sprites)
if(!sprite_name)
TEST_FAIL("Spritesheet [sheet.type] has a nameless icon state.")
if(!valid_css_class.Find(sprite_name))
// https://www.w3.org/TR/CSS2/syndata.html#value-def-identifier
TEST_FAIL("Spritesheet [sheet.type] has a icon state that doesn't comply with css standards: '[sprite_name]'.")