diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselArtistGrouping.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselArtistGrouping.cs index 84769f2cee..8f822cbb1d 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselArtistGrouping.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselArtistGrouping.cs @@ -174,5 +174,33 @@ namespace osu.Game.Tests.Visual.SongSelectV2 SelectNextGroup(); WaitForGroupSelection(1, 1); } + + [Test] + public void TestBasicFiltering() + { + ApplyToFilter("filter", c => c.SearchText = BeatmapSets[2].Metadata.Title); + WaitForFiltering(); + + AddAssert("1 group + 1 set + 3 diffs displayed", () => Carousel.DisplayableItems == 5); + + CheckNoSelection(); + SelectNextPanel(); + Select(); + SelectNextPanel(); + Select(); + WaitForGroupSelection(0, 1); + + for (int i = 0; i < 6; i++) + SelectNextPanel(); + + Select(); + + WaitForGroupSelection(0, 2); + + ApplyToFilter("remove filter", c => c.SearchText = string.Empty); + WaitForFiltering(); + + AddAssert("5 groups + 10 sets + 30 diffs displayed", () => Carousel.DisplayableItems == 45); + } } } diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselDifficultyGrouping.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselDifficultyGrouping.cs index 37fb95ce86..bf20825bdb 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselDifficultyGrouping.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselDifficultyGrouping.cs @@ -192,5 +192,35 @@ namespace osu.Game.Tests.Visual.SongSelectV2 ClickVisiblePanelWithOffset(1, new Vector2(0, (CarouselItem.DEFAULT_HEIGHT / 2 + 1))); WaitForGroupSelection(0, 1); } + + [Test] + public void TestBasicFiltering() + { + ApplyToFilter("filter", c => c.SearchText = BeatmapSets[2].Metadata.Title); + WaitForFiltering(); + + AddAssert("3 groups + 3 diffs displayed", () => Carousel.DisplayableItems == 6); + + CheckNoSelection(); + SelectNextPanel(); + Select(); + SelectNextPanel(); + Select(); + WaitForGroupSelection(0, 0); + + for (int i = 0; i < 5; i++) + SelectNextPanel(); + + Select(); + SelectNextPanel(); + Select(); + + WaitForGroupSelection(1, 0); + + ApplyToFilter("remove filter", c => c.SearchText = string.Empty); + WaitForFiltering(); + + AddAssert("3 groups + 30 diffs displayed", () => Carousel.DisplayableItems == 33); + } } } diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselFiltering.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselFiltering.cs new file mode 100644 index 0000000000..cb1b0ec31f --- /dev/null +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapCarouselFiltering.cs @@ -0,0 +1,284 @@ +// 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.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Select.Filter; +using osu.Game.Screens.SelectV2; +using osu.Game.Tests.Resources; + +namespace osu.Game.Tests.Visual.SongSelectV2 +{ + [TestFixture] + public partial class TestSceneBeatmapCarouselFiltering : BeatmapCarouselTestScene + { + [Resolved] + private RulesetStore rulesets { get; set; } = null!; + + [SetUpSteps] + public void SetUpSteps() + { + RemoveAllBeatmaps(); + CreateCarousel(); + } + + [Test] + public void TestBasicFiltering() + { + AddBeatmaps(10, 3); + WaitForDrawablePanels(); + + ApplyToFilter("filter", c => c.SearchText = BeatmapSets[2].Metadata.Title); + WaitForFiltering(); + + AddAssert("3 diffs + 1 set displayed", () => Carousel.DisplayableItems == 4); + + SelectNextPanel(); + Select(); + + WaitForSelection(2, 0); + + for (int i = 0; i < 5; i++) + SelectNextPanel(); + + Select(); + WaitForSelection(2, 1); + + ApplyToFilter("remove filter", c => c.SearchText = string.Empty); + WaitForFiltering(); + + AddAssert("30 diffs + 10 sets displayed", () => Carousel.DisplayableItems == 40); + } + + [Test] + public void TestFilteringByUserStarDifficulty() + { + AddStep("add mixed difficulty set", () => + { + var set = TestResources.CreateTestBeatmapSetInfo(1); + set.Beatmaps.Clear(); + + for (int i = 1; i <= 15; i++) + { + set.Beatmaps.Add(new BeatmapInfo(new OsuRuleset().RulesetInfo, new BeatmapDifficulty(), new BeatmapMetadata()) + { + BeatmapSet = set, + DifficultyName = $"Stars: {i}", + StarRating = i, + }); + } + + BeatmapSets.Add(set); + }); + + WaitForDrawablePanels(); + + ApplyToFilter("filter [5..]", c => + { + c.UserStarDifficulty.Min = 5; + c.UserStarDifficulty.Max = null; + }); + WaitForFiltering(); + AddAssert("1 set + 11 diffs displayed", () => Carousel.DisplayableItems == 12); + + ApplyToFilter("filter to [0..7]", c => + { + c.UserStarDifficulty.Min = null; + c.UserStarDifficulty.Max = 7; + }); + WaitForFiltering(); + AddAssert("1 set + 7 diffs displayed", () => Carousel.DisplayableItems == 8); + + ApplyToFilter("filter to [5..7]", c => + { + c.UserStarDifficulty.Min = 5; + c.UserStarDifficulty.Max = 7; + }); + + WaitForFiltering(); + AddAssert("1 set + 3 diffs displayed", () => Carousel.DisplayableItems == 4); + + ApplyToFilter("filter to [2..2]", c => + { + c.UserStarDifficulty.Min = 2; + c.UserStarDifficulty.Max = 2; + }); + + WaitForFiltering(); + AddAssert("`1 set + 1 diff displayed", () => Carousel.DisplayableItems == 2); + + ApplyToFilter("filter to [0..]", c => + { + c.UserStarDifficulty.Min = 0; + c.UserStarDifficulty.Max = null; + }); + WaitForFiltering(); + AddAssert("1 set + 15 diffs displayed", () => Carousel.DisplayableItems == 16); + } + + [Test] + public void TestCarouselRemembersSelection() + { + Guid selectedID = Guid.Empty; + + AddBeatmaps(50, 3); + WaitForDrawablePanels(); + + SelectNextGroup(); + SelectNextPanel(); + Select(); + + AddStep("record selection", () => selectedID = ((BeatmapInfo)Carousel.CurrentSelection!).ID); + + for (int i = 0; i < 5; i++) + { + ApplyToFilter("filter all", c => c.SearchText = Guid.NewGuid().ToString()); + AddAssert("selection not changed", () => ((BeatmapInfo)Carousel.CurrentSelection!).ID == selectedID); + ApplyToFilter("remove filter", c => c.SearchText = string.Empty); + AddAssert("selection not changed", () => ((BeatmapInfo)Carousel.CurrentSelection!).ID == selectedID); + } + } + + [Test] + public void TestCarouselRemembersSelectionDifficultySort() + { + Guid selectedID = Guid.Empty; + + AddBeatmaps(50, 3); + WaitForDrawablePanels(); + + SortBy(SortMode.Difficulty); + + SelectNextGroup(); + + AddStep("record selection", () => selectedID = ((BeatmapInfo)Carousel.CurrentSelection!).ID); + + for (int i = 0; i < 5; i++) + { + ApplyToFilter("filter all", c => c.SearchText = Guid.NewGuid().ToString()); + AddAssert("selection not changed", () => ((BeatmapInfo)Carousel.CurrentSelection!).ID == selectedID); + ApplyToFilter("remove filter", c => c.SearchText = string.Empty); + AddAssert("selection not changed", () => ((BeatmapInfo)Carousel.CurrentSelection!).ID == selectedID); + } + } + + [Test] + public void TestCarouselRetainsSelectionFromDifficultySort() + { + AddBeatmaps(50, 3); + WaitForDrawablePanels(); + + BeatmapInfo chosenBeatmap = null!; + + for (int i = 0; i < 3; i++) + { + int diff = i; + + AddStep($"select diff {diff}", () => Carousel.CurrentSelection = chosenBeatmap = BeatmapSets[20].Beatmaps[diff]); + AddUntilStep("selection changed", () => Carousel.CurrentSelection, () => Is.EqualTo(chosenBeatmap)); + + SortBy(SortMode.Difficulty); + AddAssert("selection retained", () => Carousel.CurrentSelection, () => Is.EqualTo(chosenBeatmap)); + + SortBy(SortMode.Title); + AddAssert("selection retained", () => Carousel.CurrentSelection, () => Is.EqualTo(chosenBeatmap)); + } + } + + [Test] + public void TestExternalRulesetChange() + { + ApplyToFilter("allow converted beatmaps", c => c.AllowConvertedBeatmaps = true); + ApplyToFilter("filter to osu", c => c.Ruleset = rulesets.AvailableRulesets.ElementAt(0)); + + WaitForFiltering(); + + AddStep("add mixed ruleset beatmapset", () => + { + var testMixed = TestResources.CreateTestBeatmapSetInfo(3); + + for (int i = 0; i <= 2; i++) + testMixed.Beatmaps[i].Ruleset = rulesets.AvailableRulesets.ElementAt(i); + + BeatmapSets.Add(testMixed); + }); + WaitForDrawablePanels(); + + SelectNextPanel(); + Select(); + + AddUntilStep("wait for filtered difficulties", () => + { + var visibleBeatmapPanels = GetVisiblePanels(); + + return visibleBeatmapPanels.Count() == 1 + && visibleBeatmapPanels.Count(p => ((BeatmapInfo)p.Item!.Model).Ruleset.OnlineID == 0) == 1; + }); + + ApplyToFilter("filter to taiko", c => c.Ruleset = rulesets.AvailableRulesets.ElementAt(1)); + + WaitForFiltering(); + + AddUntilStep("wait for filtered difficulties", () => + { + var visibleBeatmapPanels = GetVisiblePanels(); + + return visibleBeatmapPanels.Count() == 2 + && visibleBeatmapPanels.Count(p => ((BeatmapInfo)p.Item!.Model).Ruleset.OnlineID == 0) == 1 + && visibleBeatmapPanels.Count(p => ((BeatmapInfo)p.Item!.Model).Ruleset.OnlineID == 1) == 1; + }); + + ApplyToFilter("filter to catch", c => c.Ruleset = rulesets.AvailableRulesets.ElementAt(2)); + + WaitForFiltering(); + + AddUntilStep("wait for filtered difficulties", () => + { + var visibleBeatmapPanels = GetVisiblePanels(); + + return visibleBeatmapPanels.Count() == 2 + && visibleBeatmapPanels.Count(p => ((BeatmapInfo)p.Item!.Model).Ruleset.OnlineID == 0) == 1 + && visibleBeatmapPanels.Count(p => ((BeatmapInfo)p.Item!.Model).Ruleset.OnlineID == 2) == 1; + }); + } + + [Test] + [Ignore("Difficulty sorting is broken when set headers are included.")] // todo: fix. + public void TestSortingWithDifficultyFiltered() + { + const int diffs_per_set = 3; + const int local_set_count = 2; + + AddStep("populate beatmap sets", () => + { + for (int i = 0; i < local_set_count; i++) + { + var set = TestResources.CreateTestBeatmapSetInfo(diffs_per_set); + set.Beatmaps[0].StarRating = 3 - i; + set.Beatmaps[0].DifficultyName += $" ({3 - i}*)"; + set.Beatmaps[1].StarRating = 6 + i; + set.Beatmaps[1].DifficultyName += $" ({6 + i}*)"; + BeatmapSets.Add(set); + } + }); + + SortBy(SortMode.Difficulty); + + AddAssert($"3 sets + {local_set_count * diffs_per_set} diffs displayed", () => Carousel.DisplayableItems == 3 + local_set_count * diffs_per_set); + + ApplyToFilter("filter to normal", c => c.SearchText = "Normal"); + + AddAssert($"{local_set_count} sets + {local_set_count} diffs displayed", () => Carousel.DisplayableItems == local_set_count + local_set_count); + + ApplyToFilter("filter to insane", c => c.SearchText = "Insane"); + + AddAssert($"{local_set_count} sets + {local_set_count} diffs displayed", () => Carousel.DisplayableItems == local_set_count + local_set_count); + } + } +}