mirror of
https://github.com/ppy/osu.git
synced 2026-05-18 02:49:53 +08:00
f1ea3b354c
Resolves #37486 Original error log: ``` 2026-04-22 21:47:58 [error]: System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index') 2026-04-22 21:47:58 [error]: at System.Collections.Generic.List`1.get_Item(Int32 index) 2026-04-22 21:47:58 [error]: at osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand.PlayerHandOfCards.moveCardFocus(Int32 direction) 2026-04-22 21:47:58 [error]: at osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand.PlayerHandOfCards.OnKeyDown(KeyDownEvent e) ``` Currently, ```osu.Game\Screens\OnlinePlay\Matchmaking\RankedPlay\Hand\PlayerHandOfCards.cs::moveCardFocus``` does not account for the hand being empty (cards.Count ==0 ), and will attempt to move the card focus in the given direction regardless, which causes the above index out of range error. Added empty hand check to moveCardFocus, and a matching test case to TestScenePlayerCardHand.cs New test case before changes, recreating the error: <img width="986" height="232" alt="image" src="https://github.com/user-attachments/assets/daa62081-c776-44bd-b0d2-382b2dac7938" /> After: <img width="638" height="223" alt="image" src="https://github.com/user-attachments/assets/d6dcd8b8-8caf-42e3-9999-93dfe3fb6452" />
224 lines
9.4 KiB
C#
224 lines
9.4 KiB
C#
// 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 Humanizer;
|
|
using NUnit.Framework;
|
|
using osu.Framework.Allocation;
|
|
using osu.Framework.Graphics;
|
|
using osu.Framework.Testing;
|
|
using osu.Game.Online.Multiplayer.MatchTypes.RankedPlay;
|
|
using osu.Game.Overlays;
|
|
using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay;
|
|
using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand;
|
|
using osuTK.Input;
|
|
|
|
namespace osu.Game.Tests.Visual.RankedPlay
|
|
{
|
|
public partial class TestScenePlayerCardHand : OsuManualInputManagerTestScene
|
|
{
|
|
[Cached]
|
|
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple);
|
|
|
|
private PlayerHandOfCards handOfCards = null!;
|
|
|
|
[BackgroundDependencyLoader]
|
|
private void load()
|
|
{
|
|
Child = handOfCards = new PlayerHandOfCards
|
|
{
|
|
Anchor = Anchor.BottomCentre,
|
|
Origin = Anchor.BottomCentre,
|
|
RelativeSizeAxes = Axes.Both,
|
|
Height = 0.5f,
|
|
};
|
|
}
|
|
|
|
[SetUpSteps]
|
|
public void SetupSteps()
|
|
{
|
|
AddStep("reset card hand", () => Child = handOfCards = new PlayerHandOfCards
|
|
{
|
|
Anchor = Anchor.BottomCentre,
|
|
Origin = Anchor.BottomCentre,
|
|
RelativeSizeAxes = Axes.Both,
|
|
Height = 0.5f,
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public void TestSingleSelectionMode()
|
|
{
|
|
AddStep("add cards", () =>
|
|
{
|
|
for (int i = 0; i < 5; i++)
|
|
handOfCards.AddCard(new RankedPlayCardWithPlaylistItem(new RankedPlayCardItem()));
|
|
});
|
|
AddStep("single selection mode", () => handOfCards.SelectionMode = HandSelectionMode.Single);
|
|
|
|
AddStep("click first card", () => handOfCards.GetCardsInDisplayOrder()[0].TriggerClick());
|
|
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[0].Item]));
|
|
|
|
AddStep("click second card", () => handOfCards.GetCardsInDisplayOrder()[1].TriggerClick());
|
|
AddAssert("second card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[1].Item]));
|
|
|
|
AddStep("click second card again", () => handOfCards.GetCardsInDisplayOrder()[1].TriggerClick());
|
|
AddAssert("second card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[1].Item]));
|
|
}
|
|
|
|
[Test]
|
|
public void TestMultiSelectionMode()
|
|
{
|
|
AddStep("add cards", () =>
|
|
{
|
|
for (int i = 0; i < 5; i++)
|
|
handOfCards.AddCard(new RankedPlayCardWithPlaylistItem(new RankedPlayCardItem()));
|
|
});
|
|
AddStep("multi selection mode", () => handOfCards.SelectionMode = HandSelectionMode.Multiple);
|
|
|
|
AddStep("click first card", () => handOfCards.GetCardsInDisplayOrder().First().TriggerClick());
|
|
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder().First().Item]));
|
|
|
|
AddStep("click second card", () => handOfCards.GetCardsInDisplayOrder()[1].TriggerClick());
|
|
AddAssert("both cards selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[0].Item, handOfCards.GetCardsInDisplayOrder()[1].Item]));
|
|
|
|
AddStep("click second card again", () => handOfCards.GetCardsInDisplayOrder()[1].TriggerClick());
|
|
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[0].Item]));
|
|
}
|
|
|
|
[Test]
|
|
public void TestCardCount()
|
|
{
|
|
for (int i = 1; i <= 8; i++)
|
|
{
|
|
int numCards = i;
|
|
|
|
AddStep($"{i} {"cards".Pluralize(i == 1)}", () =>
|
|
{
|
|
Child = handOfCards = new PlayerHandOfCards
|
|
{
|
|
Anchor = Anchor.BottomCentre,
|
|
Origin = Anchor.BottomCentre,
|
|
RelativeSizeAxes = Axes.Both,
|
|
Height = 0.5f,
|
|
};
|
|
|
|
for (int j = 0; j < numCards; j++)
|
|
handOfCards.AddCard(new RankedPlayCardWithPlaylistItem(new RankedPlayCardItem()));
|
|
});
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void TestKeyboardSelectionSingleSelection()
|
|
{
|
|
bool playActionTriggered = false;
|
|
|
|
AddStep("add cards", () =>
|
|
{
|
|
playActionTriggered = false;
|
|
handOfCards.PlayCardAction = () => playActionTriggered = true;
|
|
|
|
handOfCards.Clear();
|
|
for (int i = 0; i < 5; i++)
|
|
handOfCards.AddCard(new RankedPlayCardWithPlaylistItem(new RankedPlayCardItem()));
|
|
});
|
|
AddStep("single selection mode", () => handOfCards.SelectionMode = HandSelectionMode.Single);
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
int i1 = i;
|
|
Key key = Key.Number1 + i;
|
|
|
|
AddStep($"key {i + 1}", () => InputManager.Key(key));
|
|
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[i1].Item]));
|
|
}
|
|
|
|
AddStep("right arrow", () => InputManager.Key(Key.Right));
|
|
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[0].Item]));
|
|
|
|
AddStep("right arrow", () => InputManager.Key(Key.Right));
|
|
AddAssert("second card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[1].Item]));
|
|
|
|
AddStep("left arrow", () => InputManager.Key(Key.Left));
|
|
AddAssert("first card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[0].Item]));
|
|
|
|
AddStep("left arrow", () => InputManager.Key(Key.Left));
|
|
AddAssert("last card selected", () => handOfCards.Selection.SequenceEqual([handOfCards.GetCardsInDisplayOrder()[^1].Item]));
|
|
|
|
AddStep("space", () => InputManager.Key(Key.Space));
|
|
AddAssert("play action triggered", () => playActionTriggered);
|
|
}
|
|
|
|
[Test]
|
|
public void TestKeyboardSelectionMultiSelection()
|
|
{
|
|
AddStep("add cards", () =>
|
|
{
|
|
for (int i = 0; i < 5; i++)
|
|
handOfCards.AddCard(new RankedPlayCardWithPlaylistItem(new RankedPlayCardItem()));
|
|
});
|
|
AddStep("multi selection mode", () => handOfCards.SelectionMode = HandSelectionMode.Multiple);
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
int i1 = i;
|
|
Key key = Key.Number1 + i;
|
|
|
|
AddStep($"key {i + 1}", () => InputManager.Key(key));
|
|
AddAssert("card hovered", () => handOfCards.GetCardsInDisplayOrder()[i1].CardHovered);
|
|
|
|
AddAssert("card not selected", () => !handOfCards.Selection.Contains(handOfCards.GetCardsInDisplayOrder()[i1].Card.Item));
|
|
AddStep("space", () => InputManager.Key(Key.Space));
|
|
AddAssert("card selected", () => handOfCards.Selection.Contains(handOfCards.GetCardsInDisplayOrder()[i1].Card.Item));
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void TestContract()
|
|
{
|
|
AddStep("add cards", () =>
|
|
{
|
|
for (int i = 0; i < 5; i++)
|
|
handOfCards.AddCard(new RankedPlayCardWithPlaylistItem(new RankedPlayCardItem()));
|
|
});
|
|
AddWaitStep("wait", 5);
|
|
AddStep("contract", () => handOfCards.Contract());
|
|
AddWaitStep("wait", 5);
|
|
AddAssert(
|
|
"all cards outside bounds", () =>
|
|
handOfCards
|
|
.ChildrenOfType<HandOfCards.HandCard>()
|
|
.All(card => !card.ScreenSpaceDrawQuad.AABBFloat.IntersectsWith(handOfCards.ScreenSpaceDrawQuad.AABBFloat))
|
|
);
|
|
}
|
|
|
|
[Test]
|
|
public void TestRemoveCardsWhileDragging()
|
|
{
|
|
AddStep("add cards", () =>
|
|
{
|
|
for (int i = 0; i < 5; i++)
|
|
handOfCards.AddCard(new RankedPlayCardWithPlaylistItem(new RankedPlayCardItem()));
|
|
});
|
|
AddStep("hover card", () => InputManager.MoveMouseTo(handOfCards.GetCardsInDisplayOrder()[0]));
|
|
AddStep("start drag", () => InputManager.PressButton(MouseButton.Left));
|
|
AddStep("move card", () => InputManager.MoveMouseTo(handOfCards.GetCardsInDisplayOrder()[3]));
|
|
AddStep("remove cards", () =>
|
|
{
|
|
foreach (var card in handOfCards.Cards.ToArray())
|
|
handOfCards.RemoveCard(card.Item);
|
|
});
|
|
AddStep("release mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
|
}
|
|
|
|
[Test]
|
|
public void TestKeyboardSelectionWithoutCards()
|
|
{
|
|
AddAssert("no cards", () => !handOfCards.Cards.Any());
|
|
AddStep("right arrow", () => InputManager.Key(Key.Right));
|
|
AddStep("left arrow", () => InputManager.Key(Key.Left));
|
|
}
|
|
}
|
|
}
|