mirror of
https://github.com/ppy/osu.git
synced 2026-05-29 23:51:01 +08:00
Add test coverage for beatmap carousel v2 sort support
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Carousel;
|
||||
using osu.Game.Screens.Select;
|
||||
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 BeatmapCarouselFilterSortingTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestSorting()
|
||||
{
|
||||
List<BeatmapSetInfo> beatmapSets = new List<BeatmapSetInfo>();
|
||||
|
||||
const string zzz_lowercase = "zzzzz";
|
||||
const string zzz_uppercase = "ZZZZZ";
|
||||
const int diff_count = 5;
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
var set = TestResources.CreateTestBeatmapSetInfo(diff_count);
|
||||
|
||||
if (i == 4)
|
||||
set.Beatmaps.ForEach(b => b.Metadata.Artist = zzz_uppercase);
|
||||
|
||||
if (i == 8)
|
||||
set.Beatmaps.ForEach(b => b.Metadata.Artist = zzz_lowercase);
|
||||
|
||||
if (i == 12)
|
||||
set.Beatmaps.ForEach(b => b.Metadata.Author.Username = zzz_uppercase);
|
||||
|
||||
if (i == 16)
|
||||
set.Beatmaps.ForEach(b => b.Metadata.Author.Username = zzz_lowercase);
|
||||
|
||||
beatmapSets.Add(set);
|
||||
}
|
||||
|
||||
var results = await runSorting(SortMode.Author, beatmapSets);
|
||||
|
||||
Assert.That(results.Last().Metadata.Author.Username, Is.EqualTo(zzz_uppercase));
|
||||
Assert.That(results.SkipLast(diff_count).Last().Metadata.Author.Username, Is.EqualTo(zzz_lowercase));
|
||||
|
||||
results = await runSorting(SortMode.Artist, beatmapSets);
|
||||
|
||||
Assert.That(results.Last().Metadata.Artist, Is.EqualTo(zzz_uppercase));
|
||||
Assert.That(results.SkipLast(diff_count).Last().Metadata.Artist, Is.EqualTo(zzz_lowercase));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestSortingDateSubmitted()
|
||||
{
|
||||
List<BeatmapSetInfo> beatmapSets = new List<BeatmapSetInfo>();
|
||||
|
||||
const string zzz_string = "zzzzz";
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var set = TestResources.CreateTestBeatmapSetInfo(5);
|
||||
|
||||
// A total of 6 sets have date submitted (4 don't)
|
||||
// A total of 5 sets have artist string (3 of which also have date submitted)
|
||||
|
||||
if (i >= 2 && i < 8) // i = 2, 3, 4, 5, 6, 7 have submitted date
|
||||
set.DateSubmitted = DateTimeOffset.Now.AddMinutes(i);
|
||||
if (i < 5) // i = 0, 1, 2, 3, 4 have matching string
|
||||
set.Beatmaps.ForEach(b => b.Metadata.Artist = zzz_string);
|
||||
|
||||
set.Beatmaps.ForEach(b => b.Metadata.Title = $"submitted: {set.DateSubmitted}");
|
||||
|
||||
beatmapSets.Add(set);
|
||||
}
|
||||
|
||||
var results = await runSorting(SortMode.DateSubmitted, beatmapSets);
|
||||
|
||||
Assert.That(results.Count(), Is.EqualTo(50));
|
||||
|
||||
Assert.That(results.Reverse().TakeWhile(b => b.BeatmapSet!.DateSubmitted == null).Count(), Is.EqualTo(20), () => "missing dates should be at the end");
|
||||
Assert.That(results.TakeWhile(b => b.BeatmapSet!.DateSubmitted != null).Count(), Is.EqualTo(30), () => "non-missing dates should be at the start");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestSortByArtistUsesTitleAsTiebreaker()
|
||||
{
|
||||
List<BeatmapSetInfo> beatmapSets = new List<BeatmapSetInfo>();
|
||||
|
||||
const int diff_count = 5;
|
||||
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
var set = TestResources.CreateTestBeatmapSetInfo(diff_count);
|
||||
|
||||
if (i == 4)
|
||||
{
|
||||
set.Beatmaps.ForEach(b =>
|
||||
{
|
||||
b.Metadata.Artist = "ZZZ";
|
||||
b.Metadata.Title = "AAA";
|
||||
});
|
||||
}
|
||||
|
||||
if (i == 8)
|
||||
{
|
||||
set.Beatmaps.ForEach(b =>
|
||||
{
|
||||
b.Metadata.Artist = "ZZZ";
|
||||
b.Metadata.Title = "ZZZ";
|
||||
});
|
||||
}
|
||||
|
||||
beatmapSets.Add(set);
|
||||
}
|
||||
|
||||
var results = await runSorting(SortMode.Artist, beatmapSets);
|
||||
|
||||
Assert.That(() =>
|
||||
{
|
||||
var lastItem = results.Last();
|
||||
return lastItem.Metadata.Artist == "ZZZ" && lastItem.Metadata.Title == "ZZZ";
|
||||
});
|
||||
|
||||
Assert.That(() =>
|
||||
{
|
||||
var secondLastItem = results.SkipLast(diff_count).Last();
|
||||
return secondLastItem.Metadata.Artist == "ZZZ" && secondLastItem.Metadata.Title == "AAA";
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures stability is maintained on different sort modes for items with equal properties.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestSortingStabilityDateAdded()
|
||||
{
|
||||
List<BeatmapSetInfo> beatmapSets = new List<BeatmapSetInfo>();
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
var set = TestResources.CreateTestBeatmapSetInfo();
|
||||
|
||||
set.DateAdded = DateTimeOffset.FromUnixTimeSeconds(i);
|
||||
|
||||
// only need to set the first as they are a shared reference.
|
||||
var beatmap = set.Beatmaps.First();
|
||||
|
||||
beatmap.Metadata.Artist = "a";
|
||||
beatmap.Metadata.Title = "b";
|
||||
|
||||
beatmapSets.Add(set);
|
||||
}
|
||||
|
||||
var results = await runSorting(SortMode.Title, beatmapSets);
|
||||
|
||||
Assert.That(results.Select(b => b.BeatmapSet!.DateAdded), Is.Ordered.Descending);
|
||||
|
||||
results = await runSorting(SortMode.Artist, beatmapSets);
|
||||
|
||||
Assert.That(results.Select(b => b.BeatmapSet!.DateAdded), Is.Ordered.Descending);
|
||||
}
|
||||
|
||||
private static async Task<IEnumerable<BeatmapInfo>> runSorting(SortMode sort, List<BeatmapSetInfo> beatmapSets)
|
||||
{
|
||||
var sorter = new BeatmapCarouselFilterSorting(() => new FilterCriteria { Sort = sort });
|
||||
var carouselItems = await sorter.Run(beatmapSets.SelectMany(s => s.Beatmaps.Select(b => new CarouselItem(b))), CancellationToken.None);
|
||||
return carouselItems.Select(ci => ci.Model).OfType<BeatmapInfo>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,12 +140,12 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
SortBy(SortMode.Title);
|
||||
}
|
||||
|
||||
protected void SortBy(SortMode mode) => ApplyToFilter($"sort by {mode.ToString().ToLowerInvariant()}", c => c.Sort = mode);
|
||||
protected void GroupBy(GroupMode mode) => ApplyToFilter($"group by {mode.ToString().ToLowerInvariant()}", c => c.Group = mode);
|
||||
protected void SortBy(SortMode mode) => ApplyToFilter($"sort by {mode.GetDescription().ToLowerInvariant()}", c => c.Sort = mode);
|
||||
protected void GroupBy(GroupMode mode) => ApplyToFilter($"group by {mode.GetDescription().ToLowerInvariant()}", c => c.Group = mode);
|
||||
|
||||
protected void SortAndGroupBy(SortMode sort, GroupMode group)
|
||||
{
|
||||
ApplyToFilter($"sort by {sort.ToString().ToLowerInvariant()} & group by {group.ToString().ToLowerInvariant()}", c =>
|
||||
ApplyToFilter($"sort by {sort.GetDescription().ToLowerInvariant()} & group by {group.GetDescription().ToLowerInvariant()}", c =>
|
||||
{
|
||||
c.Sort = sort;
|
||||
c.Group = group;
|
||||
|
||||
@@ -8,6 +8,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Screens.Select.Filter;
|
||||
using osu.Game.Screens.SelectV2;
|
||||
using osu.Game.Tests.Resources;
|
||||
|
||||
@@ -130,6 +131,124 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
AddAssert("visible panel is updateable beatmap", () => GetSelectedPanel()?.Item?.Model, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures stability is maintained on different sort modes while an item is removed and then immediately re-added.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSortingStabilityWithRemovedAndReaddedItem()
|
||||
{
|
||||
RemoveAllBeatmaps();
|
||||
|
||||
const int diff_count = 5;
|
||||
|
||||
AddStep("Populate beatmap sets", () =>
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var set = TestResources.CreateTestBeatmapSetInfo(diff_count);
|
||||
|
||||
// only need to set the first as they are a shared reference.
|
||||
var beatmap = set.Beatmaps.First();
|
||||
|
||||
beatmap.Metadata.Artist = "same artist";
|
||||
beatmap.Metadata.Title = "same title";
|
||||
|
||||
// testing the case where DateAdded happens to equal (quite rare).
|
||||
set.DateAdded = DateTimeOffset.UnixEpoch;
|
||||
|
||||
BeatmapSets.Add(set);
|
||||
}
|
||||
});
|
||||
|
||||
BeatmapSetInfo removedBeatmap = null!;
|
||||
Guid[] originalOrder = null!;
|
||||
|
||||
SortBy(SortMode.Artist);
|
||||
WaitForFiltering();
|
||||
|
||||
AddAssert("Items in descending added order", () => Carousel.PostFilterBeatmaps.Select(b => b.BeatmapSet!.DateAdded), () => Is.Ordered.Descending);
|
||||
AddStep("Save order", () => originalOrder = Carousel.PostFilterBeatmaps.Select(b => b.ID).ToArray());
|
||||
|
||||
AddStep("Remove item", () =>
|
||||
{
|
||||
removedBeatmap = BeatmapSets[1];
|
||||
BeatmapSets.RemoveAt(1);
|
||||
});
|
||||
AddStep("Re-add item", () => BeatmapSets.Insert(1, removedBeatmap));
|
||||
WaitForFiltering();
|
||||
|
||||
AddAssert("Order didn't change", () => Carousel.PostFilterBeatmaps.Select(b => b.ID), () => Is.EqualTo(originalOrder));
|
||||
|
||||
SortBy(SortMode.Title);
|
||||
WaitForFiltering();
|
||||
|
||||
AddAssert("Order didn't change", () => Carousel.PostFilterBeatmaps.Select(b => b.ID), () => Is.EqualTo(originalOrder));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures stability is maintained on different sort modes while a new item is added to the carousel.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSortingStabilityWithNewItems()
|
||||
{
|
||||
RemoveAllBeatmaps();
|
||||
|
||||
const int diff_count = 5;
|
||||
|
||||
AddStep("Populate beatmap sets", () =>
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var set = TestResources.CreateTestBeatmapSetInfo(diff_count);
|
||||
|
||||
// only need to set the first as they are a shared reference.
|
||||
var beatmap = set.Beatmaps.First();
|
||||
|
||||
beatmap.Metadata.Artist = "same artist";
|
||||
beatmap.Metadata.Title = "same title";
|
||||
|
||||
// testing the case where DateAdded happens to equal (quite rare).
|
||||
set.DateAdded = DateTimeOffset.UnixEpoch;
|
||||
|
||||
BeatmapSets.Add(set);
|
||||
}
|
||||
});
|
||||
|
||||
Guid[] originalOrder = null!;
|
||||
|
||||
SortBy(SortMode.Artist);
|
||||
WaitForFiltering();
|
||||
|
||||
AddAssert("Items in descending added order", () => Carousel.PostFilterBeatmaps.Select(b => b.BeatmapSet!.DateAdded), () => Is.Ordered.Descending);
|
||||
AddStep("Save order", () => originalOrder = Carousel.PostFilterBeatmaps.Select(b => b.ID).ToArray());
|
||||
|
||||
AddStep("Add new item", () =>
|
||||
{
|
||||
var set = TestResources.CreateTestBeatmapSetInfo();
|
||||
|
||||
// only need to set the first as they are a shared reference.
|
||||
var beatmap = set.Beatmaps.First();
|
||||
|
||||
beatmap.Metadata.Artist = "same artist";
|
||||
beatmap.Metadata.Title = "same title";
|
||||
|
||||
set.DateAdded = DateTimeOffset.FromUnixTimeSeconds(1);
|
||||
|
||||
BeatmapSets.Add(set);
|
||||
|
||||
// add set to expected ordering
|
||||
originalOrder = set.Beatmaps.Select(b => b.ID).Concat(originalOrder).ToArray();
|
||||
});
|
||||
WaitForFiltering();
|
||||
|
||||
AddAssert("Order didn't change", () => Carousel.PostFilterBeatmaps.Select(b => b.ID), () => Is.EqualTo(originalOrder));
|
||||
|
||||
SortBy(SortMode.Title);
|
||||
WaitForFiltering();
|
||||
|
||||
AddAssert("Order didn't change", () => Carousel.PostFilterBeatmaps.Select(b => b.ID), () => Is.EqualTo(originalOrder));
|
||||
}
|
||||
|
||||
private void updateBeatmap(Action<BeatmapInfo>? updateBeatmap = null, Action<BeatmapSetInfo>? updateSet = null)
|
||||
{
|
||||
AddStep("update beatmap with different reference", () =>
|
||||
|
||||
Reference in New Issue
Block a user