From 33f45b0a6d348ca2c2e9c6a9c6e6527e4eb52a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 24 Apr 2025 11:39:09 +0200 Subject: [PATCH 1/5] Add test scene coverage of HUD layouts on various ruleset/skin combinations As time goes on, default skin layouts are getting more and more complicated because of per-ruleset overrides. This was already sort of apparent in https://github.com/ppy/osu/pull/31527, and I'm about to make it worse, so before I do, this is a test scene that is supposed to make it easier to check all possible combinations at a glance. --- .../Gameplay/TestSceneHUDOverlayLayouts.cs | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayLayouts.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayLayouts.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayLayouts.cs new file mode 100644 index 0000000000..3b9fcd1102 --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayLayouts.cs @@ -0,0 +1,161 @@ +// 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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Track; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Rendering; +using osu.Framework.Graphics.Textures; +using osu.Framework.IO.Stores; +using osu.Framework.Platform; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Database; +using osu.Game.IO; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Spectator; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Scoring; +using osu.Game.Screens.Play; +using osu.Game.Screens.Select.Leaderboards; +using osu.Game.Skinning; +using osu.Game.Tests.Gameplay; +using osu.Game.Tests.Visual.Spectator; + +namespace osu.Game.Tests.Visual.Gameplay +{ + [System.ComponentModel.Description(@"Exercises the appearance of the HUD overlay on various skin and ruleset combinations.")] + public partial class TestSceneHUDOverlayRulesetLayouts : OsuTestScene, IStorageResourceProvider + { + private readonly Dictionary skins = new Dictionary(); + + [Resolved] + private GameHost host { get; set; } = null!; + + [Resolved] + private RulesetStore rulesets { get; set; } = null!; + + [Resolved] + private OsuConfigManager configManager { get; set; } = null!; + + [BackgroundDependencyLoader] + private void load() + { + skins["argon"] = new ArgonSkin(this); + skins["triangles"] = new TrianglesSkin(this); + skins["legacy"] = new DefaultLegacySkin(this); + } + + [Test] + public void TestLayout( + [Values("argon", "triangles", "legacy")] + string skinName, + [Values("osu", "taiko", "fruits", "mania")] + string rulesetName) + { + AddStep("create content", () => + { + var rulesetInfo = rulesets.GetRuleset(rulesetName); + var ruleset = rulesetInfo!.CreateInstance(); + var beatmap = ruleset.CreateBeatmapConverter(new Beatmap()).Convert(); + var drawableRuleset = ruleset.CreateDrawableRulesetWith(beatmap); + + var skin = skins[skinName]; + ISkin provider = ruleset.CreateSkinTransformer(skin, beatmap) ?? skin; + + var gameplayState = TestGameplayState.Create(ruleset); + ((Bindable)gameplayState.PlayingState).Value = LocalUserPlayingState.Playing; + var spectatorClient = new TestSpectatorClient(); + + for (int i = 0; i < 15; ++i) + { + ((ISpectatorClient)spectatorClient).UserStartedWatching([ + new SpectatorUser + { + OnlineID = i, + Username = $"User {i}" + } + ]); + } + + GameplayClockContainer gameplayClock; + + List<(Type, object)> dependencies = + [ + (typeof(GameplayState), gameplayState), + (typeof(ScoreProcessor), gameplayState.ScoreProcessor), + (typeof(HealthProcessor), gameplayState.HealthProcessor), + (typeof(IGameplayClock), gameplayClock = new GameplayClockContainer(new TrackVirtual(60000), false, false)), + (typeof(SpectatorClient), spectatorClient), + (typeof(IGameplayLeaderboardProvider), new TestGameplayLeaderboardProvider()), + ]; + + if (drawableRuleset is IDrawableScrollingRuleset scrolling) + dependencies.Add((typeof(IScrollingInfo), scrolling.ScrollingInfo)); + + Child = new DependencyProvidingContainer + { + RelativeSizeAxes = Axes.Both, + CachedDependencies = dependencies.ToArray(), + Children = new Drawable[] + { + spectatorClient, + new SkinProvidingContainer(provider) + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + drawableRuleset, + new HUDOverlay(drawableRuleset, []) + { + RelativeSizeAxes = Axes.Both, + } + } + } + } + }; + + gameplayClock.Start(); + }); + } + + private class TestGameplayLeaderboardProvider : IGameplayLeaderboardProvider + { + IBindableList IGameplayLeaderboardProvider.Scores => Scores; + public BindableList Scores { get; } = new BindableList(); + public bool IsPartial { get; } = false; + + public TestGameplayLeaderboardProvider() + { + for (int i = 0; i < 20; ++i) + { + Scores.Add(new GameplayLeaderboardScore(new ScoreInfo + { + User = new APIUser { Username = $"User {i}" }, + TotalScore = (20 - i) * 50_000, + Accuracy = i * 0.05, + Combo = i * 50 + }, i == 19)); + } + } + } + + #region IResourceStorageProvider + + public IRenderer Renderer => host.Renderer; + public AudioManager AudioManager => Audio; + public IResourceStore Files => null!; + public new IResourceStore Resources => base.Resources; + public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => host.CreateTextureLoaderStore(underlyingStore); + RealmAccess IStorageResourceProvider.RealmAccess => null!; + + #endregion + } +} From 9818f859cbc59c62724b7702d9821348fc7862a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 24 Apr 2025 13:19:31 +0200 Subject: [PATCH 2/5] Remove (currently) unused resolved dependency --- osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayLayouts.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayLayouts.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayLayouts.cs index 3b9fcd1102..ae6e297f96 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayLayouts.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayLayouts.cs @@ -14,7 +14,6 @@ using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Game.Beatmaps; -using osu.Game.Configuration; using osu.Game.Database; using osu.Game.IO; using osu.Game.Online.API.Requests.Responses; @@ -42,9 +41,6 @@ namespace osu.Game.Tests.Visual.Gameplay [Resolved] private RulesetStore rulesets { get; set; } = null!; - [Resolved] - private OsuConfigManager configManager { get; set; } = null!; - [BackgroundDependencyLoader] private void load() { From 0287ca285c5c5edd40519f44234db53cbdd80983 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Apr 2025 04:51:03 +0900 Subject: [PATCH 3/5] Fix filename not matching test scene --- ...UDOverlayLayouts.cs => TestSceneHUDOverlayRulesetLayouts.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Tests/Visual/Gameplay/{TestSceneHUDOverlayLayouts.cs => TestSceneHUDOverlayRulesetLayouts.cs} (97%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayLayouts.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayRulesetLayouts.cs similarity index 97% rename from osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayLayouts.cs rename to osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayRulesetLayouts.cs index ae6e297f96..249128565c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayLayouts.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayRulesetLayouts.cs @@ -30,7 +30,7 @@ using osu.Game.Tests.Visual.Spectator; namespace osu.Game.Tests.Visual.Gameplay { - [System.ComponentModel.Description(@"Exercises the appearance of the HUD overlay on various skin and ruleset combinations.")] + [Description(@"Exercises the appearance of the HUD overlay on various skin and ruleset combinations.")] public partial class TestSceneHUDOverlayRulesetLayouts : OsuTestScene, IStorageResourceProvider { private readonly Dictionary skins = new Dictionary(); From 54e0e1420fe45463e25e1bf451637a9a84978148 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Apr 2025 05:14:36 +0900 Subject: [PATCH 4/5] Fix `TriangleSkin` not always returning a container for `GlobalSkinnableContainerLookup` We have other safeties which mean that this is not an issue during gameplay, but in the new `TestSceneHUDOverlayRulesetLayouts` it became apparent that allowing this to fallback (via `null` return) could lead to weirdness. --- osu.Game/Skinning/TrianglesSkin.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/TrianglesSkin.cs b/osu.Game/Skinning/TrianglesSkin.cs index a4a967bed9..18ca7629d7 100644 --- a/osu.Game/Skinning/TrianglesSkin.cs +++ b/osu.Game/Skinning/TrianglesSkin.cs @@ -68,10 +68,6 @@ namespace osu.Game.Skinning switch (lookup) { case GlobalSkinnableContainerLookup containerLookup: - // Only handle global level defaults for now. - if (containerLookup.Ruleset != null) - return null; - switch (containerLookup.Lookup) { case GlobalSkinnableContainers.SongSelect: @@ -83,6 +79,11 @@ namespace osu.Game.Skinning return songSelectComponents; case GlobalSkinnableContainers.MainHUDComponents: + if (containerLookup.Ruleset != null) + { + return new DefaultSkinComponentsContainer(_ => { }); + } + var skinnableTargetWrapper = new DefaultSkinComponentsContainer(container => { var score = container.OfType().FirstOrDefault(); From 03014638107782c7cb4e1cbcfccda2c08c55cf5b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 26 Apr 2025 05:18:24 +0900 Subject: [PATCH 5/5] Adjust variables for legibility I found the previous way things were written a bit awkward. Easier to just enforce non-null here. --- .../Visual/Gameplay/TestSceneHUDOverlayRulesetLayouts.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayRulesetLayouts.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayRulesetLayouts.cs index 249128565c..1f883aa784 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayRulesetLayouts.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlayRulesetLayouts.cs @@ -63,8 +63,7 @@ namespace osu.Game.Tests.Visual.Gameplay var beatmap = ruleset.CreateBeatmapConverter(new Beatmap()).Convert(); var drawableRuleset = ruleset.CreateDrawableRulesetWith(beatmap); - var skin = skins[skinName]; - ISkin provider = ruleset.CreateSkinTransformer(skin, beatmap) ?? skin; + ISkin provider = ruleset.CreateSkinTransformer(skins[skinName], beatmap)!; var gameplayState = TestGameplayState.Create(ruleset); ((Bindable)gameplayState.PlayingState).Value = LocalUserPlayingState.Playing;