1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-22 06:29:54 +08:00
Files
osu-lazer/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselRandom.cs
T
Dean Herbert f676206331 SongSelectV2: Fix random selection not working as expected when displaying individual difficulties
Previously, random selection would always be done at a *set* level. The
final operation of a random action would be "select the user's
recommended difficulty from this randomly selected set".

This makes no sense when sets are not grouped together at song select.
In fact, it is completely broken with the previous commit which adds
group-isolated random support – if we're grouping by difficulty and the
user's recommendation is not in the current group it would throw the
user into another group unexpectedly.

This fixes the issue by splitting out the random implementation into two
separate pathways depending on the carousel display mode.
2025-06-19 17:29:41 +09:00

293 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 NUnit.Framework;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Screens.Select.Filter;
using osu.Game.Screens.SelectV2;
namespace osu.Game.Tests.Visual.SongSelectV2
{
[TestFixture]
public partial class TestSceneBeatmapCarouselRandom : BeatmapCarouselTestScene
{
[SetUpSteps]
public void SetUpSteps()
{
RemoveAllBeatmaps();
CreateCarousel();
}
[Test]
public void TestRandomObeysFiltering()
{
AddBeatmaps(2, 10, true);
ApplyToFilterAndWaitForFilter("filter", c => c.SearchText = BeatmapSets[0].Beatmaps.Last().DifficultyName);
CheckDisplayedBeatmapSetsCount(1);
CheckDisplayedBeatmapsCount(1);
for (int i = 0; i < 10; i++)
{
nextRandom();
WaitForSetSelection(0, 9);
}
}
/// <summary>
/// Test random non-repeating algorithm
/// </summary>
[Test]
public void TestRandomArtistGrouping()
{
SortAndGroupBy(SortMode.Artist, GroupMode.Artist);
AddBeatmaps(10, 3, true);
WaitForDrawablePanels();
GroupDefinition? expanded = null;
for (int i = 0; i < 2; i++)
{
nextRandom();
expanded ??= storeExpandedGroup();
ensureSetRandomDidNotRepeat();
checkExpandedGroupUnchanged();
}
nextRandom();
ensureSetRandomDidRepeat();
checkExpandedGroupUnchanged();
prevRandomSet();
checkRewindCorrectSet();
checkExpandedGroupUnchanged();
prevRandomSet();
checkRewindCorrectSet();
checkExpandedGroupUnchanged();
nextRandom();
ensureSetRandomDidNotRepeat();
checkExpandedGroupUnchanged();
nextRandom();
ensureSetRandomDidRepeat();
checkExpandedGroupUnchanged();
GroupDefinition? storeExpandedGroup()
{
AddStep("store open group", () => expanded = Carousel.ExpandedGroup);
return null;
}
void checkExpandedGroupUnchanged() => AddAssert("expanded did not change", () => Carousel.ExpandedGroup, () => Is.EqualTo(expanded));
}
/// <summary>
/// Test random non-repeating algorithm
/// </summary>
[Test]
public void TestRandomDifficultyGroupingRewindsCorrectly()
{
SortAndGroupBy(SortMode.Difficulty, GroupMode.Difficulty);
AddBeatmaps(3, 3, true);
WaitForDrawablePanels();
GroupDefinition? expanded = null;
for (int i = 0; i < 3; i++)
{
nextRandom();
expanded ??= storeExpandedGroup();
ensureRandomDidNotRepeat();
checkExpandedGroupUnchanged();
}
for (int i = 0; i < 2; i++)
{
prevRandom();
checkRewindCorrect();
checkExpandedGroupUnchanged();
}
for (int i = 0; i < 2; i++)
{
nextRandom();
ensureRandomDidNotRepeat();
checkExpandedGroupUnchanged();
}
nextRandom();
ensureRandomDidRepeat();
checkExpandedGroupUnchanged();
GroupDefinition? storeExpandedGroup()
{
AddStep("store open group", () => expanded = Carousel.ExpandedGroup);
return null;
}
void checkExpandedGroupUnchanged() => AddAssert("expanded did not change", () => Carousel.ExpandedGroup, () => Is.EqualTo(expanded));
}
/// <summary>
/// Test random non-repeating algorithm
/// </summary>
[Test]
public void TestRandomDifficultyGroupingRepeatsWhenExhausted()
{
SortAndGroupBy(SortMode.Difficulty, GroupMode.Difficulty);
AddBeatmaps(3, 3, true);
WaitForDrawablePanels();
GroupDefinition? expanded = null;
for (int i = 0; i < 3; i++)
{
nextRandom();
expanded ??= storeExpandedGroup();
ensureRandomDidNotRepeat();
checkExpandedGroupUnchanged();
}
for (int i = 0; i < 3; i++)
{
nextRandom();
ensureRandomDidRepeat();
}
for (int i = 0; i < 5; i++)
{
prevRandom();
checkRewindCorrect();
checkExpandedGroupUnchanged();
}
nextRandom();
checkExpandedGroupUnchanged();
// can't assert repeat or otherwise as we went through multiple permutations.
GroupDefinition? storeExpandedGroup()
{
AddStep("store open group", () => expanded = Carousel.ExpandedGroup);
return null;
}
void checkExpandedGroupUnchanged() => AddAssert("expanded did not change", () => Carousel.ExpandedGroup, () => Is.EqualTo(expanded));
}
[Test]
public void TestRewindOverMultipleIterations()
{
const int local_set_count = 3;
const int random_select_count = local_set_count * 3;
AddBeatmaps(local_set_count, 3, true);
WaitForDrawablePanels();
SelectNextSet();
for (int i = 0; i < random_select_count; i++)
nextRandom();
for (int i = 0; i < random_select_count; i++)
{
prevRandomSet();
checkRewindCorrectSet();
}
}
[Test]
public void TestRandomThenRewindSameFrame()
{
AddBeatmaps(10, 3, true);
WaitForDrawablePanels();
BeatmapInfo? originalSelected = null;
nextRandom();
CheckHasSelection();
AddStep("store selection", () => originalSelected = (BeatmapInfo)Carousel.CurrentSelection!);
AddStep("random then rewind", () =>
{
Carousel.NextRandom();
Carousel.PreviousRandom();
});
AddAssert("selection not changed", () => Carousel.CurrentSelection, () => Is.EqualTo(originalSelected));
}
[Test]
public void TestRewindToDeletedBeatmap()
{
AddBeatmaps(10, 3, true);
WaitForDrawablePanels();
BeatmapInfo? originalSelected = null;
BeatmapInfo? postRandomSelection = null;
nextRandom();
CheckHasSelection();
AddStep("store selection", () => originalSelected = (BeatmapInfo)Carousel.CurrentSelection!);
nextRandom();
AddStep("store selection", () => postRandomSelection = (BeatmapInfo)Carousel.CurrentSelection!);
AddAssert("selection changed", () => originalSelected, () => Is.Not.SameAs(postRandomSelection));
AddStep("delete previous selection beatmaps", () => BeatmapSets.Remove(originalSelected!.BeatmapSet!));
WaitForFiltering();
AddAssert("selection not changed", () => Carousel.CurrentSelection, () => Is.EqualTo(postRandomSelection));
prevRandomSet();
AddAssert("selection not changed", () => Carousel.CurrentSelection, () => Is.EqualTo(postRandomSelection));
}
private void nextRandom() =>
AddStep("select random next", () => Carousel.NextRandom());
private void ensureRandomDidRepeat() =>
AddAssert("did repeat", () => BeatmapRequestedSelections.Distinct().Count(), () => Is.LessThan(BeatmapRequestedSelections.Count));
private void ensureRandomDidNotRepeat() =>
AddAssert("no repeats", () => BeatmapRequestedSelections.Distinct().Count(), () => Is.EqualTo(BeatmapRequestedSelections.Count));
private void ensureSetRandomDidRepeat() =>
AddAssert("did repeat", () => BeatmapSetRequestedSelections.Distinct().Count(), () => Is.LessThan(BeatmapSetRequestedSelections.Count));
private void ensureSetRandomDidNotRepeat() =>
AddAssert("no repeats", () => BeatmapSetRequestedSelections.Distinct().Count(), () => Is.EqualTo(BeatmapSetRequestedSelections.Count));
private void checkRewindCorrect() =>
AddAssert("rewind matched expected beatmap", () => BeatmapRequestedSelections.Peek(), () => Is.EqualTo(Carousel.SelectedBeatmapInfo));
private void checkRewindCorrectSet() =>
AddAssert("rewind matched expected set", () => BeatmapSetRequestedSelections.Peek(), () => Is.EqualTo(Carousel.SelectedBeatmapSet));
private void prevRandom() => AddStep("select last random", () =>
{
Carousel.PreviousRandom();
BeatmapRequestedSelections.Pop();
// Pop twice because the PreviousRandom call also requests selection.
BeatmapRequestedSelections.Pop();
});
private void prevRandomSet() => AddStep("select last random set", () =>
{
Carousel.PreviousRandom();
BeatmapSetRequestedSelections.Pop();
});
}
}