diff --git a/osu.Game.Tests/NonVisual/ClosestBeatDivisorTest.cs b/osu.Game.Tests/NonVisual/ClosestBeatDivisorTest.cs new file mode 100644 index 0000000000..4d6986f5d2 --- /dev/null +++ b/osu.Game.Tests/NonVisual/ClosestBeatDivisorTest.cs @@ -0,0 +1,91 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Tests.NonVisual +{ + public class ClosestBeatDivisorTest + { + [Test] + public void TestExactDivisors() + { + var cpi = new ControlPointInfo(); + cpi.Add(-1000, new TimingControlPoint { BeatLength = 1000 }); + + double[] divisors = { 3, 1, 16, 12, 8, 6, 4, 3, 2, 1 }; + + assertClosestDivisors(divisors, divisors, cpi); + } + + [Test] + public void TestExactDivisorWithTempoChanges() + { + int offset = 0; + int[] beatLengths = { 1000, 200, 100, 50 }; + + var cpi = new ControlPointInfo(); + + foreach (int beatLength in beatLengths) + { + cpi.Add(offset, new TimingControlPoint { BeatLength = beatLength }); + offset += beatLength * 2; + } + + double[] divisors = { 3, 1, 16, 12, 8, 6, 4, 3 }; + + assertClosestDivisors(divisors, divisors, cpi); + } + + [Test] + public void TestExactDivisorsHighBPMStream() + { + var cpi = new ControlPointInfo(); + cpi.Add(-50, new TimingControlPoint { BeatLength = 50 }); // 1200 BPM 1/4 (limit testing) + + // A 1/4 stream should land on 1/1, 1/2 and 1/4 divisors. + double[] divisors = { 4, 4, 4, 4, 4, 4, 4, 4 }; + double[] closestDivisors = { 4, 2, 4, 1, 4, 2, 4, 1 }; + + assertClosestDivisors(divisors, closestDivisors, cpi, step: 1 / 4d); + } + + [Test] + public void TestApproximateDivisors() + { + var cpi = new ControlPointInfo(); + cpi.Add(-1000, new TimingControlPoint { BeatLength = 1000 }); + + double[] divisors = { 3.03d, 0.97d, 14, 13, 7.94d, 6.08d, 3.93d, 2.96d, 2.02d, 64 }; + double[] closestDivisors = { 3, 1, 16, 12, 8, 6, 4, 3, 2, 1 }; + + assertClosestDivisors(divisors, closestDivisors, cpi); + } + + private void assertClosestDivisors(IReadOnlyList divisors, IReadOnlyList closestDivisors, ControlPointInfo cpi, double step = 1) + { + List hitobjects = new List(); + double offset = cpi.TimingPoints[0].Time; + + for (int i = 0; i < divisors.Count; ++i) + { + double beatLength = cpi.TimingPointAt(offset).BeatLength; + hitobjects.Add(new HitObject { StartTime = offset + beatLength / divisors[i] }); + offset += beatLength * step; + } + + var beatmap = new Beatmap + { + HitObjects = hitobjects, + ControlPointInfo = cpi + }; + + for (int i = 0; i < divisors.Count; ++i) + Assert.AreEqual(closestDivisors[i], beatmap.ClosestBeatDivisor(beatmap.HitObjects[i].StartTime), $"at index {i}"); + } + } +}