From 15a4726d68e53304bb847dc05636601394a3e1d1 Mon Sep 17 00:00:00 2001 From: Salman Alshamrani Date: Thu, 28 Nov 2024 23:26:57 -0500 Subject: [PATCH] Refactor skin deserialisation tests to better support skin migration --- .../Skins/SkinDeserialisationTest.cs | 185 ----------------- .../Skins/TestSceneSkinDeserialisation.cs | 194 ++++++++++++++++++ 2 files changed, 194 insertions(+), 185 deletions(-) delete mode 100644 osu.Game.Tests/Skins/SkinDeserialisationTest.cs create mode 100644 osu.Game.Tests/Skins/TestSceneSkinDeserialisation.cs diff --git a/osu.Game.Tests/Skins/SkinDeserialisationTest.cs b/osu.Game.Tests/Skins/SkinDeserialisationTest.cs deleted file mode 100644 index 7372557161..0000000000 --- a/osu.Game.Tests/Skins/SkinDeserialisationTest.cs +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Linq; -using NUnit.Framework; -using osu.Framework.Audio.Sample; -using osu.Framework.Bindables; -using osu.Framework.Graphics.Textures; -using osu.Framework.IO.Stores; -using osu.Game.Audio; -using osu.Game.IO; -using osu.Game.IO.Archives; -using osu.Game.Screens.Menu; -using osu.Game.Screens.Play.HUD; -using osu.Game.Screens.Play.HUD.HitErrorMeters; -using osu.Game.Skinning; -using osu.Game.Skinning.Components; -using osu.Game.Tests.Resources; - -namespace osu.Game.Tests.Skins -{ - /// - /// Test that the main components (which are serialised based on namespace/class name) - /// remain compatible with any changes. - /// - /// - /// If this test breaks, check any naming or class structure changes. - /// Migration rules may need to be added to . - /// - [TestFixture] - public class SkinDeserialisationTest - { - private static readonly string[] available_skins = - { - // Covers song progress before namespace changes, and most other components. - "Archives/modified-default-20220723.osk", - "Archives/modified-classic-20220723.osk", - // Covers legacy song progress, UR counter, colour hit error metre. - "Archives/modified-classic-20220801.osk", - // Covers clicks/s counter - "Archives/modified-default-20220818.osk", - // Covers longest combo counter - "Archives/modified-default-20221012.osk", - // Covers Argon variant of song progress bar - "Archives/modified-argon-20221024.osk", - // Covers TextElement and BeatmapInfoDrawable - "Archives/modified-default-20221102.osk", - // Covers BPM counter. - "Archives/modified-default-20221205.osk", - // Covers judgement counter. - "Archives/modified-default-20230117.osk", - // Covers player avatar and flag. - "Archives/modified-argon-20230305.osk", - // Covers key counters - "Archives/modified-argon-pro-20230618.osk", - // Covers "Argon" health display - "Archives/modified-argon-pro-20231001.osk", - // Covers player name text component. - "Archives/modified-argon-20231106.osk", - // Covers "Argon" accuracy/score/combo counters, and wedges - "Archives/modified-argon-20231108.osk", - // Covers "Argon" performance points counter - "Archives/modified-argon-20240305.osk", - // Covers default rank display - "Archives/modified-default-20230809.osk", - // Covers legacy rank display - "Archives/modified-classic-20230809.osk", - // Covers legacy key counter - "Archives/modified-classic-20240724.osk" - }; - - /// - /// If this test fails, new test resources should be added to include new components. - /// - [Test] - public void TestSkinnableComponentsCoveredByDeserialisationTests() - { - HashSet instantiatedTypes = new HashSet(); - - foreach (string oskFile in available_skins) - { - using (var stream = TestResources.OpenResource(oskFile)) - using (var storage = new ZipArchiveReader(stream)) - { - var skin = new TestSkin(new SkinInfo(), null, storage); - - foreach (var target in skin.LayoutInfos) - { - foreach (var info in target.Value.AllDrawables) - instantiatedTypes.Add(info.Type); - } - } - } - - var editableTypes = SerialisedDrawableInfo.GetAllAvailableDrawables().Where(t => (Activator.CreateInstance(t) as ISerialisableDrawable)?.IsEditable == true); - - Assert.That(instantiatedTypes, Is.EquivalentTo(editableTypes)); - } - - [Test] - public void TestDeserialiseModifiedDefault() - { - using (var stream = TestResources.OpenResource("Archives/modified-default-20220723.osk")) - using (var storage = new ZipArchiveReader(stream)) - { - var skin = new TestSkin(new SkinInfo(), null, storage); - - Assert.That(skin.LayoutInfos, Has.Count.EqualTo(2)); - Assert.That(skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.ToArray(), Has.Length.EqualTo(9)); - } - } - - [Test] - public void TestDeserialiseModifiedArgon() - { - using (var stream = TestResources.OpenResource("Archives/modified-argon-20231106.osk")) - using (var storage = new ZipArchiveReader(stream)) - { - var skin = new TestSkin(new SkinInfo(), null, storage); - - Assert.That(skin.LayoutInfos, Has.Count.EqualTo(2)); - Assert.That(skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.ToArray(), Has.Length.EqualTo(10)); - Assert.That(skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.Select(i => i.Type), Contains.Item(typeof(PlayerName))); - } - } - - [Test] - public void TestDeserialiseInvalidDrawables() - { - using (var stream = TestResources.OpenResource("Archives/argon-invalid-drawable.osk")) - using (var storage = new ZipArchiveReader(stream)) - { - var skin = new TestSkin(new SkinInfo(), null, storage); - - Assert.That(skin.LayoutInfos.Any(kvp => kvp.Value.AllDrawables.Any(d => d.Type == typeof(StarFountain))), Is.False); - } - } - - [Test] - public void TestDeserialiseModifiedClassic() - { - using (var stream = TestResources.OpenResource("Archives/modified-classic-20220723.osk")) - using (var storage = new ZipArchiveReader(stream)) - { - var skin = new TestSkin(new SkinInfo(), null, storage); - - Assert.That(skin.LayoutInfos, Has.Count.EqualTo(2)); - Assert.That(skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.ToArray(), Has.Length.EqualTo(6)); - Assert.That(skin.LayoutInfos[GlobalSkinnableContainers.SongSelect].AllDrawables.ToArray(), Has.Length.EqualTo(1)); - - var skinnableInfo = skin.LayoutInfos[GlobalSkinnableContainers.SongSelect].AllDrawables.First(); - - Assert.That(skinnableInfo.Type, Is.EqualTo(typeof(SkinnableSprite))); - Assert.That(skinnableInfo.Settings.First().Key, Is.EqualTo("sprite_name")); - Assert.That(skinnableInfo.Settings.First().Value, Is.EqualTo("ppy_logo-2.png")); - } - - using (var stream = TestResources.OpenResource("Archives/modified-classic-20220801.osk")) - using (var storage = new ZipArchiveReader(stream)) - { - var skin = new TestSkin(new SkinInfo(), null, storage); - Assert.That(skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.ToArray(), Has.Length.EqualTo(8)); - Assert.That(skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.Select(i => i.Type), Contains.Item(typeof(UnstableRateCounter))); - Assert.That(skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.Select(i => i.Type), Contains.Item(typeof(ColourHitErrorMeter))); - Assert.That(skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.Select(i => i.Type), Contains.Item(typeof(LegacySongProgress))); - } - } - - private class TestSkin : Skin - { - public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? fallbackStore = null, string configurationFilename = "skin.ini") - : base(skin, resources, fallbackStore, configurationFilename) - { - } - - public override Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => throw new NotImplementedException(); - - public override IBindable GetConfig(TLookup lookup) => throw new NotImplementedException(); - - public override ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException(); - } - } -} diff --git a/osu.Game.Tests/Skins/TestSceneSkinDeserialisation.cs b/osu.Game.Tests/Skins/TestSceneSkinDeserialisation.cs new file mode 100644 index 0000000000..595180c27c --- /dev/null +++ b/osu.Game.Tests/Skins/TestSceneSkinDeserialisation.cs @@ -0,0 +1,194 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Game.Database; +using osu.Game.Screens.Menu; +using osu.Game.Screens.Play.HUD; +using osu.Game.Screens.Play.HUD.HitErrorMeters; +using osu.Game.Skinning; +using osu.Game.Skinning.Components; +using osu.Game.Tests.Resources; +using osu.Game.Tests.Visual; + +namespace osu.Game.Tests.Skins +{ + /// + /// Test that the main components (which are serialised based on namespace/class name) + /// remain compatible with any changes. + /// + /// + /// If this test breaks, check any naming or class structure changes. + /// Migration rules may need to be added to . + /// + public partial class TestSceneSkinDeserialisation : OsuTestScene + { + private static readonly string[] available_skins = + { + // Covers song progress before namespace changes, and most other components. + "Archives/modified-default-20220723.osk", + "Archives/modified-classic-20220723.osk", + // Covers legacy song progress, UR counter, colour hit error metre. + "Archives/modified-classic-20220801.osk", + // Covers clicks/s counter + "Archives/modified-default-20220818.osk", + // Covers longest combo counter + "Archives/modified-default-20221012.osk", + // Covers Argon variant of song progress bar + "Archives/modified-argon-20221024.osk", + // Covers TextElement and BeatmapInfoDrawable + "Archives/modified-default-20221102.osk", + // Covers BPM counter. + "Archives/modified-default-20221205.osk", + // Covers judgement counter. + "Archives/modified-default-20230117.osk", + // Covers player avatar and flag. + "Archives/modified-argon-20230305.osk", + // Covers key counters + "Archives/modified-argon-pro-20230618.osk", + // Covers "Argon" health display + "Archives/modified-argon-pro-20231001.osk", + // Covers player name text component. + "Archives/modified-argon-20231106.osk", + // Covers "Argon" accuracy/score/combo counters, and wedges + "Archives/modified-argon-20231108.osk", + // Covers "Argon" performance points counter + "Archives/modified-argon-20240305.osk", + // Covers default rank display + "Archives/modified-default-20230809.osk", + // Covers legacy rank display + "Archives/modified-classic-20230809.osk", + // Covers legacy key counter + "Archives/modified-classic-20240724.osk" + }; + + [Resolved] + private SkinManager skins { get; set; } = null!; + + /// + /// If this test fails, new test resources should be added to include new components. + /// + [Test] + public void TestSkinnableComponentsCoveredByDeserialisationTests() + { + HashSet instantiatedTypes = new HashSet(); + + AddStep("load skin", () => + { + foreach (string oskFile in available_skins) + { + var skin = importSkinFromArchives(oskFile); + + foreach (var target in skin.LayoutInfos) + { + foreach (var info in target.Value.AllDrawables) + instantiatedTypes.Add(info.Type); + } + } + }); + + var existingTypes = SerialisedDrawableInfo.GetAllAvailableDrawables().Where(t => (Activator.CreateInstance(t) as ISerialisableDrawable)?.IsEditable == true); + + AddAssert("all types available", () => instantiatedTypes, () => Is.EquivalentTo(existingTypes)); + } + + [Test] + public void TestDeserialiseModifiedDefault() + { + Skin skin = null!; + + AddStep("load skin", () => skin = importSkinFromArchives("Archives/modified-default-20220723.osk")); + + AddAssert("layouts count = 2", () => skin.LayoutInfos, () => Has.Count.EqualTo(2)); + AddAssert("hud count = 12", + () => skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.ToArray(), + () => Has.Length.EqualTo(12)); + } + + [Test] + public void TestDeserialiseModifiedArgon() + { + Skin skin = null!; + + AddStep("load skin", () => skin = importSkinFromArchives("Archives/modified-argon-20231106.osk")); + + AddAssert("layouts count = 2", () => skin.LayoutInfos, () => Has.Count.EqualTo(2)); + AddAssert("hud count = 13", + () => skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.ToArray(), + () => Has.Length.EqualTo(13)); + + AddAssert("hud contains player name", + () => skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.Select(i => i.Type), + () => Does.Contain(typeof(PlayerName))); + } + + [Test] + public void TestDeserialiseInvalidDrawables() + { + Skin skin = null!; + + AddStep("load skin", () => skin = importSkinFromArchives("Archives/argon-invalid-drawable.osk")); + + AddAssert("skin does not contain star fountain", + () => skin.LayoutInfos.SelectMany(kvp => kvp.Value.AllDrawables).Select(d => d.Type), + () => Does.Not.Contain(typeof(StarFountain))); + } + + [Test] + public void TestDeserialiseModifiedClassic() + { + Skin skin = null!; + + AddStep("load skin", () => skin = importSkinFromArchives("Archives/modified-classic-20220723.osk")); + + AddAssert("layouts count = 2", () => skin.LayoutInfos, () => Has.Count.EqualTo(2)); + AddAssert("hud count = 11", + () => skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.ToArray(), + () => Has.Length.EqualTo(11)); + + AddAssert("song select count = 1", + () => skin.LayoutInfos[GlobalSkinnableContainers.SongSelect].AllDrawables.ToArray(), + () => Has.Length.EqualTo(1)); + + AddAssert("song select component correct", () => + { + var skinnableInfo = skin.LayoutInfos[GlobalSkinnableContainers.SongSelect].AllDrawables.First(); + + Assert.That(skinnableInfo.Type, Is.EqualTo(typeof(SkinnableSprite))); + Assert.That(skinnableInfo.Settings.First().Key, Is.EqualTo("sprite_name")); + Assert.That(skinnableInfo.Settings.First().Value, Is.EqualTo("ppy_logo-2.png")); + return true; + }); + + AddStep("load another skin", () => skin = importSkinFromArchives("Archives/modified-classic-20220801.osk")); + + AddAssert("hud count = 13", + () => skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.ToArray(), + () => Has.Length.EqualTo(13)); + + AddAssert("hud contains ur counter", + () => skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.Select(i => i.Type), + () => Does.Contain(typeof(UnstableRateCounter))); + + AddAssert("hud contains colour hit error", + () => skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.Select(i => i.Type), + () => Does.Contain(typeof(ColourHitErrorMeter))); + + AddAssert("hud contains legacy song progress", + () => skin.LayoutInfos[GlobalSkinnableContainers.MainHUDComponents].AllDrawables.Select(i => i.Type), + () => Does.Contain(typeof(LegacySongProgress))); + } + + private Skin importSkinFromArchives(string filename) + { + var imported = skins.Import(new ImportTask(TestResources.OpenResource(filename), Path.GetFileNameWithoutExtension(filename))).GetResultSafely(); + return imported.PerformRead(skinInfo => skins.GetSkin(skinInfo)); + } + } +}