<commit_before>// Copyright (c) ppy Pty Ltd <<EMAIL>>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

#nullable disable

using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.UI;

namespace osu.Game.Tests.Visual.Gameplay
{
    public class TestSceneFrameStabilityContainer : OsuTestScene
    {
        private readonly ManualClock manualClock;

        private readonly Container mainContainer;

        private ClockConsumingChild consumer;

        public TestSceneFrameStabilityContainer()
        {
            Child = mainContainer = new Container
            {
                RelativeSizeAxes = Axes.Both,
                Clock = new FramedClock(manualClock = new ManualClock()),
            };
        }

        [Test]
        public void TestLargeJumps()
        {
            seekManualTo(0);
            createStabilityContainer();
            seekManualTo(100000);

            confirmSeek(100000);
            checkFrameCount(6000);

            seekManualTo(0);

            confirmSeek(0);
            checkFrameCount(12000);
        }

        [Test]
        public void TestSmallJumps()
        {
            seekManualTo(0);
            createStabilityContainer();
            seekManualTo(40);

            confirmSeek(40);
            checkFrameCount(3);

            seekManualTo(0);

            confirmSeek(0);
            checkFrameCount(6);
        }

        [Test]
        public void TestSingleFrameJump()
        {
            seekManualTo(0);
            createStabilityContainer();
            seekManualTo(8);
            confirmSeek(8);
            checkFrameCount(1);

            seekManualTo(16);
            confirmSeek(16);
            checkFrameCount(2);
        }

        [Test]
        public void TestInitialSeekWithGameplayStart()
        {
            seekManualTo(1000);
            createStabilityContainer(30000);

            confirmSeek(1000);
            checkFrameCount(0);

            seekManualTo(10000);
            confirmSeek(10000);

            checkFrameCount(1);

            seekManualTo(130000);
            confirmSeek(130000);

            checkFrameCount(6002);
        }

        [Test]
        public void TestInitialSeek()
        {
            seekManualTo(100000);
            createStabilityContainer();

            confirmSeek(100000);
            checkFrameCount(0);
        }

        [Test]
        public void TestRatePreservedWhenTimeNotProgressing()
        {
            AddStep("set manual clock rate", () => manualClock.Rate = 1);
            seekManualTo(5000);
            createStabilityContainer();
            checkRate(1);

            seekManualTo(10000);
            checkRate(1);

            AddWaitStep("wait some", 3);
            checkRate(1);

            seekManualTo(5000);
            checkRate(-1);

            AddWaitStep("wait some", 3);
            checkRate(-1);

            seekManualTo(10000);
            checkRate(1);
        }

        private const int max_frames_catchup = 50;

        private void createStabilityContainer(double gameplayStartTime = double.MinValue) => AddStep("create container", () =>
            mainContainer.Child = new FrameStabilityContainer(gameplayStartTime) { MaxCatchUpFrames = max_frames_catchup }
                .WithChild(consumer = new ClockConsumingChild()));

        private void seekManualTo(double time) => AddStep($"seek manual clock to {time}", () => manualClock.CurrentTime = time);

        private void confirmSeek(double time) => AddUntilStep($"wait for seek to {time}", () => consumer.Clock.CurrentTime == time);

        private void checkFrameCount(int frames) =>
            AddAssert($"elapsed frames is {frames}", () => consumer.ElapsedFrames == frames);

        private void checkRate(double rate) =>
            AddAssert($"clock rate is {rate}", () => consumer.Clock.Rate == rate);

        public class ClockConsumingChild : CompositeDrawable
        {
            private readonly OsuSpriteText text;
            private readonly OsuSpriteText text2;
            private readonly OsuSpriteText text3;

            public ClockConsumingChild()
            {
                Anchor = Anchor.Centre;
                Origin = Anchor.Centre;

                InternalChildren = new Drawable[]
                {
                    new FillFlowContainer<commit_msg>Use constraint based assertions in `TestSceneFrameStabilityContainer`<commit_after>// Copyright (c) ppy Pty Ltd <<EMAIL>>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

#nullable disable

using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Timing;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.UI;

namespace osu.Game.Tests.Visual.Gameplay
{
    public class TestSceneFrameStabilityContainer : OsuTestScene
    {
        private readonly ManualClock manualClock;

        private readonly Container mainContainer;

        private ClockConsumingChild consumer;

        public TestSceneFrameStabilityContainer()
        {
            Child = mainContainer = new Container
            {
                RelativeSizeAxes = Axes.Both,
                Clock = new FramedClock(manualClock = new ManualClock()),
            };
        }

        [Test]
        public void TestLargeJumps()
        {
            seekManualTo(0);
            createStabilityContainer();
            seekManualTo(100000);

            confirmSeek(100000);
            checkFrameCount(6000);

            seekManualTo(0);

            confirmSeek(0);
            checkFrameCount(12000);
        }

        [Test]
        public void TestSmallJumps()
        {
            seekManualTo(0);
            createStabilityContainer();
            seekManualTo(40);

            confirmSeek(40);
            checkFrameCount(3);

            seekManualTo(0);

            confirmSeek(0);
            checkFrameCount(6);
        }

        [Test]
        public void TestSingleFrameJump()
        {
            seekManualTo(0);
            createStabilityContainer();
            seekManualTo(8);
            confirmSeek(8);
            checkFrameCount(1);

            seekManualTo(16);
            confirmSeek(16);
            checkFrameCount(2);
        }

        [Test]
        public void TestInitialSeekWithGameplayStart()
        {
            seekManualTo(1000);
            createStabilityContainer(30000);

            confirmSeek(1000);
            checkFrameCount(0);

            seekManualTo(10000);
            confirmSeek(10000);

            checkFrameCount(1);

            seekManualTo(130000);
            confirmSeek(130000);

            checkFrameCount(6002);
        }

        [Test]
        public void TestInitialSeek()
        {
            seekManualTo(100000);
            createStabilityContainer();

            confirmSeek(100000);
            checkFrameCount(0);
        }

        [Test]
        public void TestRatePreservedWhenTimeNotProgressing()
        {
            AddStep("set manual clock rate", () => manualClock.Rate = 1);
            seekManualTo(5000);
            createStabilityContainer();
            checkRate(1);

            seekManualTo(10000);
            checkRate(1);

            AddWaitStep("wait some", 3);
            checkRate(1);

            seekManualTo(5000);
            checkRate(-1);

            AddWaitStep("wait some", 3);
            checkRate(-1);

            seekManualTo(10000);
            checkRate(1);
        }

        private const int max_frames_catchup = 50;

        private void createStabilityContainer(double gameplayStartTime = double.MinValue) => AddStep("create container", () =>
            mainContainer.Child = new FrameStabilityContainer(gameplayStartTime) { MaxCatchUpFrames = max_frames_catchup }
                .WithChild(consumer = new ClockConsumingChild()));

        private void seekManualTo(double time) => AddStep($"seek manual clock to {time}", () => manualClock.CurrentTime = time);

        private void confirmSeek(double time) => AddUntilStep($"wait for seek to {time}", () => consumer.Clock.CurrentTime, () => Is.EqualTo(time));

        private void checkFrameCount(int frames) =>
            AddAssert($"elapsed frames is {frames}", () => consumer.ElapsedFrames, () => Is.EqualTo(frames));

        private void checkRate(double rate) =>
            AddAssert($"clock rate is {rate}", () => consumer.Clock.Rate, () => Is.EqualTo(rate));

        public class ClockConsumingChild : CompositeDrawable
        {
            private readonly OsuSpriteText text;
            private readonly OsuSpriteText text2;
            private readonly OsuSpriteText text3;

            public ClockConsumingChild()
            {
                Anchor = Anchor.Centre;
                Origin = Anchor.Centre;

                InternalChildren = new Drawable[]
                {
                    new FillFlowContainer
{'fraction_non_alphanumeric': 0.09090909090909091, 'fraction_numerical': 0.027820372398685653, 'mean_word_length': 1.8768115942028984, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 32, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7487715', 'n_tokens_mistral': 2568, 'n_tokens_neox': 2303, 'n_words': 488}