// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; using osu.Framework.Allocation; using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Overlays.BeatmapSet; using osu.Game.Rulesets; using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Testing; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics.Sprites; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.BeatmapSet.Scores; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Select.Details; using APIUser = osu.Game.Online.API.Requests.Responses.APIUser; namespace osu.Game.Tests.Visual.Online { public partial class TestSceneBeatmapSetOverlay : OsuManualInputManagerTestScene { private readonly TestBeatmapSetOverlay overlay; private int nextBeatmapSetId = 1; public TestSceneBeatmapSetOverlay() { Add(overlay = new TestBeatmapSetOverlay()); } [Resolved] private IRulesetStore rulesets { get; set; } = null!; [SetUp] public void SetUp() => Schedule(() => SelectedMods.Value = Array.Empty()); [Test] public void TestLoading() { AddStep(@"show loading", () => overlay.ShowBeatmapSet(null)); } [Test] public void TestLocalBeatmaps() { AddStep(@"show first", () => { overlay.ShowBeatmapSet(new APIBeatmapSet { Genre = new BeatmapSetOnlineGenre { Id = 15, Name = "Future genre" }, Language = new BeatmapSetOnlineLanguage { Id = 15, Name = "Future language" }, OnlineID = 1235, Title = @"an awesome beatmap", Artist = @"naru narusegawa", Source = @"hinata sou", Tags = @"test tag tag more tag", Author = new APIUser { Username = @"BanchoBot", Id = 3, }, Preview = @"https://b.ppy.sh/preview/12345.mp3", PlayCount = 123, FavouriteCount = 456, Submitted = DateTime.Now, Ranked = DateTime.Now, BPM = 111, HasVideo = true, Ratings = Enumerable.Range(0, 11).ToArray(), HasStoryboard = true, Covers = new BeatmapSetOnlineCovers(), Beatmaps = new[] { new APIBeatmap { StarRating = 9.99, DifficultyName = @"TEST", Length = 456000, HitLength = 400000, RulesetID = 3, CircleSize = 1, DrainRate = 2.3f, OverallDifficulty = 4.5f, ApproachRate = 6, CircleCount = 111, SliderCount = 12, PlayCount = 222, PassCount = 21, FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }, }, }); }); downloadAssert(true); AddStep("show many difficulties", () => overlay.ShowBeatmapSet(createManyDifficultiesBeatmapSet())); downloadAssert(true); AddAssert("status is loved", () => overlay.ChildrenOfType().Single().Status == BeatmapOnlineStatus.Loved); AddAssert("scores container is visible", () => overlay.ChildrenOfType().Single().Alpha == 1); AddAssert("mod selector is visible", () => overlay.ChildrenOfType().Single().Alpha == 1); AddStep("go to second beatmap", () => overlay.ChildrenOfType().ElementAt(1).TriggerClick()); AddAssert("status is graveyard", () => overlay.ChildrenOfType().Single().Status == BeatmapOnlineStatus.Graveyard); AddAssert("scores container is hidden", () => overlay.ChildrenOfType().Single().Alpha == 0); } [Test] public void TestAvailability() { AddStep(@"show undownloadable", () => { var set = getBeatmapSet(); set.Availability = new BeatmapSetOnlineAvailability { DownloadDisabled = true, ExternalLink = "https://osu.ppy.sh", }; overlay.ShowBeatmapSet(set); }); downloadAssert(false); } [Test] public void TestMultipleRulesets() { AddStep("show multiple rulesets beatmap", () => { var beatmaps = new List(); foreach (var ruleset in rulesets.AvailableRulesets.Skip(1)) { beatmaps.Add(new APIBeatmap { DifficultyName = ruleset.Name, RulesetID = ruleset.OnlineID, FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }); } var set = getBeatmapSet(); set.Beatmaps = beatmaps.ToArray(); overlay.ShowBeatmapSet(set); }); AddAssert("shown beatmaps of current ruleset", () => overlay.Header.HeaderContent.Picker.Difficulties.All(b => b.Beatmap.Ruleset.OnlineID == overlay.Header.RulesetSelector.Current.Value.OnlineID)); AddAssert("left-most beatmap selected", () => overlay.Header.HeaderContent.Picker.Difficulties.First().State == BeatmapPicker.DifficultySelectorState.Selected); } [Test] public void TestExplicitBeatmap() { AddStep("show explicit map", () => { var beatmapSet = getBeatmapSet(); beatmapSet.HasExplicitContent = true; overlay.ShowBeatmapSet(beatmapSet); }); } [Test] public void TestSpotlightBeatmap() { AddStep("show spotlight map", () => { var beatmapSet = getBeatmapSet(); beatmapSet.FeaturedInSpotlight = true; overlay.ShowBeatmapSet(beatmapSet); }); } [Test] public void TestFeaturedBeatmap() { AddStep("show featured map", () => { var beatmapSet = getBeatmapSet(); beatmapSet.TrackId = 1; overlay.ShowBeatmapSet(beatmapSet); }); } [Test] public void TestAllBadgesBeatmap() { AddStep("show map with all badges", () => { var beatmapSet = getBeatmapSet(); beatmapSet.HasExplicitContent = true; beatmapSet.FeaturedInSpotlight = true; beatmapSet.TrackId = 1; overlay.ShowBeatmapSet(beatmapSet); }); } [Test] public void TestSelectedModsDontAffectStatistics() { AddStep("show map", () => overlay.ShowBeatmapSet(getBeatmapSet())); AddAssert("AR displayed as 0", () => overlay.ChildrenOfType().Single(s => s.Title == BeatmapsetsStrings.ShowStatsAr).Value == (0, null)); AddStep("set AR10 diff adjust", () => SelectedMods.Value = new[] { new OsuModDifficultyAdjust { ApproachRate = { Value = 10 } } }); AddAssert("AR still displayed as 0", () => overlay.ChildrenOfType().Single(s => s.Title == BeatmapsetsStrings.ShowStatsAr).Value == (0, null)); } [Test] public void TestHide() { AddStep(@"hide", overlay.Hide); } [Test] public void TestShowWithNoReload() { AddStep(@"show without reload", overlay.Show); } [TestCase(BeatmapSetLookupType.BeatmapId)] [TestCase(BeatmapSetLookupType.SetId)] public void TestFetchLookupType(BeatmapSetLookupType lookupType) { string type = string.Empty; AddStep("register request handling", () => { ((DummyAPIAccess)API).HandleRequest = req => { switch (req) { case GetBeatmapSetRequest getBeatmapSet: type = getBeatmapSet.Type.ToString(); return true; } return false; }; }); AddStep(@"fetch", () => { switch (lookupType) { case BeatmapSetLookupType.BeatmapId: overlay.FetchAndShowBeatmap(55); break; case BeatmapSetLookupType.SetId: overlay.FetchAndShowBeatmapSet(55); break; } }); AddAssert(@"type is correct", () => type == lookupType.ToString()); } [Test] public void TestBeatmapSetWithGuestDifficulty() { AddStep("show map", () => overlay.ShowBeatmapSet(createBeatmapSetWithGuestDifficulty())); AddStep("move mouse to host difficulty", () => { InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(0)); }); AddAssert("guest mapper information not shown", () => overlay.ChildrenOfType().Single().ChildrenOfType().All(s => s.Text != "BanchoBot")); AddStep("move mouse to guest difficulty", () => { InputManager.MoveMouseTo(overlay.ChildrenOfType().ElementAt(1)); }); AddAssert("guest mapper information shown", () => overlay.ChildrenOfType().Single().ChildrenOfType().Any(s => s.Text == "BanchoBot")); } private APIBeatmapSet createManyDifficultiesBeatmapSet() { var set = getBeatmapSet(); var beatmaps = new List(); for (int i = 1; i < 41; i++) { beatmaps.Add(new APIBeatmap { OnlineID = i * 10, DifficultyName = $"Test #{i}", RulesetID = Ruleset.Value.OnlineID, StarRating = 2 + i * 0.1, OverallDifficulty = 3.5f, FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(), }, Status = i % 2 == 0 ? BeatmapOnlineStatus.Graveyard : BeatmapOnlineStatus.Loved, }); } set.Beatmaps = beatmaps.ToArray(); return set; } private APIBeatmapSet getBeatmapSet() { var beatmapSet = CreateAPIBeatmapSet(Ruleset.Value); // Make sure the overlay is reloaded (see `BeatmapSetInfo.Equals`). beatmapSet.OnlineID = nextBeatmapSetId++; return beatmapSet; } private APIBeatmapSet createBeatmapSetWithGuestDifficulty() { var set = getBeatmapSet(); var beatmaps = new List(); var guestUser = new APIUser { Username = @"BanchoBot", Id = 3, }; set.RelatedUsers = new[] { set.Author, guestUser }; beatmaps.Add(new APIBeatmap { OnlineID = 1145, DifficultyName = "Host Diff", RulesetID = Ruleset.Value.OnlineID, StarRating = 1.4, OverallDifficulty = 3.5f, AuthorID = set.AuthorID, FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(), }, Status = BeatmapOnlineStatus.Graveyard }); beatmaps.Add(new APIBeatmap { OnlineID = 1919, DifficultyName = "Guest Diff", RulesetID = Ruleset.Value.OnlineID, StarRating = 8.1, OverallDifficulty = 3.5f, AuthorID = 3, FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).Select(j => j % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(j => j % 12 - 6).ToArray(), }, Status = BeatmapOnlineStatus.Graveyard }); set.Beatmaps = beatmaps.ToArray(); return set; } private void downloadAssert(bool shown) { AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.Header.HeaderContent.DownloadButtonsVisible == shown); } private partial class TestBeatmapSetOverlay : BeatmapSetOverlay { public new BeatmapSetHeader Header => base.Header; } } }