mirror of
https://github.com/ppy/osu.git
synced 2025-02-24 15:13:06 +08:00
I was trying to be smart about things and make use of our `SynchronisationContext` setup, but it turns out to not work in all cases due to the context being missing depending on where you are calling the method from. For now let's prefer the "works everywhere" method of scheduling the final work back to update.
240 lines
9.6 KiB
C#
240 lines
9.6 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.Allocation;
|
|
using osu.Framework.Bindables;
|
|
using osu.Framework.Extensions.ObjectExtensions;
|
|
using osu.Framework.Graphics;
|
|
using osu.Framework.Graphics.Containers;
|
|
using osu.Framework.Graphics.Shapes;
|
|
using osu.Framework.Testing;
|
|
using osu.Framework.Utils;
|
|
using osu.Game.Beatmaps;
|
|
using osu.Game.Database;
|
|
using osu.Game.Graphics;
|
|
using osu.Game.Graphics.Containers;
|
|
using osu.Game.Screens.Select;
|
|
using osu.Game.Screens.Select.Filter;
|
|
using osu.Game.Screens.SelectV2;
|
|
using osu.Game.Tests.Beatmaps;
|
|
using osu.Game.Tests.Resources;
|
|
using osuTK.Graphics;
|
|
using osuTK.Input;
|
|
using BeatmapCarousel = osu.Game.Screens.SelectV2.BeatmapCarousel;
|
|
|
|
namespace osu.Game.Tests.Visual.SongSelect
|
|
{
|
|
public abstract partial class BeatmapCarouselV2TestScene : OsuManualInputManagerTestScene
|
|
{
|
|
protected readonly BindableList<BeatmapSetInfo> BeatmapSets = new BindableList<BeatmapSetInfo>();
|
|
|
|
protected BeatmapCarousel Carousel = null!;
|
|
|
|
protected OsuScrollContainer<Drawable> Scroll => Carousel.ChildrenOfType<OsuScrollContainer<Drawable>>().Single();
|
|
|
|
[Cached(typeof(BeatmapStore))]
|
|
private BeatmapStore store;
|
|
|
|
private OsuTextFlowContainer stats = null!;
|
|
|
|
private int beatmapCount;
|
|
|
|
protected BeatmapCarouselV2TestScene()
|
|
{
|
|
store = new TestBeatmapStore
|
|
{
|
|
BeatmapSets = { BindTarget = BeatmapSets }
|
|
};
|
|
|
|
BeatmapSets.BindCollectionChanged((_, _) => beatmapCount = BeatmapSets.Sum(s => s.Beatmaps.Count));
|
|
|
|
Scheduler.AddDelayed(updateStats, 100, true);
|
|
}
|
|
|
|
[SetUpSteps]
|
|
public virtual void SetUpSteps()
|
|
{
|
|
RemoveAllBeatmaps();
|
|
|
|
CreateCarousel();
|
|
|
|
SortBy(new FilterCriteria { Sort = SortMode.Title });
|
|
}
|
|
|
|
protected void CreateCarousel()
|
|
{
|
|
AddStep("create components", () =>
|
|
{
|
|
Box topBox;
|
|
Children = new Drawable[]
|
|
{
|
|
new GridContainer
|
|
{
|
|
RelativeSizeAxes = Axes.Both,
|
|
ColumnDimensions = new[]
|
|
{
|
|
new Dimension(GridSizeMode.Relative, 1),
|
|
},
|
|
RowDimensions = new[]
|
|
{
|
|
new Dimension(GridSizeMode.Absolute, 200),
|
|
new Dimension(),
|
|
new Dimension(GridSizeMode.Absolute, 200),
|
|
},
|
|
Content = new[]
|
|
{
|
|
new Drawable[]
|
|
{
|
|
topBox = new Box
|
|
{
|
|
Anchor = Anchor.Centre,
|
|
Origin = Anchor.Centre,
|
|
Colour = Color4.Cyan,
|
|
RelativeSizeAxes = Axes.Both,
|
|
Alpha = 0.4f,
|
|
},
|
|
},
|
|
new Drawable[]
|
|
{
|
|
Carousel = new BeatmapCarousel
|
|
{
|
|
Anchor = Anchor.Centre,
|
|
Origin = Anchor.Centre,
|
|
Width = 500,
|
|
RelativeSizeAxes = Axes.Y,
|
|
},
|
|
},
|
|
new[]
|
|
{
|
|
new Box
|
|
{
|
|
Anchor = Anchor.Centre,
|
|
Origin = Anchor.Centre,
|
|
Colour = Color4.Cyan,
|
|
RelativeSizeAxes = Axes.Both,
|
|
Alpha = 0.4f,
|
|
},
|
|
topBox.CreateProxy(),
|
|
}
|
|
}
|
|
},
|
|
stats = new OsuTextFlowContainer
|
|
{
|
|
AutoSizeAxes = Axes.Both,
|
|
Padding = new MarginPadding(10),
|
|
TextAnchor = Anchor.CentreLeft,
|
|
},
|
|
};
|
|
});
|
|
}
|
|
|
|
protected void SortBy(FilterCriteria criteria) => AddStep($"sort:{criteria.Sort} group:{criteria.Group}", () => Carousel.Filter(criteria));
|
|
|
|
protected void WaitForDrawablePanels() => AddUntilStep("drawable panels loaded", () => Carousel.ChildrenOfType<ICarouselPanel>().Count(), () => Is.GreaterThan(0));
|
|
protected void WaitForSorting() => AddUntilStep("sorting finished", () => Carousel.IsFiltering, () => Is.False);
|
|
protected void WaitForScrolling() => AddUntilStep("scroll finished", () => Scroll.Current, () => Is.EqualTo(Scroll.Target));
|
|
|
|
protected void SelectNextPanel() => AddStep("select next panel", () => InputManager.Key(Key.Down));
|
|
protected void SelectPrevPanel() => AddStep("select prev panel", () => InputManager.Key(Key.Up));
|
|
protected void SelectNextGroup() => AddStep("select next group", () => InputManager.Key(Key.Right));
|
|
protected void SelectPrevGroup() => AddStep("select prev group", () => InputManager.Key(Key.Left));
|
|
|
|
protected void Select() => AddStep("select", () => InputManager.Key(Key.Enter));
|
|
|
|
protected void CheckNoSelection() => AddAssert("has no selection", () => Carousel.CurrentSelection, () => Is.Null);
|
|
protected void CheckHasSelection() => AddAssert("has selection", () => Carousel.CurrentSelection, () => Is.Not.Null);
|
|
|
|
protected void WaitForGroupSelection(int group, int panel)
|
|
{
|
|
AddUntilStep($"selected is group{group} panel{panel}", () =>
|
|
{
|
|
var groupingFilter = Carousel.Filters.OfType<BeatmapCarouselFilterGrouping>().Single();
|
|
|
|
GroupDefinition g = groupingFilter.GroupItems.Keys.ElementAt(group);
|
|
// offset by one because the group itself is included in the items list.
|
|
CarouselItem item = groupingFilter.GroupItems[g].ElementAt(panel + 1);
|
|
|
|
return ReferenceEquals(Carousel.CurrentSelection, item.Model);
|
|
});
|
|
}
|
|
|
|
protected void WaitForSelection(int set, int? diff = null)
|
|
{
|
|
AddUntilStep($"selected is set{set}{(diff.HasValue ? $" diff{diff.Value}" : "")}", () =>
|
|
{
|
|
if (diff != null)
|
|
return ReferenceEquals(Carousel.CurrentSelection, BeatmapSets[set].Beatmaps[diff.Value]);
|
|
|
|
return BeatmapSets[set].Beatmaps.Contains(Carousel.CurrentSelection);
|
|
});
|
|
}
|
|
|
|
protected void ClickVisiblePanel<T>(int index)
|
|
where T : Drawable
|
|
{
|
|
AddStep($"click panel at index {index}", () =>
|
|
{
|
|
Carousel.ChildrenOfType<UserTrackingScrollContainer>().Single()
|
|
.ChildrenOfType<T>()
|
|
.Where(p => ((ICarouselPanel)p).Item?.IsVisible == true)
|
|
.OrderBy(p => p.Y)
|
|
.ElementAt(index)
|
|
.TriggerClick();
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add requested beatmap sets count to list.
|
|
/// </summary>
|
|
/// <param name="count">The count of beatmap sets to add.</param>
|
|
/// <param name="fixedDifficultiesPerSet">If not null, the number of difficulties per set. If null, randomised difficulty count will be used.</param>
|
|
protected void AddBeatmaps(int count, int? fixedDifficultiesPerSet = null) => AddStep($"add {count} beatmaps", () =>
|
|
{
|
|
for (int i = 0; i < count; i++)
|
|
BeatmapSets.Add(TestResources.CreateTestBeatmapSetInfo(fixedDifficultiesPerSet ?? RNG.Next(1, 4)));
|
|
});
|
|
|
|
protected void RemoveAllBeatmaps() => AddStep("clear all beatmaps", () => BeatmapSets.Clear());
|
|
|
|
protected void RemoveFirstBeatmap() =>
|
|
AddStep("remove first beatmap", () =>
|
|
{
|
|
if (BeatmapSets.Count == 0) return;
|
|
|
|
BeatmapSets.Remove(BeatmapSets.First());
|
|
});
|
|
|
|
private void updateStats()
|
|
{
|
|
if (Carousel.IsNull())
|
|
return;
|
|
|
|
stats.Clear();
|
|
createHeader("beatmap store");
|
|
stats.AddParagraph($"""
|
|
sets: {BeatmapSets.Count}
|
|
beatmaps: {beatmapCount}
|
|
""");
|
|
createHeader("carousel");
|
|
stats.AddParagraph($"""
|
|
sorting: {Carousel.IsFiltering}
|
|
tracked: {Carousel.ItemsTracked}
|
|
displayable: {Carousel.DisplayableItems}
|
|
displayed: {Carousel.VisibleItems}
|
|
selected: {Carousel.CurrentSelection}
|
|
""");
|
|
|
|
void createHeader(string text)
|
|
{
|
|
stats.AddParagraph(string.Empty);
|
|
stats.AddParagraph(text, cp =>
|
|
{
|
|
cp.Font = cp.Font.With(size: 18, weight: FontWeight.Bold);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|