From 090fe44f860e76c7713c75d05e96428061c13032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 3 Apr 2026 17:17:31 +0200 Subject: [PATCH] Assorted test deflaking (#37191) A few quick ones from https://github.com/ppy/osu/issues/37190. ## [Rewrite `TestSceneDeleteLocalScore` to have less context menu containers (and hopefully no longer flake)](https://github.com/ppy/osu/commit/ea0bc5b72374056d1749f8058f4874b7ef66a241) As seen in https://github.com/ppy/osu/actions/runs/23890777748#user-content-r3s3. This is a speculative fix but I'm feeling somewhat confident about this one. `BeatmapLeaderboardWedge` has TWO separate `ContextMenuContainer`s itself, and the test mentioned here was bringing a third. I have a feeling that the test flaking may have something to do with the fact that the test logic would attempt to click a menu item on specifically ONE of the three context menus. My bet is that when it fails, it's because it's trying the wrong one, but I don't have reproduction. ## [Wait for text to appear in flaking `TestSceneDrawableRoomPlaylist.TestSelectableMouseHandling` test](https://github.com/ppy/osu/commit/9d62eea8b56a0eb013e8116db0d38424b500876f) As seen in https://github.com/ppy/osu/actions/runs/23888446400#user-content-r1s1. Speculative. Banking on the fact that it takes time to load the sprite texts. ## [Remove all tests from `TestSceneWikiMarkdownContainer` dependent on existence of wiki on dev](https://github.com/ppy/osu/commit/8f319f91c98510b84e915bb61ec147618a584ce1) Deletes a flaky as seen in https://github.com/ppy/osu/actions/runs/23878899702#user-content-r2s2. The year is 2026 and LLM scrapers hammer [the entire](https://sourcehut.org/blog/2025-04-15-you-cannot-have-our-users-data/) [internet](https://blog.metabrainz.org/2025/12/11/we-cant-have-nice-things-because-of-ai-scrapers/) all over to scrape whatever ounce of Human Content there is left to feed the Moloch so that it can regurgitate it back in the form of The Most Average Speech You've Ever Read. We are not immune to this, and as such the LLM homunculi have hit the dev.ppy.sh wiki instance enough times for it to just completely [be banished to the blagole](https://www.youtube.com/watch?v=AfA_2Ku1aJY). Which means I get to freely delete flaky tests that should never have been running as part of CI because they're completely useless now and it's not like we're ever turning them back on again. ## [Use equality check in `TestSceneBeatmapCarouselScrolling.TestScrollPositionMaintainedOnRemove_SecondSelected` that's less sensitive to floating point](https://github.com/ppy/osu/commit/6f2a1de58f7e816830c430e89117ee4c594f529d) As seen in https://github.com/ppy/osu/actions/runs/23826420794#user-content-r0s1. --- .../TestSceneDrawableRoomPlaylist.cs | 4 +- .../Online/TestSceneWikiMarkdownContainer.cs | 88 ------------------- .../TestSceneBeatmapCarouselScrolling.cs | 7 +- .../TestSceneDeleteLocalScore.cs | 33 +++---- 4 files changed, 21 insertions(+), 111 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index c8216c54be..28780fdfd2 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -331,7 +331,9 @@ namespace osu.Game.Tests.Visual.Multiplayer p.RequestResults = _ => resultsRequested = true; }); - AddUntilStep("wait for load", () => playlist.ChildrenOfType().Any() && playlist.ChildrenOfType().First().DrawWidth > 0); + AddUntilStep("wait for load", () => playlist.ChildrenOfType().Any() + && playlist.ChildrenOfType().First().ChildrenOfType().Any() + && playlist.ChildrenOfType().First().DrawWidth > 0); AddStep("move mouse to first item title", () => InputManager.MoveMouseTo(playlist.ChildrenOfType().First().ChildrenOfType().First())); AddAssert("first item title not hovered", () => playlist.ChildrenOfType().First().IsHovered, () => Is.False); diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index 243a22243e..434b2925b6 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; -using osu.Framework.Utils; using osu.Game.Graphics.Containers.Markdown; using osu.Game.Graphics.Containers.Markdown.Footnotes; using osu.Game.Overlays; @@ -143,98 +142,11 @@ outdated: true # not sure about the format for ""list of mods"". AddAssert("No notice box visible", () => !markdownContainer.ChildrenOfType().Any()); } - [Test] - public void TestAbsoluteImage() - { - AddStep("Add absolute image", () => - { - markdownContainer.CurrentPath = "https://dev.ppy.sh"; - markdownContainer.Text = "![intro](/wiki/images/Client/Interface/img/intro-screen.jpg)"; - }); - } - - [Test] - public void TestRelativeImage() - { - AddStep("Add relative image", () => - { - markdownContainer.CurrentPath = "https://dev.ppy.sh/wiki/Interface/"; - markdownContainer.Text = "![intro](../images/Client/Interface/img/intro-screen.jpg)"; - }); - } - - [Test] - public void TestBlockImage() - { - AddStep("Add paragraph with block image", () => - { - markdownContainer.CurrentPath = "https://dev.ppy.sh/wiki/Interface/"; - markdownContainer.Text = @"Line before image - -![play menu](../images/Client/Interface/img/play-menu.jpg ""Main Menu in osu!"") - -Line after image"; - }); - } - - [Test] - public void TestInlineImage() - { - AddStep("Add inline image", () => - { - markdownContainer.CurrentPath = "https://dev.ppy.sh"; - markdownContainer.Text = "![osu! mode icon](/wiki/shared/mode/osu.png) osu!"; - }); - } - - [Test] - public void TestTableWithImageContent() - { - AddStep("Add Table", () => - { - markdownContainer.CurrentPath = "https://dev.ppy.sh"; - markdownContainer.Text = @" -| Image | Name | Effect | -| :-: | :-: | :-- | -| ![](/wiki/images/shared/judgement/osu!/hit300.png ""300"") | 300 | A possible score when tapping a hit circle precisely on time, completing a Slider and keeping the cursor over every tick, or completing a Spinner with the Spinner Metre full. A score of 300 appears in an blue score by default. Scoring nothing except 300s in a beatmap will award the player with the SS or SSH grade. | -| ![](/wiki/images/shared/judgement/osu!/hit300g.png ""Geki"") | (激) Geki | A term from Ouendan, called Elite Beat! in EBA. Appears when playing the last element in a combo in which the player has scored only 300s. Getting a Geki will give a sizable boost to the Life Bar. By default, it is blue. | -| ![](/wiki/images/shared/judgement/osu!/hit100.png ""100"") | 100 | A possible score one can get when tapping a Hit Object slightly late or early, completing a Slider and missing a number of ticks, or completing a Spinner with the Spinner Meter almost full. A score of 100 appears in a green score by default. When very skilled players test a beatmap and they get a lot of 100s, this may mean that the beatmap does not have correct timing. | -| ![](/wiki/images/shared/judgement/osu!/hit300k.png ""300 Katu"") ![](/wiki/Skinning/Interface/img/hit100k.png ""100 Katu"") | (喝) Katu or Katsu | A term from Ouendan, called Beat! in EBA. Appears when playing the last element in a combo in which the player has scored at least one 100, but no 50s or misses. Getting a Katu will give a small boost to the Life Bar. By default, it is coloured green or blue depending on whether the Katu itself is a 100 or a 300. | -| ![](/wiki/images/shared/judgement/osu!/hit50.png ""50"") | 50 | A possible score one can get when tapping a hit circle rather early or late but not early or late enough to cause a miss, completing a Slider and missing a lot of ticks, or completing a Spinner with the Spinner Metre close to full. A score of 50 appears in a orange score by default. Scoring a 50 in a combo will prevent the appearance of a Katu or a Geki at the combo's end. | -| ![](/wiki/images/shared/judgement/osu!/hit0.png ""Miss"") | Miss | A possible score one can get when not tapping a hit circle or too early (based on OD and AR, it may *shake* instead), not tapping or holding the Slider at least once, or completing a Spinner with low Spinner Metre fill. Scoring a Miss will reset the current combo to 0 and will prevent the appearance of a Katu or a Geki at the combo's end. | -"; - }); - } - - [Test] - public void TestWideImageNotExceedContainer() - { - AddStep("Add image", () => - { - markdownContainer.CurrentPath = "https://dev.ppy.sh/wiki/osu!_Program_Files/"; - markdownContainer.Text = "![](../images/Client/Program_files/img/file_structure.jpg \"The file structure of osu!'s installation folder, on Windows and macOS\")"; - }); - - AddUntilStep("Wait image to load", () => markdownContainer.ChildrenOfType().First().DelayedLoadCompleted); - - AddStep("Change container width", () => - { - markdownContainer.Width = 0.5f; - }); - - AddAssert("Image not exceed container width", () => - { - var spriteImage = markdownContainer.ChildrenOfType().First(); - return Precision.DefinitelyBigger(markdownContainer.DrawWidth, spriteImage.DrawWidth); - }); - } - [Test] public void TestFlag() { AddStep("Add flag", () => { - markdownContainer.CurrentPath = @"https://dev.ppy.sh"; markdownContainer.Text = "::{flag=\"AU\"}:: ::{flag=\"ZZ\"}::"; }); AddAssert("Two flags visible", () => markdownContainer.ChildrenOfType().Count(), () => Is.EqualTo(2)); diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselScrolling.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselScrolling.cs index 319803ef5f..8a97e1ce09 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselScrolling.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarouselScrolling.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics.Primitives; using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Screens.Select; namespace osu.Game.Tests.Visual.SongSelect @@ -37,7 +38,11 @@ namespace osu.Game.Tests.Visual.SongSelect WaitForFiltering(); AddAssert("select screen position unchanged", () => Carousel.ChildrenOfType().Single(p => p.Selected.Value).ScreenSpaceDrawQuad, - () => Is.EqualTo(positionBefore)); + () => Is.EqualTo(positionBefore).Using((expected, actual) + => Precision.AlmostEquals(expected.TopLeft, actual.TopLeft) + && Precision.AlmostEquals(expected.TopRight, actual.TopRight) + && Precision.AlmostEquals(expected.BottomLeft, actual.BottomLeft) + && Precision.AlmostEquals(expected.BottomRight, actual.BottomRight))); } [Test] diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs index 5eec60e9ec..f30c5a61cc 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDeleteLocalScore.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -11,13 +9,11 @@ using osu.Framework.Audio; using osu.Framework.Extensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Cursor; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Database; -using osu.Game.Graphics.Cursor; using osu.Game.Graphics.UserInterface; using osu.Game.Models; using osu.Game.Online.API.Requests.Responses; @@ -35,18 +31,17 @@ namespace osu.Game.Tests.Visual.UserInterface { public partial class TestSceneDeleteLocalScore : OsuManualInputManagerTestScene { - private readonly ContextMenuContainer contextMenuContainer; private readonly BeatmapLeaderboardWedge leaderboard; private RulesetStore rulesets = null!; - private BeatmapManager beatmapManager; - private ScoreManager scoreManager; + private BeatmapManager beatmapManager = null!; + private ScoreManager scoreManager = null!; private readonly List importedScores = new List(); - private BeatmapInfo beatmapInfo; + private BeatmapInfo beatmapInfo = null!; - private LeaderboardManager leaderboardManager { get; set; } + private LeaderboardManager leaderboardManager { get; set; } = null!; [Cached] private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine); @@ -60,15 +55,11 @@ namespace osu.Game.Tests.Visual.UserInterface { Children = new Drawable[] { - contextMenuContainer = new OsuContextMenuContainer + leaderboard = new BeatmapLeaderboardWedge { - RelativeSizeAxes = Axes.Both, - Child = leaderboard = new BeatmapLeaderboardWedge - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Size = new Vector2(0.6f), - } + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(0.6f), }, dialogOverlay = new DialogOverlay() }; @@ -145,7 +136,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestDeleteViaRightClick() { - ScoreInfo scoreBeingDeleted = null; + ScoreInfo scoreBeingDeleted = null!; AddStep("open menu for top score", () => { var leaderboardScore = leaderboard.ChildrenOfType().First(); @@ -157,12 +148,12 @@ namespace osu.Game.Tests.Visual.UserInterface }); // Ensure the context menu has finished showing - AddStep("finish transforms", () => contextMenuContainer.FinishTransforms(true)); + AddStep("finish transforms", () => leaderboard.FinishTransforms(true)); AddStep("click delete option", () => { - InputManager.MoveMouseTo(contextMenuContainer.ChildrenOfType() - .First(i => string.Equals(i.Item.Text.Value.ToString(), "delete", System.StringComparison.OrdinalIgnoreCase))); + InputManager.MoveMouseTo(leaderboard.ChildrenOfType() + .First(i => string.Equals(i.Item.Text.Value.ToString(), "delete", System.StringComparison.OrdinalIgnoreCase))); InputManager.Click(MouseButton.Left); });