2021-06-21 21:34:11 +08:00
|
|
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
|
|
|
using Moq;
|
|
|
|
using NUnit.Framework;
|
|
|
|
using osu.Game.Beatmaps;
|
|
|
|
using osu.Game.Rulesets.Edit;
|
|
|
|
using osu.Game.Rulesets.Objects;
|
|
|
|
using osu.Game.Rulesets.Objects.Types;
|
|
|
|
using osu.Game.Rulesets.Osu.Edit.Checks;
|
|
|
|
using osu.Game.Rulesets.Osu.Objects;
|
|
|
|
using osu.Game.Tests.Beatmaps;
|
|
|
|
using osuTK;
|
|
|
|
|
|
|
|
namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks
|
|
|
|
{
|
|
|
|
[TestFixture]
|
|
|
|
public class CheckTimeDistanceEqualityTest
|
|
|
|
{
|
2022-11-05 16:44:10 +08:00
|
|
|
private CheckTimeDistanceEquality check = null!;
|
2021-06-21 21:34:11 +08:00
|
|
|
|
|
|
|
[SetUp]
|
|
|
|
public void Setup()
|
|
|
|
{
|
|
|
|
check = new CheckTimeDistanceEquality();
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestCirclesEquidistant()
|
|
|
|
{
|
|
|
|
assertOk(new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(50, 0) },
|
|
|
|
new HitCircle { StartTime = 1000, Position = new Vector2(100, 0) },
|
|
|
|
new HitCircle { StartTime = 1500, Position = new Vector2(150, 0) }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestCirclesOneSlightlyOff()
|
|
|
|
{
|
|
|
|
assertWarning(new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(50, 0) },
|
|
|
|
new HitCircle { StartTime = 1000, Position = new Vector2(80, 0) }, // Distance a quite low compared to previous.
|
|
|
|
new HitCircle { StartTime = 1500, Position = new Vector2(130, 0) }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestCirclesOneOff()
|
|
|
|
{
|
|
|
|
assertProblem(new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(50, 0) },
|
|
|
|
new HitCircle { StartTime = 1000, Position = new Vector2(150, 0) }, // Twice the regular spacing.
|
|
|
|
new HitCircle { StartTime = 1500, Position = new Vector2(100, 0) }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestCirclesTwoOff()
|
|
|
|
{
|
|
|
|
assertProblem(new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(50, 0) },
|
|
|
|
new HitCircle { StartTime = 1000, Position = new Vector2(150, 0) }, // Twice the regular spacing.
|
|
|
|
new HitCircle { StartTime = 1500, Position = new Vector2(250, 0) } // Also twice the regular spacing.
|
|
|
|
}
|
|
|
|
}, count: 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestCirclesStacked()
|
|
|
|
{
|
|
|
|
assertOk(new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(50, 0) },
|
|
|
|
new HitCircle { StartTime = 1000, Position = new Vector2(50, 0) }, // Stacked, is fine.
|
|
|
|
new HitCircle { StartTime = 1500, Position = new Vector2(100, 0) }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestCirclesStacking()
|
|
|
|
{
|
|
|
|
assertWarning(new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(50, 0) },
|
|
|
|
new HitCircle { StartTime = 1000, Position = new Vector2(50, 0), StackHeight = 1 },
|
|
|
|
new HitCircle { StartTime = 1500, Position = new Vector2(50, 0), StackHeight = 2 },
|
|
|
|
new HitCircle { StartTime = 2000, Position = new Vector2(50, 0), StackHeight = 3 },
|
|
|
|
new HitCircle { StartTime = 2500, Position = new Vector2(50, 0), StackHeight = 4 }, // Ends up far from (50; 0), causing irregular spacing.
|
|
|
|
new HitCircle { StartTime = 3000, Position = new Vector2(100, 0) }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestCirclesHalfStack()
|
|
|
|
{
|
|
|
|
assertOk(new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(50, 0) },
|
|
|
|
new HitCircle { StartTime = 1000, Position = new Vector2(55, 0) }, // Basically stacked, so is fine.
|
|
|
|
new HitCircle { StartTime = 1500, Position = new Vector2(105, 0) }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestCirclesPartialOverlap()
|
|
|
|
{
|
|
|
|
assertProblem(new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(50, 0) },
|
|
|
|
new HitCircle { StartTime = 1000, Position = new Vector2(65, 0) }, // Really low distance compared to previous.
|
|
|
|
new HitCircle { StartTime = 1500, Position = new Vector2(115, 0) }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestCirclesSlightlyDifferent()
|
|
|
|
{
|
|
|
|
assertOk(new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
// Does not need to be perfect, as long as the distance is approximately correct it's sight-readable.
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(52, 0) },
|
|
|
|
new HitCircle { StartTime = 1000, Position = new Vector2(97, 0) },
|
|
|
|
new HitCircle { StartTime = 1500, Position = new Vector2(165, 0) }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestCirclesSlowlyChanging()
|
|
|
|
{
|
|
|
|
const float multiplier = 1.2f;
|
|
|
|
|
|
|
|
assertOk(new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(50, 0) },
|
|
|
|
new HitCircle { StartTime = 1000, Position = new Vector2(50 + 50 * multiplier, 0) },
|
|
|
|
// This gap would be a warning if it weren't for the previous pushing the average spacing up.
|
|
|
|
new HitCircle { StartTime = 1500, Position = new Vector2(50 + 50 * multiplier + 50 * multiplier * multiplier, 0) }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestCirclesQuicklyChanging()
|
|
|
|
{
|
|
|
|
const float multiplier = 1.6f;
|
|
|
|
|
|
|
|
var beatmap = new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(50, 0) },
|
|
|
|
new HitCircle { StartTime = 1000, Position = new Vector2(50 + 50 * multiplier, 0) }, // Warning
|
|
|
|
new HitCircle { StartTime = 1500, Position = new Vector2(50 + 50 * multiplier + 50 * multiplier * multiplier, 0) } // Problem
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap), DifficultyRating.Easy);
|
|
|
|
var issues = check.Run(context).ToList();
|
|
|
|
|
|
|
|
Assert.That(issues, Has.Count.EqualTo(2));
|
|
|
|
Assert.That(issues.First().Template is CheckTimeDistanceEquality.IssueTemplateIrregularSpacingWarning);
|
|
|
|
Assert.That(issues.Last().Template is CheckTimeDistanceEquality.IssueTemplateIrregularSpacingProblem);
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestCirclesTooFarApart()
|
|
|
|
{
|
|
|
|
assertOk(new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(50, 0) },
|
|
|
|
new HitCircle { StartTime = 4000, Position = new Vector2(200, 0) }, // 2 seconds apart from previous, so can start from wherever.
|
|
|
|
new HitCircle { StartTime = 4500, Position = new Vector2(250, 0) }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestCirclesOneOffExpert()
|
|
|
|
{
|
|
|
|
assertOk(new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(50, 0) },
|
|
|
|
new HitCircle { StartTime = 1000, Position = new Vector2(150, 0) }, // Jumps are allowed in higher difficulties.
|
|
|
|
new HitCircle { StartTime = 1500, Position = new Vector2(100, 0) }
|
|
|
|
}
|
|
|
|
}, DifficultyRating.Expert);
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestSpinner()
|
|
|
|
{
|
|
|
|
assertOk(new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(50, 0) },
|
|
|
|
new Spinner { StartTime = 500, EndTime = 1000 }, // Distance to and from the spinner should be ignored. If it isn't this should give a problem.
|
|
|
|
new HitCircle { StartTime = 1500, Position = new Vector2(100, 0) },
|
|
|
|
new HitCircle { StartTime = 2000, Position = new Vector2(150, 0) }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestSliders()
|
|
|
|
{
|
|
|
|
assertOk(new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(50, 0) },
|
|
|
|
getSliderMock(startTime: 1000, endTime: 1500, startPosition: new Vector2(100, 0), endPosition: new Vector2(150, 0)).Object,
|
|
|
|
getSliderMock(startTime: 2000, endTime: 2500, startPosition: new Vector2(200, 0), endPosition: new Vector2(250, 0)).Object,
|
|
|
|
new HitCircle { StartTime = 2500, Position = new Vector2(300, 0) }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
public void TestSlidersOneOff()
|
|
|
|
{
|
|
|
|
assertProblem(new Beatmap<HitObject>
|
|
|
|
{
|
|
|
|
HitObjects = new List<HitObject>
|
|
|
|
{
|
|
|
|
new HitCircle { StartTime = 0, Position = new Vector2(0) },
|
|
|
|
new HitCircle { StartTime = 500, Position = new Vector2(50, 0) },
|
|
|
|
getSliderMock(startTime: 1000, endTime: 1500, startPosition: new Vector2(100, 0), endPosition: new Vector2(150, 0)).Object,
|
|
|
|
getSliderMock(startTime: 2000, endTime: 2500, startPosition: new Vector2(250, 0), endPosition: new Vector2(300, 0)).Object, // Twice the spacing.
|
|
|
|
new HitCircle { StartTime = 2500, Position = new Vector2(300, 0) }
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
private Mock<Slider> getSliderMock(double startTime, double endTime, Vector2 startPosition, Vector2 endPosition)
|
|
|
|
{
|
|
|
|
var mockSlider = new Mock<Slider>();
|
|
|
|
mockSlider.SetupGet(s => s.StartTime).Returns(startTime);
|
|
|
|
mockSlider.SetupGet(s => s.Position).Returns(startPosition);
|
|
|
|
mockSlider.SetupGet(s => s.EndPosition).Returns(endPosition);
|
|
|
|
mockSlider.As<IHasDuration>().Setup(d => d.EndTime).Returns(endTime);
|
|
|
|
|
|
|
|
return mockSlider;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void assertOk(IBeatmap beatmap, DifficultyRating difficultyRating = DifficultyRating.Easy)
|
|
|
|
{
|
|
|
|
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap), difficultyRating);
|
|
|
|
Assert.That(check.Run(context), Is.Empty);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void assertWarning(IBeatmap beatmap, int count = 1)
|
|
|
|
{
|
|
|
|
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap), DifficultyRating.Easy);
|
|
|
|
var issues = check.Run(context).ToList();
|
|
|
|
|
|
|
|
Assert.That(issues, Has.Count.EqualTo(count));
|
|
|
|
Assert.That(issues.All(issue => issue.Template is CheckTimeDistanceEquality.IssueTemplateIrregularSpacingWarning));
|
|
|
|
}
|
|
|
|
|
|
|
|
private void assertProblem(IBeatmap beatmap, int count = 1)
|
|
|
|
{
|
|
|
|
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap), DifficultyRating.Easy);
|
|
|
|
var issues = check.Run(context).ToList();
|
|
|
|
|
|
|
|
Assert.That(issues, Has.Count.EqualTo(count));
|
|
|
|
Assert.That(issues.All(issue => issue.Template is CheckTimeDistanceEquality.IssueTemplateIrregularSpacingProblem));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|