diff --git a/osu.Game.Tests/Visual/TestSceneReplayStability.cs b/osu.Game.Tests/Visual/TestSceneReplayStability.cs new file mode 100644 index 0000000000..749493c4b1 --- /dev/null +++ b/osu.Game.Tests/Visual/TestSceneReplayStability.cs @@ -0,0 +1,59 @@ +// Copyright (c) ppy Pty Ltd . 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.Game.Replays; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Scoring; +using osuTK; + +namespace osu.Game.Tests.Visual +{ + public partial class TestSceneReplayStability : ReplayStabilityTestScene + { + [Test] + public void TestOutrageouslyLargeLeadInTime() + { + // "graciously borrowed" from https://osu.ppy.sh/beatmapsets/948643#osu/1981090 + const double lead_in_time = 2147272727; + const double hit_circle_time = 100; + + var beatmap = new OsuBeatmap + { + HitObjects = + { + new HitCircle + { + StartTime = hit_circle_time, + Position = OsuPlayfield.BASE_SIZE / 2 + } + }, + AudioLeadIn = lead_in_time, + BeatmapInfo = + { + Ruleset = new OsuRuleset().RulesetInfo, + }, + }; + + var replay = new Replay + { + Frames = Enumerable.Range(0, 300).Select(t => new OsuReplayFrame(-lead_in_time + 40 * t, new Vector2(t), t % 2 == 0 ? [] : [OsuAction.LeftButton])) + .Concat([ + new OsuReplayFrame(0, OsuPlayfield.BASE_SIZE / 2), + new OsuReplayFrame(hit_circle_time, OsuPlayfield.BASE_SIZE / 2, OsuAction.LeftButton), + new OsuReplayFrame(hit_circle_time + 20, OsuPlayfield.BASE_SIZE / 2), + ]) + .Cast() + .ToList(), + }; + + RunTest(beatmap, replay, [HitResult.Great]); + } + } +} diff --git a/osu.Game/Tests/Visual/ReplayStabilityTestScene.cs b/osu.Game/Tests/Visual/ReplayStabilityTestScene.cs index 13abedf611..af41617a7b 100644 --- a/osu.Game/Tests/Visual/ReplayStabilityTestScene.cs +++ b/osu.Game/Tests/Visual/ReplayStabilityTestScene.cs @@ -19,7 +19,7 @@ using osu.Game.Screens.Play; namespace osu.Game.Tests.Visual { /// - /// The goal of this abstract test class is to ensure that the process of exporting of a replay does not affect its playback. + /// The goal of this abstract test class is to ensure that the process of exporting and re-importing of a replay does not affect its playback. /// Use to exercise that property. /// [HeadlessTest] @@ -51,6 +51,7 @@ namespace osu.Game.Tests.Visual AddStep(@"push player", () => pushNewPlayer(originalScore)); AddUntilStep(@"wait until player is loaded", () => currentPlayer.IsCurrentScreen()); + skipIntroIfPresent(); AddUntilStep(@"wait for completion", () => currentPlayer.GameplayState.HasCompleted); AddAssert(@"judgement results before encode are correct", () => results.Select(r => r.Type), () => Is.EquivalentTo(expectedResults)); @@ -71,6 +72,7 @@ namespace osu.Game.Tests.Visual AddStep(@"push player", () => pushNewPlayer(decodedScore)); AddUntilStep(@"Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); + skipIntroIfPresent(); AddUntilStep(@"Wait for completion", () => currentPlayer.GameplayState.HasCompleted); AddAssert(@"judgement results after encode are correct", () => results.Select(r => r.Type), () => Is.EquivalentTo(expectedResults)); } @@ -90,6 +92,13 @@ namespace osu.Game.Tests.Visual results.Clear(); } + private void skipIntroIfPresent() => + AddStep(@"skip intro if present", () => + { + if (currentPlayer.ChildrenOfType().Single().CurrentTime < 0) + currentPlayer.Seek(0); + }); + private class TestScoreDecoder : LegacyScoreDecoder { private readonly WorkingBeatmap beatmap;