// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Framework.Timing; using osu.Game.Screens.Play; using osu.Game.Screens.Play.HUD.ClicksPerSecond; using osuTK; namespace osu.Game.Tests.Visual.Gameplay { public partial class TestSceneClicksPerSecondCalculator : OsuTestScene { private ClicksPerSecondCalculator calculator = null!; private TestGameplayClock manualGameplayClock = null!; [SetUpSteps] public void SetUpSteps() { AddStep("create components", () => { manualGameplayClock = new TestGameplayClock(); Child = new DependencyProvidingContainer { RelativeSizeAxes = Axes.Both, CachedDependencies = new (Type, object)[] { (typeof(IGameplayClock), manualGameplayClock) }, Children = new Drawable[] { calculator = new ClicksPerSecondCalculator(), new DependencyProvidingContainer { RelativeSizeAxes = Axes.Both, CachedDependencies = new (Type, object)[] { (typeof(ClicksPerSecondCalculator), calculator) }, Child = new ClicksPerSecondCounter { Anchor = Anchor.Centre, Origin = Anchor.Centre, Scale = new Vector2(5), } } }, }; }); } [Test] public void TestBasicConsistency() { seek(1000); AddStep("add inputs in past", () => addInputs(new double[] { 0, 100, 200, 300, 400, 500, 600, 700, 800, 900 })); checkClicksPerSecondValue(10); } [Test] public void TestRateAdjustConsistency() { seek(1000); AddStep("add inputs in past", () => addInputs(new double[] { 0, 100, 200, 300, 400, 500, 600, 700, 800, 900 })); checkClicksPerSecondValue(10); AddStep("set rate 0.5x", () => manualGameplayClock.TrueGameplayRate = 0.5); checkClicksPerSecondValue(5); } [Test] public void TestInputsDiscardedOnRewind() { seek(1000); AddStep("add inputs in past", () => addInputs(new double[] { 0, 100, 200, 300, 400, 500, 600, 700, 800, 900 })); checkClicksPerSecondValue(10); seek(500); checkClicksPerSecondValue(6); seek(1000); checkClicksPerSecondValue(6); } private void checkClicksPerSecondValue(int i) => AddAssert("clicks/s is correct", () => calculator.Value, () => Is.EqualTo(i)); private void seekClockImmediately(double time) => manualGameplayClock.CurrentTime = time; private void seek(double time) => AddStep($"Seek to {time}ms", () => seekClockImmediately(time)); private void addInputs(IEnumerable inputs) { double baseTime = manualGameplayClock.CurrentTime; foreach (double timestamp in inputs) { seekClockImmediately(timestamp); calculator.AddInputTimestamp(); } seekClockImmediately(baseTime); } private class TestGameplayClock : IGameplayClock { public double CurrentTime { get; set; } public double Rate => 1; public bool IsRunning => true; public double TrueGameplayRate { set => adjustableAudioComponent.Tempo.Value = value; } private readonly AudioAdjustments adjustableAudioComponent = new AudioAdjustments(); public void ProcessFrame() { } public double ElapsedFrameTime => throw new NotImplementedException(); public double FramesPerSecond => throw new NotImplementedException(); public FrameTimeInfo TimeInfo => throw new NotImplementedException(); public double StartTime => throw new NotImplementedException(); public IAdjustableAudioComponent AdjustmentsFromMods => adjustableAudioComponent; public IEnumerable NonGameplayAdjustments => throw new NotImplementedException(); public IBindable IsPaused => throw new NotImplementedException(); } } }