Skip to content
Merged

a #177

Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b08a86f
Remove left over DI usage
peppy Apr 5, 2026
9a56aed
Add current stage overlay to ranked play (#37202)
LiquidPL Apr 5, 2026
dfeb9d8
Remove unused lookup component
peppy Apr 5, 2026
db92681
Ignore one more false positive rider issue
peppy Apr 6, 2026
809298d
Turn tests green, for a short while, maybe (#37218)
bdach Apr 6, 2026
0f40d61
Fix skin editor origin dropdown options applying origin in wrong coor…
bdach Apr 6, 2026
ea0c04a
Ranked Play: fix cards not going all the way off-screen (#37222)
minetoblend Apr 6, 2026
e80e5f3
Ranked play general UI/UX fixes (#37220)
peppy Apr 7, 2026
a3a530c
Add method for silently dismissing progress notifications without com…
peppy Apr 7, 2026
c570db6
Add ability to search for users (#37225)
peppy Apr 7, 2026
84cce2f
Improve efficiency of tag population (#37228)
peppy Apr 7, 2026
ed84dd4
Tidy up track switching handling (#37230)
peppy Apr 7, 2026
97ddc8e
Fix footer buttons still being able to fire their action when tempora…
bdach Apr 7, 2026
2058297
Localise tabs on `DashboardOverlay` (#37236)
diquoks Apr 8, 2026
8c6818e
Add models for improvements to matchmaking lobby (#37226)
smoogipoo Apr 8, 2026
ca44d82
Do not send replay frames to spectator server if initial begin play i…
bdach Apr 8, 2026
b838564
Make `RankedPlayColourScheme` properly static (#37244)
peppy Apr 9, 2026
93b7c33
Add matchmaking profile badge (#37241)
smoogipoo Apr 9, 2026
9c43739
Improvements to discard screen UX (#37245)
peppy Apr 10, 2026
4b8fa89
Tidy up `ResultsScreen` (#37242)
peppy Apr 10, 2026
3e62330
Update editor icons (#37201)
LiquidPL Apr 10, 2026
6231e06
Fix lack of encode-decode stability when writing out mania beatmaps w…
bdach Apr 10, 2026
e36f9b2
Add more ranked play SFX (#37258)
nekodex Apr 11, 2026
bcc36b4
Fix missing null check
smoogipoo Apr 12, 2026
0e9664b
Fix multiple issues with ranked play card audio previews (#37262)
peppy Apr 12, 2026
ac620ee
Add additional information to the matchmaking queue screen (#37229)
smoogipoo Apr 14, 2026
16a2a96
Add bottom 'ornament' overlay to ranked play (#37288)
LiquidPL Apr 14, 2026
ca40749
Ranked Play: Fix chat temporarily appearing during intro (#37294)
smoogipoo Apr 14, 2026
daff79b
Ranked Play: Fix stutter when entering queue screen (#37295)
smoogipoo Apr 14, 2026
7c114cd
Update CodeFileSanity (#37293)
smoogipoo Apr 14, 2026
16882c6
Fix `KeyboardStep` precision in Accuracy Challenge (#37299)
diquoks Apr 15, 2026
4fd4544
Replicate osu!web logic in display of matchmaking badge (#37303)
smoogipoo Apr 15, 2026
bcf3712
Safeguard rating distribution graph against null InputManager (#37304)
smoogipoo Apr 15, 2026
8050ee3
Add support for grouping by keys in song select for osu!mania (#37285)
bdach Apr 15, 2026
e6a74fd
Ranked Play: Work around rating data not always including user (#37310)
smoogipoo Apr 16, 2026
96513ce
Add note on AI usage in contributions
peppy Apr 16, 2026
c2ed2f7
Update framework (#37315)
peppy Apr 16, 2026
9ac1ad2
Reduce log spam from spectator client when playing offline (#37316)
bdach Apr 16, 2026
964d3fb
Avoid moving auth files during storage migration
smoogipoo Apr 17, 2026
cb843b1
Limit maximum password length for multiplayer lobbies to conformant v…
peppy Apr 17, 2026
9bea319
Fix failing test
smoogipoo Apr 17, 2026
b36f8f9
Avoid recomputing full path vertices in `SliderPath` when possible (#…
EVAST9919 Apr 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ public virtual void Shake() { }
/// </summary>
public void MissForcefully() => ApplyMinResult();

// ReSharper disable once FunctionRecursiveOnAllPaths (TODO: remove after fixed https://youtrack.jetbrains.com/issue/RIDER-135036/Incorrect-recursive-on-all-execution-paths-inspection)
private RectangleF parentScreenSpaceRectangle => ((DrawableOsuHitObject)ParentHitObject)?.parentScreenSpaceRectangle ?? Parent!.ScreenSpaceDrawQuad.AABBFloat;

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
public partial class TrianglesPiece : Triangles
{
protected override bool CreateNewTriangles => false;
protected override float SpawnRatio => 0.5f;

public TrianglesPiece(int? seed = null)
: base(seed)
{
TriangleScale = 1.2f;
SpawnRatio = 0.5f;
HideAlphaDiscrepancies = false;
ClampAxes = Axes.None;
}
Expand Down
1 change: 1 addition & 0 deletions osu.Game.Tests/Beatmaps/TestSceneBeatmapDifficultyCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public void SetUpSteps()
}

[Test]
[FlakyTest] // one fix attempted in https://github.com/ppy/osu/pull/37178, didn't work
public void TestInvalidationFlow()
{
BeatmapInfo postEditBeatmapInfo = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ public void TestScoreUpgradeFailed(int scoreVersion)
}

[Test]
[FlakyTest]
public void TestCustomRulesetScoreNotSubjectToUpgrades([Values] bool available)
{
RulesetInfo rulesetInfo = null!;
Expand Down
1 change: 1 addition & 0 deletions osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ public void TestHitObjectPlacement()
}

[Test]
[FlakyTest]
public void TestLengthAndStarRatingUpdated()
{
WorkingBeatmap working = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public override void SetUpSteps()
}

[Test]
[FlakyTest]
public void TestLocallyModifyingOnlineBeatmap()
{
string initialHash = string.Empty;
Expand Down
13 changes: 7 additions & 6 deletions osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Linq;
using NUnit.Framework;
using osu.Framework.Extensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Database;
Expand Down Expand Up @@ -140,14 +139,16 @@ private void assertMovedScreenTo(EditorScreenMode screen, string text = "moved t

private void setUpEditor(RulesetInfo ruleset)
{
BeatmapSetInfo beatmapSet = null!;
BeatmapSetInfo? beatmapSet = null;

AddStep("Import test beatmap", () =>
Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely()
);
AddStep("Retrieve beatmap", () =>
beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach()
);
AddUntilStep("Retrieve beatmap", () =>
{
beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected)?.Value.Detach();
return beatmapSet != null;
});
AddStep("Present beatmap", () => Game.PresentBeatmap(beatmapSet));
AddUntilStep("Wait for song select", () =>
Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet)
Expand All @@ -157,7 +158,7 @@ private void setUpEditor(RulesetInfo ruleset)
AddStep("Switch ruleset", () => Game.Ruleset.Value = ruleset);
AddStep("Open editor for ruleset", () =>
((SoloSongSelect)Game.ScreenStack.CurrentScreen)
.Edit(beatmapSet.Beatmaps.Last(beatmap => beatmap.Ruleset.Name == ruleset.Name))
.Edit(beatmapSet!.Beatmaps.Last(beatmap => beatmap.Ruleset.Name == ruleset.Name))
);
AddUntilStep("Wait for editor open", () => editor?.ReadyForUse == true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public partial class TestSceneGameplaySamplePlayback : PlayerTestScene
private bool seek;

[Test]
[FlakyTest]
[Ignore("Still failing even with [FlakyTest] applied.")]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid indefinitely disabling this regression test.

Line 23 fully skips this test, which drops coverage for a known seek/audio edge case. Please attach a concrete tracking issue + exit criteria (or keep it flaky/quarantined instead of ignored).

Suggested direction
-[Ignore("Still failing even with [FlakyTest] applied.")]
+[FlakyTest] // TODO: replace with issue link and remove once fixed
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[Ignore("Still failing even with [FlakyTest] applied.")]
[FlakyTest] // TODO: replace with issue link and remove once fixed
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySamplePlayback.cs` at line
23, The test is being fully disabled via [Ignore] which removes coverage for the
regression in TestSceneGameplaySamplePlayback; replace the blanket ignore with a
quarantined/flaky marker (e.g., use [FlakyTest] or your project's quarantine
attribute) instead of [Ignore], add a short TODO comment above the
TestSceneGameplaySamplePlayback class/method referencing a concrete tracking
issue ID and clear exit criteria (what must be fixed to re-enable), and ensure
the test remains discoverable in CI reports so it can be retried/monitored
rather than permanently skipped.

public void TestAllSamplesStopDuringSeek()
{
DrawableSlider? slider = null;
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ public void TestLeaveNavigation()

AddStep("invoke on back button", () => multiplayerComponents.OnBackButton());

AddAssert("mod overlay is hidden", () => this.ChildrenOfType<MultiplayerUserModSelectOverlay>().Single().State.Value == Visibility.Hidden);
AddAssert("mod overlay is hidden", () => this.ChildrenOfType<MultiplayerUserModSelectOverlay>().All(o => o.State.Value == Visibility.Hidden));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Prevent vacuous pass in the mod overlay assertion.

On Line 665, All(...) passes for an empty sequence, so this test can succeed even when no MultiplayerUserModSelectOverlay exists. Require presence + hidden state to avoid false positives.

Suggested fix
-            AddAssert("mod overlay is hidden", () => this.ChildrenOfType<MultiplayerUserModSelectOverlay>().All(o => o.State.Value == Visibility.Hidden));
+            AddAssert("mod overlay is hidden", () =>
+            {
+                var overlays = this.ChildrenOfType<MultiplayerUserModSelectOverlay>().ToList();
+                return overlays.Count > 0 && overlays.All(o => o.State.Value == Visibility.Hidden);
+            });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
AddAssert("mod overlay is hidden", () => this.ChildrenOfType<MultiplayerUserModSelectOverlay>().All(o => o.State.Value == Visibility.Hidden));
AddAssert("mod overlay is hidden", () =>
{
var overlays = this.ChildrenOfType<MultiplayerUserModSelectOverlay>().ToList();
return overlays.Count > 0 && overlays.All(o => o.State.Value == Visibility.Hidden);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs` at line 665, The
current assertion uses All(...) which vacuously passes for an empty sequence;
update the AddAssert call that checks MultiplayerUserModSelectOverlay to require
that at least one overlay exists and that every overlay's State.Value ==
Visibility.Hidden (e.g. combine Any(...) or Count>0 with the existing All(...)
check) so the assertion only passes when overlays are present and hidden; locate
the AddAssert invocation referencing MultiplayerUserModSelectOverlay in
TestSceneMultiplayer and adjust its predicate accordingly.


AddAssert("dialog overlay is hidden", () => DialogOverlay.State.Value == Visibility.Hidden);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,7 @@ public void TestDeleteAllScoresAfterPlaying()
}

[Test]
[FlakyTest]
public void TestDeleteScoreAfterPlaying()
{
playToResults();
Expand Down
7 changes: 3 additions & 4 deletions osu.Game.Tests/Visual/Online/TestSceneFriendDisplay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Metadata;
Expand Down Expand Up @@ -219,13 +218,13 @@ public void TestLoadFriendsBeforeDisplay()
}

private void waitForLoad()
=> AddUntilStep("wait for panels to load", () => this.ChildrenOfType<LoadingSpinner>().First().State.Value, () => Is.EqualTo(Visibility.Hidden));
=> AddUntilStep("wait for panels to load", () => this.ChildrenOfType<UserPanel>().Any());

private void assertVisiblePanelCount<T>(int expectedVisible)
where T : UserPanel
{
AddAssert($"{typeof(T).ReadableName()}s in list", () => this.ChildrenOfType<FriendsList>().Last().ChildrenOfType<UserPanel>().All(p => p is T));
AddAssert($"{expectedVisible} panels visible", () => this.ChildrenOfType<FriendsList>().Last().ChildrenOfType<FriendsList.FilterableUserPanel>().Count(p => p.IsPresent),
AddUntilStep($"{typeof(T).ReadableName()}s in list", () => this.ChildrenOfType<FriendsList>().Last().ChildrenOfType<UserPanel>().All(p => p is T));
AddUntilStep($"{expectedVisible} panels visible", () => this.ChildrenOfType<FriendsList>().Last().ChildrenOfType<FriendsList.FilterableUserPanel>().Count(p => p.IsPresent),
() => Is.EqualTo(expectedVisible));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Testing;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays;
using osu.Game.Overlays.Profile;
using osu.Game.Overlays.Profile.Header.Components;
using osu.Game.Rulesets.Osu;
using osuTK;

namespace osu.Game.Tests.Visual.Online
{
public partial class TestSceneUserProfileMatchmakingStatsDisplay : OsuManualInputManagerTestScene
{
[Cached]
private readonly Bindable<UserProfileData?> userProfileData = new Bindable<UserProfileData?>(new UserProfileData(new APIUser(), new OsuRuleset().RulesetInfo));

[Cached]
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink);

[SetUpSteps]
public void SetUpSteps()
{
AddStep("create", () =>
{
Clear();
Add(new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourProvider.Background2,
});
Add(new MatchmakingStatsDisplay
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Scale = new Vector2(1f),
User = { BindTarget = userProfileData },
});
});

AddStep("set stats", () => userProfileData.Value = new UserProfileData(new APIUser
{
MatchmakingStatistics =
[
new APIUserMatchmakingStatistics
{
Plays = 10,
FirstPlacements = 8,
Rank = 1000,
Rating = 2000,
TotalPoints = 500,
Pool =
{
Name = "Active Pool"
}
},
new APIUserMatchmakingStatistics
{
Plays = 5,
FirstPlacements = 4,
Rank = 500,
Rating = 1000,
TotalPoints = 250,
Pool =
{
Name = "Inactive Pool"
}
},
new APIUserMatchmakingStatistics
{
Rating = 1500,
IsRatingProvisional = true,
Pool =
{
Name = "Provisional"
}
}
Comment on lines +57 to +82

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Test fixture doesn’t actually exercise pool-priority selection.

Lines 57-82 only set Pool.Name, but relevance sorting depends on Pool.Active and Pool.Id. This makes the test rely on defaults/insertion order and can miss ordering regressions.

🧪 Suggested fixture hardening
                     new APIUserMatchmakingStatistics
                     {
                         Plays = 10,
                         FirstPlacements = 8,
                         Rank = 1000,
                         Rating = 2000,
                         TotalPoints = 500,
                         Pool =
                         {
+                            Id = 10,
+                            Active = true,
                             Name = "Active Pool"
                         }
                     },
                     new APIUserMatchmakingStatistics
                     {
                         Plays = 5,
                         FirstPlacements = 4,
                         Rank = 500,
                         Rating = 1000,
                         TotalPoints = 250,
                         Pool =
                         {
+                            Id = 1,
+                            Active = false,
                             Name = "Inactive Pool"
                         }
                     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@osu.Game.Tests/Visual/Online/TestSceneUserProfileMatchmakingStatsDisplay.cs`
around lines 57 - 82, The test fixture sets only Pool.Name which doesn't
exercise pool-priority selection; update the three APIUserMatchmakingStatistics
instances in TestSceneUserProfileMatchmakingStatsDisplay (the objects with
properties Plays, FirstPlacements, Rank, Rating, TotalPoints,
IsRatingProvisional) to explicitly set Pool.Active and Pool.Id (use distinct ids
that reflect intended priority ordering, e.g., active pool Active=true Id=1,
inactive pool Active=false Id=2, provisional pool Active=true/false with its own
Id) so the relevance sorting logic depends on explicit Active/Id values rather
than insertion order or defaults.

]
}, new OsuRuleset().RulesetInfo));

AddStep("clear stats", () => userProfileData.Value = null);
}
}
}
24 changes: 24 additions & 0 deletions osu.Game.Tests/Visual/Online/TestSceneUserSearchDisplay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Overlays;
using osu.Game.Overlays.Dashboard.UserSearch;

namespace osu.Game.Tests.Visual.Online
{
public partial class TestSceneUserSearchDisplay : OsuTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);

protected override bool UseOnlineAPI => true;

[SetUp]
public void Setup() => Schedule(() =>
{
Child = new UserSearchDisplay();
});
}
}
11 changes: 9 additions & 2 deletions osu.Game.Tests/Visual/RankedPlay/TestSceneOpponentPickScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ public override void SetUpSteps()

AddStep("set pick state", () => MultiplayerClient.RankedPlayChangeStage(RankedPlayStage.CardPlay, state => state.ActiveUserId = 2).WaitSafely());

AddWaitStep("wait some", 5);

AddStep("reveal cards", () =>
{
for (int i = 0; i < 5; i++)
Expand All @@ -46,6 +44,15 @@ public override void SetUpSteps()
}).WaitSafely();
}
});

AddWaitStep("wait", 15);

AddStep("play beatmap", () => MultiplayerClient.PlayUserCard(2, hand => hand[0]).WaitSafely());
AddStep("reveal card", () => MultiplayerClient.RankedPlayRevealUserCard(2, hand => hand[0], new MultiplayerPlaylistItem
{
ID = 0,
BeatmapID = requestHandler.Beatmaps[0].OnlineID
}).WaitSafely());
}
}
}
29 changes: 29 additions & 0 deletions osu.Game.Tests/Visual/RankedPlay/TestScenePickScreen.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Linq;
using osu.Framework.Extensions;
using osu.Framework.Testing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.RankedPlay;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay;
using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand;
using osuTK.Input;

namespace osu.Game.Tests.Visual.RankedPlay
{
Expand Down Expand Up @@ -46,6 +51,30 @@ public override void SetUpSteps()
}).WaitSafely();
}
});

for (int i = 0; i < 3; i++)
{
int i2 = i;
AddStep($"click card {i2}", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<PlayerHandOfCards.PlayerHandCard>().ElementAt(i2));
InputManager.Click(MouseButton.Left);
});
}

AddWaitStep("wait", 3);

AddStep("click play button", () =>
{
var button = screen
.ChildrenOfType<PlayerHandOfCards.PlayerHandCard>()
.First(it => it.Selected)
.ChildrenOfType<ShearedButton>()
.First();

InputManager.MoveMouseTo(button);
InputManager.Click(MouseButton.Left);
});
Comment on lines +55 to +77

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add deterministic readiness checks before indexed/First() UI access.

Line 60 (ElementAt(i2)) and Lines 71-73 (First(...)/First()) can intermittently throw when the drawable tree is not ready yet; Line 65’s fixed wait does not guarantee readiness and makes the test flaky on CI.

🔧 Proposed stabilization
+            AddUntilStep("at least 3 cards visible",
+                () => this.ChildrenOfType<PlayerHandOfCards.PlayerHandCard>().Count() >= 3);
+
             for (int i = 0; i < 3; i++)
             {
                 int i2 = i;
                 AddStep($"click card {i2}", () =>
                 {
-                    InputManager.MoveMouseTo(this.ChildrenOfType<PlayerHandOfCards.PlayerHandCard>().ElementAt(i2));
+                    var card = this.ChildrenOfType<PlayerHandOfCards.PlayerHandCard>().ElementAt(i2);
+                    InputManager.MoveMouseTo(card);
                     InputManager.Click(MouseButton.Left);
                 });
             }
 
-            AddWaitStep("wait", 3);
+            AddUntilStep("selected card has play button", () =>
+                screen.ChildrenOfType<PlayerHandOfCards.PlayerHandCard>()
+                      .Any(c => c.Selected && c.ChildrenOfType<ShearedButton>().Any()));
 
             AddStep("click play button", () =>
             {
-                var button = screen
-                             .ChildrenOfType<PlayerHandOfCards.PlayerHandCard>()
-                             .First(it => it.Selected)
-                             .ChildrenOfType<ShearedButton>()
-                             .First();
+                var selectedCard = screen.ChildrenOfType<PlayerHandOfCards.PlayerHandCard>().First(c => c.Selected);
+                var button = selectedCard.ChildrenOfType<ShearedButton>().First();
 
                 InputManager.MoveMouseTo(button);
                 InputManager.Click(MouseButton.Left);
             });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (int i = 0; i < 3; i++)
{
int i2 = i;
AddStep($"click card {i2}", () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<PlayerHandOfCards.PlayerHandCard>().ElementAt(i2));
InputManager.Click(MouseButton.Left);
});
}
AddWaitStep("wait", 3);
AddStep("click play button", () =>
{
var button = screen
.ChildrenOfType<PlayerHandOfCards.PlayerHandCard>()
.First(it => it.Selected)
.ChildrenOfType<ShearedButton>()
.First();
InputManager.MoveMouseTo(button);
InputManager.Click(MouseButton.Left);
});
AddUntilStep("at least 3 cards visible",
() => this.ChildrenOfType<PlayerHandOfCards.PlayerHandCard>().Count() >= 3);
for (int i = 0; i < 3; i++)
{
int i2 = i;
AddStep($"click card {i2}", () =>
{
var card = this.ChildrenOfType<PlayerHandOfCards.PlayerHandCard>().ElementAt(i2);
InputManager.MoveMouseTo(card);
InputManager.Click(MouseButton.Left);
});
}
AddUntilStep("selected card has play button", () =>
screen.ChildrenOfType<PlayerHandOfCards.PlayerHandCard>()
.Any(c => c.Selected && c.ChildrenOfType<ShearedButton>().Any()));
AddStep("click play button", () =>
{
var selectedCard = screen.ChildrenOfType<PlayerHandOfCards.PlayerHandCard>().First(c => c.Selected);
var button = selectedCard.ChildrenOfType<ShearedButton>().First();
InputManager.MoveMouseTo(button);
InputManager.Click(MouseButton.Left);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@osu.Game.Tests/Visual/RankedPlay/TestScenePickScreen.cs` around lines 55 -
77, The test uses fixed waits and directly calls ElementAt(i2) and First(...) on
the drawable tree which can be null/unready; replace the fixed AddWaitStep and
direct indexed/First access with deterministic readiness checks (e.g.,
AddUntilStep/AddWaitUntil or WaitForCondition) that wait until
screen.ChildrenOfType<PlayerHandOfCards.PlayerHandCard>().Count() > i2 before
clicking in the loop, and wait until
screen.ChildrenOfType<PlayerHandOfCards.PlayerHandCard>().Any(it => it.Selected)
and that the selected card has a child ShearedButton available before calling
First()/clicking; keep using InputManager.MoveMouseTo/Click but only after those
conditions are satisfied.

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public partial class TestSceneRankedPlayBackground : OsuTestScene

private readonly Bindable<Colour4> gradientOuter = new Bindable<Colour4>(Color4Extensions.FromHex("AC6D97"));
private readonly Bindable<Colour4> gradientInner = new Bindable<Colour4>(Color4Extensions.FromHex("544483"));
private readonly Bindable<Colour4> dots = new Bindable<Colour4>(Color4Extensions.FromHex("D56CF6"));

public TestSceneRankedPlayBackground()
{
Expand All @@ -40,11 +39,6 @@ public TestSceneRankedPlayBackground()
Scale = new Vector2(0.4f),
Current = gradientInner,
},
new BasicColourPicker
{
Scale = new Vector2(0.4f),
Current = dots,
}
]
}
];
Expand All @@ -54,9 +48,8 @@ protected override void LoadComplete()
{
base.LoadComplete();

gradientOuter.BindValueChanged(e => background.GradientOutside = e.NewValue, true);
gradientInner.BindValueChanged(e => background.GradientInside = e.NewValue, true);
dots.BindValueChanged(e => background.DotsColour = e.NewValue, true);
gradientOuter.BindValueChanged(e => background.GradientBottom = e.NewValue, true);
gradientInner.BindValueChanged(e => background.GradientTop = e.NewValue, true);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay;
using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Components;
using osu.Game.Tests.Visual.Multiplayer;
Expand All @@ -24,18 +25,18 @@ public override void SetUpSteps()
{
Children =
[
new RankedPlayCornerPiece(RankedPlayColourScheme.Blue, Anchor.BottomLeft)
new RankedPlayCornerPiece(RankedPlayColourScheme.BLUE, Anchor.BottomLeft)
{
State = { BindTarget = visibility },
Child = new RankedPlayUserDisplay(2, Anchor.BottomLeft, RankedPlayColourScheme.Blue)
Child = new RankedPlayUserDisplay(new APIUser { Id = 2, Username = "peppy" }, Anchor.BottomLeft, RankedPlayColourScheme.BLUE)
{
RelativeSizeAxes = Axes.Both,
}
},
new RankedPlayCornerPiece(RankedPlayColourScheme.Red, Anchor.TopRight)
new RankedPlayCornerPiece(RankedPlayColourScheme.RED, Anchor.TopRight)
{
State = { BindTarget = visibility },
Child = new RankedPlayUserDisplay(2, Anchor.TopRight, RankedPlayColourScheme.Red)
Child = new RankedPlayUserDisplay(new APIUser { Id = 2, Username = "peppy" }, Anchor.TopRight, RankedPlayColourScheme.RED)
{
RelativeSizeAxes = Axes.Both,
}
Expand Down
Loading