Screenshot tests (#67679)
Adds screenshot visual testing workflow and scripts.
@@ -110,6 +110,9 @@
|
||||
early = TRUE
|
||||
cross_round_cachable = TRUE
|
||||
|
||||
/// Mapping of spritesheet keys -> icons
|
||||
var/list/antag_icons = list()
|
||||
|
||||
/datum/asset/spritesheet/antagonists/create_spritesheets()
|
||||
// Antagonists that don't have a dynamic ruleset, but do have a preference
|
||||
var/static/list/non_ruleset_antagonists = list(
|
||||
@@ -128,7 +131,6 @@
|
||||
antagonists[initial(ruleset.antag_flag)] = antagonist_type
|
||||
|
||||
var/list/generated_icons = list()
|
||||
var/list/to_insert = list()
|
||||
|
||||
for (var/antag_flag in antagonists)
|
||||
var/datum/antagonist/antagonist_type = antagonists[antag_flag]
|
||||
@@ -137,7 +139,7 @@
|
||||
var/spritesheet_key = serialize_antag_name(antag_flag)
|
||||
|
||||
if (!isnull(generated_icons[antagonist_type]))
|
||||
to_insert[spritesheet_key] = generated_icons[antagonist_type]
|
||||
antag_icons[spritesheet_key] = generated_icons[antagonist_type]
|
||||
continue
|
||||
|
||||
var/datum/antagonist/antagonist = new antagonist_type
|
||||
@@ -152,10 +154,10 @@
|
||||
// If an icon is not prepared to be scaled to that size, it looks really ugly, and this
|
||||
// makes it harder to figure out what size it *actually* is.
|
||||
generated_icons[antagonist_type] = preview_icon
|
||||
to_insert[spritesheet_key] = preview_icon
|
||||
antag_icons[spritesheet_key] = preview_icon
|
||||
|
||||
for (var/spritesheet_key in to_insert)
|
||||
Insert(spritesheet_key, to_insert[spritesheet_key])
|
||||
for (var/spritesheet_key in antag_icons)
|
||||
Insert(spritesheet_key, antag_icons[spritesheet_key])
|
||||
|
||||
/// Serializes an antag name to be used for preferences UI
|
||||
/proc/serialize_antag_name(antag_name)
|
||||
|
||||
@@ -102,7 +102,8 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
|
||||
dna.features["snout"] = "Round"
|
||||
dna.features["spines"] = "None"
|
||||
dna.features["tail_cat"] = "None"
|
||||
dna.features["tail_lizard"] = "Light"
|
||||
dna.features["tail_lizard"] = "Smooth"
|
||||
dna.features["pod_hair"] = "Ivy"
|
||||
|
||||
//Inefficient pooling/caching way.
|
||||
GLOBAL_LIST_EMPTY(human_dummy_list)
|
||||
|
||||
@@ -129,6 +129,9 @@
|
||||
#include "reagent_recipe_collisions.dm"
|
||||
#include "resist.dm"
|
||||
#include "say.dm"
|
||||
#include "screenshot_antag_icons.dm"
|
||||
#include "screenshot_basic.dm"
|
||||
#include "screenshot_humanoids.dm"
|
||||
#include "security_officer_distribution.dm"
|
||||
#include "serving_tray.dm"
|
||||
#include "siunit.dm"
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/// A screenshot test to make sure every antag icon in the preferences menu is consistent
|
||||
/datum/unit_test/screenshot_antag_icons
|
||||
|
||||
/datum/unit_test/screenshot_antag_icons/Run()
|
||||
var/datum/asset/spritesheet/antagonists/antagonists = get_asset_datum(/datum/asset/spritesheet/antagonists)
|
||||
|
||||
for (var/antag_icon_key in antagonists.antag_icons)
|
||||
var/icon/reference_icon = antagonists.antag_icons[antag_icon_key]
|
||||
|
||||
var/icon/icon = new()
|
||||
icon.Insert(reference_icon, null, SOUTH, 1)
|
||||
test_screenshot(antag_icon_key, icon)
|
||||
@@ -0,0 +1,8 @@
|
||||
/// This is an example for screenshot tests, and a meta-test to make sure they work in the success case.
|
||||
/// It creates a picture that is red on the left side, green on the other.
|
||||
/datum/unit_test/screenshot_basic
|
||||
|
||||
/datum/unit_test/screenshot_basic/Run()
|
||||
var/icon/red = icon('icons/blanks/32x32.dmi', "nothing")
|
||||
red.Blend(COLOR_RED, ICON_OVERLAY)
|
||||
test_screenshot("red", red)
|
||||
@@ -0,0 +1,44 @@
|
||||
/// A screenshot test for every humanoid species with a handful of jobs.
|
||||
/datum/unit_test/screenshot_humanoids
|
||||
|
||||
/datum/unit_test/screenshot_humanoids/Run()
|
||||
// Test lizards as their own thing so we can get more coverage on their features
|
||||
var/mob/living/carbon/human/lizard = allocate(/mob/living/carbon/human/dummy/consistent)
|
||||
lizard.dna.features["mcolor"] = "#099"
|
||||
lizard.dna.features["tail_lizard"] = "Light Tiger"
|
||||
lizard.dna.features["snout"] = "Sharp + Light"
|
||||
lizard.dna.features["horns"] = "Simple"
|
||||
lizard.dna.features["frills"] = "Aquatic"
|
||||
lizard.dna.features["legs"] = "Normal Legs"
|
||||
lizard.set_species(/datum/species/lizard)
|
||||
lizard.equipOutfit(/datum/outfit/job/engineer)
|
||||
test_screenshot("[/datum/species/lizard]", get_flat_icon_for_all_directions(lizard))
|
||||
|
||||
// let me have this
|
||||
var/mob/living/carbon/human/moth = allocate(/mob/living/carbon/human/dummy/consistent)
|
||||
moth.dna.features["moth_antennae"] = "Firewatch"
|
||||
moth.dna.features["moth_markings"] = "None"
|
||||
moth.dna.features["moth_wings"] = "Firewatch"
|
||||
moth.set_species(/datum/species/moth)
|
||||
moth.equipOutfit(/datum/outfit/job/cmo, visualsOnly = TRUE)
|
||||
test_screenshot("[/datum/species/moth]", get_flat_icon_for_all_directions(moth))
|
||||
|
||||
// The rest of the species
|
||||
for (var/datum/species/species_type as anything in subtypesof(/datum/species) - /datum/species/moth - /datum/species/lizard)
|
||||
test_screenshot("[species_type]", get_flat_icon_for_all_directions(make_dummy(species_type, /datum/outfit/job/assistant/consistent)))
|
||||
|
||||
/datum/unit_test/screenshot_humanoids/proc/get_flat_icon_for_all_directions(atom/thing)
|
||||
var/icon/output = icon('icons/effects/effects.dmi', "nothing")
|
||||
COMPILE_OVERLAYS(thing)
|
||||
|
||||
for (var/direction in GLOB.cardinals)
|
||||
var/icon/partial = getFlatIcon(thing, defdir = direction, no_anim = TRUE)
|
||||
output.Insert(partial, dir = direction)
|
||||
|
||||
return output
|
||||
|
||||
/datum/unit_test/screenshot_humanoids/proc/make_dummy(species, job_outfit)
|
||||
var/mob/living/carbon/human/dummy/consistent/dummy = allocate(/mob/living/carbon/human/dummy/consistent)
|
||||
dummy.set_species(species)
|
||||
dummy.equipOutfit(job_outfit, visualsOnly = TRUE)
|
||||
return dummy
|
||||
@@ -0,0 +1,18 @@
|
||||
This folder contains the results for screenshot tests. Screenshot tests make sure an icon looks the same as it did before a change to prevent regressions.
|
||||
|
||||
You can create one by simply using the `test_screenshot` proc.
|
||||
|
||||
This example test screenshots a red image and keeps it.
|
||||
|
||||
```dm
|
||||
/// This is an example for screenshot tests, and a meta-test to make sure they work in the success case.
|
||||
/// It creates a picture that is red on the left side, green on the other.
|
||||
/datum/unit_test/screenshot_basic
|
||||
|
||||
/datum/unit_test/screenshot_basic/Run()
|
||||
var/icon/red = icon('icons/blanks/32x32.dmi', "nothing")
|
||||
red.Blend(COLOR_RED, ICON_OVERLAY)
|
||||
test_screenshot("red", red)
|
||||
```
|
||||
|
||||
Unfortunately, screenshot tests are sanest to test through a pull request directly, due to limitations with both DM and GitHub.
|
||||
|
After Width: | Height: | Size: 907 B |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 898 B |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 672 B |
|
After Width: | Height: | Size: 627 B |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 627 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 624 B |
|
After Width: | Height: | Size: 624 B |
|
After Width: | Height: | Size: 619 B |
|
After Width: | Height: | Size: 528 B |
|
After Width: | Height: | Size: 528 B |
|
After Width: | Height: | Size: 617 B |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 741 B |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 923 B |
|
After Width: | Height: | Size: 389 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 572 B |
|
After Width: | Height: | Size: 821 B |
|
After Width: | Height: | Size: 821 B |
|
After Width: | Height: | Size: 741 B |
|
After Width: | Height: | Size: 821 B |
|
After Width: | Height: | Size: 800 B |
|
After Width: | Height: | Size: 800 B |
|
After Width: | Height: | Size: 592 B |
|
After Width: | Height: | Size: 218 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 913 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 927 B |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1011 B |
|
After Width: | Height: | Size: 918 B |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1002 B |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 948 B |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 806 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 853 B |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
@@ -84,6 +84,30 @@ GLOBAL_LIST_EMPTY(unit_test_mapping_logs)
|
||||
allocated += instance
|
||||
return instance
|
||||
|
||||
/datum/unit_test/proc/test_screenshot(name, icon/icon)
|
||||
if (!istype(icon))
|
||||
TEST_FAIL("[icon] is not an icon.")
|
||||
return
|
||||
|
||||
var/path_prefix = replacetext(replacetext("[type]", "/datum/unit_test/", ""), "/", "_")
|
||||
name = replacetext(name, "/", "_")
|
||||
|
||||
var/filename = "code/modules/unit_tests/screenshots/[path_prefix]_[name].png"
|
||||
|
||||
if (fexists(filename))
|
||||
var/data_filename = "data/screenshots/[path_prefix]_[name].png"
|
||||
fcopy(icon, data_filename)
|
||||
log_test("[path_prefix]_[name] was found, putting in data/screenshots")
|
||||
else if (fexists("code"))
|
||||
// We are probably running in a local build
|
||||
fcopy(icon, filename)
|
||||
TEST_FAIL("Screenshot for [name] did not exist. One has been created.")
|
||||
else
|
||||
// We are probably running in real CI, so just pretend it worked and move on
|
||||
fcopy(icon, "data/screenshots_new/[path_prefix]_[name].png")
|
||||
|
||||
log_test("[path_prefix]_[name] was put in data/screenshots_new")
|
||||
|
||||
/proc/RunUnitTest(test_path, list/test_results)
|
||||
var/datum/unit_test/test = new test_path
|
||||
|
||||
|
||||