// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable disable using System; using System.Collections.Generic; using JetBrains.Annotations; using osu.Game.Beatmaps; using osu.Game.Online.API.Requests.Responses; using osu.Game.Replays; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Replays; using osu.Game.Scoring; namespace osu.Game.Tests.Visual { public abstract class ModTestScene : PlayerTestScene { protected sealed override bool HasCustomSteps => true; private ModTestData currentTestData; protected void CreateModTest(ModTestData testData) => CreateTest(() => { AddStep("set test data", () => currentTestData = testData); }); public override void TearDownSteps() { AddUntilStep("test passed", () => { if (currentTestData == null) return true; return currentTestData.PassCondition?.Invoke() ?? false; }); base.TearDownSteps(); } protected sealed override IBeatmap CreateBeatmap(RulesetInfo ruleset) => currentTestData?.Beatmap ?? base.CreateBeatmap(ruleset); protected sealed override TestPlayer CreatePlayer(Ruleset ruleset) { var mods = new List(SelectedMods.Value); if (currentTestData.Mods != null) mods.AddRange(currentTestData.Mods); if (currentTestData.Autoplay) mods.Add(ruleset.GetAutoplayMod()); SelectedMods.Value = mods; return CreateModPlayer(ruleset); } protected virtual TestPlayer CreateModPlayer(Ruleset ruleset) => new ModTestPlayer(currentTestData, AllowFail); protected class ModTestPlayer : TestPlayer { private readonly bool allowFail; private readonly ModTestData currentTestData; protected override bool CheckModsAllowFailure() => allowFail; public ModTestPlayer(ModTestData data, bool allowFail) : base(false, false) { this.allowFail = allowFail; currentTestData = data; } protected override void PrepareReplay() { if (currentTestData.Autoplay && currentTestData.ReplayFrames?.Count > 0) throw new InvalidOperationException(@$"{nameof(ModTestData.Autoplay)} must be false when {nameof(ModTestData.ReplayFrames)} is specified."); if (currentTestData.ReplayFrames != null) { DrawableRuleset?.SetReplayScore(new Score { Replay = new Replay { Frames = currentTestData.ReplayFrames }, ScoreInfo = new ScoreInfo { User = new APIUser { Username = @"Test" } }, }); } base.PrepareReplay(); } } protected class ModTestData { /// /// Whether to use a replay to simulate an auto-play. True by default. /// public bool Autoplay = true; /// /// The frames to use for replay. must be set to false. /// [CanBeNull] public List ReplayFrames; /// /// The beatmap for this test case. /// [CanBeNull] public IBeatmap Beatmap; /// /// The conditions that cause this test case to pass. /// [CanBeNull] public Func PassCondition; /// /// The s this test case tests. /// public IReadOnlyList Mods; /// /// Convenience property for setting if only /// a single mod is to be tested. /// public Mod Mod { set => Mods = new[] { value }; } } } }