diff --git a/check_regex.yaml b/check_regex.yaml index a56bd83644d3..94f37cdcf83e 100644 --- a/check_regex.yaml +++ b/check_regex.yaml @@ -38,7 +38,7 @@ standards: - exactly: [ - 261, + 262, "non-bitwise << uses", '(?You are Santa! Your objective is to bring joy to the people on this station. You have a magical bag, which generates presents as long as you have it! You can examine the presents to take a peek inside, to make sure that you give the right gift to the right person.") /datum/antagonist/santa/proc/give_equipment() - var/mob/living/carbon/human/H = owner.current owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/area_teleport/teleport/santa) /datum/antagonist/santa/proc/give_objective() diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 4b4e10edd7ac..ba42b9bc9f46 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -41,6 +41,11 @@ /// Intended to be used in the manner of `TEST_FOCUS(/datum/unit_test/math)` #define TEST_FOCUS(test_path) ##test_path { focus = TRUE; } +/// Logs a noticable message on GitHub, but will not mark as an error. +/// Use this when something shouldn't happen and is of note, but shouldn't block CI. +/// Does not mark the test as failed. +#define TEST_NOTICE(source, message) source.log_for_test((##message), "notice", __FILE__, __LINE__) + /// Constants indicating unit test completion status #define UNIT_TEST_PASSED 0 #define UNIT_TEST_FAILED 1 @@ -81,7 +86,6 @@ #include "keybinding_init.dm" #include "machine_disassembly.dm" #include "open_air.dm" -#include "outfit_names.dm" #include "outfit_sanity.dm" #include "overmap.dm" #include "pills.dm" diff --git a/code/modules/unit_tests/outfit_names.dm b/code/modules/unit_tests/outfit_names.dm deleted file mode 100644 index b381bfeb7bf5..000000000000 --- a/code/modules/unit_tests/outfit_names.dm +++ /dev/null @@ -1,12 +0,0 @@ -/datum/unit_test/outfit_names/Run() - var/list/outfit_names = list() - - for(var/datum/outfit/outfit_type as anything in subtypesof(/datum/outfit)) - var/name = initial(outfit_type.name) - - if(name in outfit_names) - TEST_FAIL("Outfit name [name] is not unique: [outfit_type], [outfit_names[name]]") - - outfit_names[name] = outfit_type - - diff --git a/code/modules/unit_tests/outfit_sanity.dm b/code/modules/unit_tests/outfit_sanity.dm index 4dc12b4dc6e3..a09395d42103 100644 --- a/code/modules/unit_tests/outfit_sanity.dm +++ b/code/modules/unit_tests/outfit_sanity.dm @@ -6,7 +6,7 @@ if (outfit.random != TRUE) \ TEST_FAIL("[outfit.name]'s [#outfit_key] is invalid! Could not equip a [outfit.##outfit_key] into that slot."); \ else \ - log_world("[outfit.name]'s [#outfit_key] is invalid! Could not equip a [outfit.##outfit_key] into that slot."); \ + log_test("[outfit.name]'s [#outfit_key] is invalid! Could not equip a [outfit.##outfit_key] into that slot."); \ } \ } @@ -26,6 +26,7 @@ var/prototype_name = initial(prototype_outfit.name) var/mob/living/carbon/human/H = allocate(/mob/living/carbon/human) + var/list/outfit_names = list() for (var/outfit_type in subtypesof(/datum/outfit)) // Only make one human and keep undressing it because it's much faster for (var/obj/item/I in H.get_equipped_items(include_pockets = TRUE)) @@ -33,8 +34,12 @@ var/datum/outfit/outfit = new outfit_type - if(outfit.name == prototype_name) + var/outfit_name = outfit.name + if(outfit_name == prototype_name) TEST_FAIL("[outfit.type]'s name is invalid! Uses default outfit name!") + if(outfit_name in outfit_names) + TEST_FAIL("Outfit name [outfit_name] is not unique: [outfit_type], [outfit_names[outfit_name]]") + outfit_names[outfit_name] = outfit_type outfit.pre_equip(H, TRUE) CHECK_OUTFIT_SLOT(uniform, ITEM_SLOT_ICLOTHING) @@ -67,7 +72,7 @@ if (outfit.random != TRUE) TEST_FAIL("[outfit.name]'s backpack_contents are invalid! Couldn't add [path] to backpack.") else - log_world("[outfit.name]'s backpack_contents are invalid! Couldn't add [path] to backpack.") + log_test("[outfit.name]'s backpack_contents are invalid! Couldn't add [path] to backpack.") #undef CHECK_OUTFIT_SLOT diff --git a/code/modules/unit_tests/ship_outpost_placement.dm b/code/modules/unit_tests/ship_outpost_placement.dm index 48bbd6a181e3..0762af79e304 100644 --- a/code/modules/unit_tests/ship_outpost_placement.dm +++ b/code/modules/unit_tests/ship_outpost_placement.dm @@ -3,7 +3,7 @@ // disabled or intended as subshuttles for(var/name as anything in SSmapping.shuttle_templates) var/datum/map_template/shuttle/map = SSmapping.shuttle_templates[name] - log_world("Loading [map.name]") + log_test("Loading [map.name]") try // they'll spawn in empty space, and won't be docked new /datum/overmap/ship/controlled(list("x" = 1, "y" = 1), map) @@ -13,10 +13,10 @@ for(var/outpost_type in subtypesof(/datum/overmap/outpost)) var/datum/overmap/outpost/test_outpost = new outpost_type() - log_world("Testing [test_outpost.type]") + log_test("Testing [test_outpost.type]") for(var/datum/overmap/ship/controlled/cur_ship as anything in SSovermap.controlled_ships) - log_world(" - Docking [cur_ship.source_template.name]") + log_test(" - Docking [cur_ship.source_template.name]") // already-docked ships are ignored. // this was added to stop runtimes when subshuttles, which were docked to their parent ship, attempted to dock to the outpost as part of this test. diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm index 00e7c6e756ac..18b5b12e7f48 100644 --- a/code/modules/unit_tests/unit_test.dm +++ b/code/modules/unit_tests/unit_test.dm @@ -85,38 +85,58 @@ GLOBAL_VAR(test_log) allocated += instance return instance +/// Logs a test message. Will use GitHub action syntax found at https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions +/datum/unit_test/proc/log_for_test(text, priority, file, line, test_path) + // Need to escape the text to properly support newlines. + var/annotation_text = replacetext(text, "%", "%25") + annotation_text = replacetext(annotation_text, "\n", "%0A") + + log_world("::[priority] file=[file],line=[line],title=[test_path]: [type]::[annotation_text]") + /proc/RunUnitTest(test_path, list/test_results) var/datum/unit_test/test = new test_path GLOB.current_test = test var/duration = REALTIMEOFDAY + log_world("::group::[test_path]") test.Run() duration = REALTIMEOFDAY - duration GLOB.current_test = null GLOB.failed_any_test |= !test.succeeded - var/list/log_entry = list( - "[test.succeeded ? TEST_OUTPUT_GREEN("PASS") : TEST_OUTPUT_RED("FAIL")]: [test_path] [duration / 10]s", - ) + var/list/log_entry = list() var/list/fail_reasons = test.fail_reasons + var/test_output_desc = "[test_path]" + var/message = "" + for(var/reasonID in 1 to LAZYLEN(fail_reasons)) var/text = fail_reasons[reasonID][1] var/file = fail_reasons[reasonID][2] var/line = fail_reasons[reasonID][3] - /// Github action annotation. - log_world("::error file=[file],line=[line],title=[test_path]::[text]") + test.log_for_test(text, "error", file, line, test_path) // Normal log message log_entry += "\tFAILURE #[reasonID]: [text] at [file]:[line]" - var/message = log_entry.Join("\n") - log_test(message) + if(length(log_entry)) + message = log_entry.Join("\n") + log_test(message) + + test_output_desc += " [duration / 10]s" + if (test.succeeded) + log_world("[TEST_OUTPUT_GREEN("PASS")] [test_output_desc]") - test_results[test_path] = list("status" = test.succeeded ? UNIT_TEST_PASSED : UNIT_TEST_FAILED, "message" = message, "name" = test_path) + log_world("::endgroup::") + + if (!test.succeeded) + log_world("::error::[TEST_OUTPUT_RED("FAIL")] [test_output_desc]") + + var/final_status = test.succeeded ? UNIT_TEST_PASSED : UNIT_TEST_FAILED + test_results[test_path] = list("status" = final_status, "message" = message, "name" = test_path) qdel(test) @@ -141,6 +161,10 @@ GLOBAL_VAR(test_log) CHECK_TICK //We check tick first because the unit test we run last may be so expensive that checking tick will lock up this loop forever RunUnitTest(unit_path, test_results) + var/file_name = "data/unit_tests.json" + fdel(file_name) + file(file_name) << json_encode(test_results) + SSticker.force_ending = TRUE //We have to call this manually because del_text can preceed us, and SSticker doesn't fire in the post game SSticker.declare_completion() diff --git a/html/changelogs/AutoChangeLog-pr-3493.yml b/html/changelogs/AutoChangeLog-pr-3493.yml new file mode 100644 index 000000000000..e670f8c7359f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3493.yml @@ -0,0 +1,5 @@ +author: FalloutFalcon +changes: + - {code_imp: tweaks to the syntax of unit test logs to be more readable by humans + and flaky tests} +delete-after: true