1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-15 09:22:54 +08:00

Add failing tests for pooling scrolling playfield

This commit is contained in:
ekrctb 2020-11-24 12:50:37 +09:00
parent d5a4d46c6d
commit df3844cdbb

View File

@ -21,13 +21,13 @@ using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using JetBrains.Annotations;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
@ -46,6 +46,65 @@ namespace osu.Game.Tests.Visual.Gameplay
[SetUp] [SetUp]
public void Setup() => Schedule(() => testClock.CurrentTime = 0); public void Setup() => Schedule(() => testClock.CurrentTime = 0);
[Test]
public void TestHitObjectPooling()
{
var beatmap = createBeatmap(_ => new TestPooledHitObject());
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range });
createTest(beatmap);
assertPosition(0, 0f);
assertDead(3);
setTime(3 * time_range);
assertPosition(3, 0f);
assertDead(0);
setTime(0 * time_range);
assertPosition(0, 0f);
assertDead(3);
}
[TestCase("pooled")]
[TestCase("non-pooled")]
public void TestNestedHitObject(string pooled)
{
var beatmap = createBeatmap(i =>
{
var h = pooled == "pooled" ? new TestPooledParentHitObject() : new TestParentHitObject();
h.Duration = 300;
h.ChildTimeOffset = i % 3 * 100;
return h;
});
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range });
createTest(beatmap);
assertPosition(0, 0f);
assertHeight(0);
assertChildPosition(0);
setTime(5 * time_range);
assertPosition(5, 0f);
assertHeight(5);
assertChildPosition(5);
}
private void assertDead(int index) => AddAssert($"hitobject {index} is dead", () => getDrawableHitObject(index) == null);
private void assertHeight(int index) => AddAssert($"hitobject {index} height", () =>
{
var d = getDrawableHitObject(index);
return d != null && Precision.AlmostEquals(d.DrawHeight, yScale * (float)(d.HitObject.Duration / time_range), 0.1f);
});
private void assertChildPosition(int index) => AddAssert($"hitobject {index} child position", () =>
{
var d = getDrawableHitObject(index);
return d is DrawableTestParentHitObject && Precision.AlmostEquals(
d.NestedHitObjects.First().DrawPosition.Y,
yScale * (float)((TestParentHitObject)d.HitObject).ChildTimeOffset / time_range, 0.1f);
});
[Test] [Test]
public void TestRelativeBeatLengthScaleSingleTimingPoint() public void TestRelativeBeatLengthScaleSingleTimingPoint()
{ {
@ -147,8 +206,21 @@ namespace osu.Game.Tests.Visual.Gameplay
assertPosition(1, 1); assertPosition(1, 1);
} }
/// <summary>
/// Get a <see cref="DrawableTestHitObject" /> corresponding to the <paramref name="index"/>'th <see cref="TestHitObject"/>.
/// When a pooling is used and the hit object is not alive, `null` is returned.
/// </summary>
[CanBeNull]
private DrawableTestHitObject getDrawableHitObject(int index)
{
var hitObject = drawableRuleset.Beatmap.HitObjects.ElementAt(index);
return (DrawableTestHitObject)drawableRuleset.Playfield.HitObjectContainer.Objects.FirstOrDefault(obj => obj.HitObject == hitObject);
}
private float yScale => drawableRuleset.Playfield.HitObjectContainer.DrawHeight;
private void assertPosition(int index, float relativeY) => AddAssert($"hitobject {index} at {relativeY}", private void assertPosition(int index, float relativeY) => AddAssert($"hitobject {index} at {relativeY}",
() => Precision.AlmostEquals(drawableRuleset.Playfield.AllHitObjects.ElementAt(index).DrawPosition.Y, drawableRuleset.Playfield.HitObjectContainer.DrawHeight * relativeY)); () => Precision.AlmostEquals(getDrawableHitObject(index)?.DrawPosition.Y ?? -1, yScale * relativeY));
private void setTime(double time) private void setTime(double time)
{ {
@ -160,12 +232,16 @@ namespace osu.Game.Tests.Visual.Gameplay
/// The hitobjects are spaced <see cref="time_range"/> milliseconds apart. /// The hitobjects are spaced <see cref="time_range"/> milliseconds apart.
/// </summary> /// </summary>
/// <returns>The <see cref="IBeatmap"/>.</returns> /// <returns>The <see cref="IBeatmap"/>.</returns>
private IBeatmap createBeatmap() private IBeatmap createBeatmap(Func<int, TestHitObject> createAction = null)
{ {
var beatmap = new Beatmap<HitObject> { BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo } }; var beatmap = new Beatmap<TestHitObject> { BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo } };
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
beatmap.HitObjects.Add(new HitObject { StartTime = i * time_range }); {
var h = createAction?.Invoke(i) ?? new TestHitObject();
h.StartTime = i * time_range;
beatmap.HitObjects.Add(h);
}
return beatmap; return beatmap;
} }
@ -225,7 +301,14 @@ namespace osu.Game.Tests.Visual.Gameplay
TimeRange.Value = time_range; TimeRange.Value = time_range;
} }
public override DrawableHitObject<TestHitObject> CreateDrawableRepresentation(TestHitObject h) => new DrawableTestHitObject(h); public override DrawableHitObject<TestHitObject> CreateDrawableRepresentation(TestHitObject h) =>
h switch
{
TestPooledHitObject _ => null,
TestPooledParentHitObject _ => null,
TestParentHitObject p => new DrawableTestParentHitObject(p),
_ => new DrawableTestHitObject(h),
};
protected override PassThroughInputManager CreateInputManager() => new PassThroughInputManager(); protected override PassThroughInputManager CreateInputManager() => new PassThroughInputManager();
@ -265,6 +348,9 @@ namespace osu.Game.Tests.Visual.Gameplay
} }
} }
}); });
RegisterPool<TestPooledHitObject, DrawableTestPooledHitObject>(1);
RegisterPool<TestPooledParentHitObject, DrawableTestPooledParentHitObject>(1);
} }
} }
@ -277,30 +363,46 @@ namespace osu.Game.Tests.Visual.Gameplay
public override bool CanConvert() => true; public override bool CanConvert() => true;
protected override IEnumerable<TestHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap, CancellationToken cancellationToken) protected override IEnumerable<TestHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap, CancellationToken cancellationToken) =>
{ throw new NotImplementedException();
yield return new TestHitObject
{
StartTime = original.StartTime,
Duration = (original as IHasDuration)?.Duration ?? 100
};
}
} }
#endregion #endregion
#region HitObject #region HitObject
private class TestHitObject : ConvertHitObject, IHasDuration private class TestHitObject : HitObject, IHasDuration
{ {
public double EndTime => StartTime + Duration; public double EndTime => StartTime + Duration;
public double Duration { get; set; } public double Duration { get; set; } = 100;
}
private class TestPooledHitObject : TestHitObject
{
}
private class TestParentHitObject : TestHitObject
{
public double ChildTimeOffset;
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
{
AddNested(new TestHitObject { StartTime = StartTime + ChildTimeOffset });
}
}
private class TestPooledParentHitObject : TestParentHitObject
{
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
{
AddNested(new TestPooledHitObject { StartTime = StartTime + ChildTimeOffset });
}
} }
private class DrawableTestHitObject : DrawableHitObject<TestHitObject> private class DrawableTestHitObject : DrawableHitObject<TestHitObject>
{ {
public DrawableTestHitObject(TestHitObject hitObject) public DrawableTestHitObject([CanBeNull] TestHitObject hitObject)
: base(hitObject) : base(hitObject)
{ {
Anchor = Anchor.TopCentre; Anchor = Anchor.TopCentre;
@ -326,6 +428,52 @@ namespace osu.Game.Tests.Visual.Gameplay
} }
} }
private class DrawableTestPooledHitObject : DrawableTestHitObject
{
public DrawableTestPooledHitObject()
: base(null)
{
InternalChildren[0].Colour = Color4.LightSkyBlue;
InternalChildren[1].Colour = Color4.Blue;
}
protected override void Update() => LifetimeEnd = HitObject.EndTime;
}
private class DrawableTestParentHitObject : DrawableTestHitObject
{
private readonly Container<DrawableHitObject> container;
public DrawableTestParentHitObject([CanBeNull] TestHitObject hitObject)
: base(hitObject)
{
InternalChildren[0].Colour = Color4.LightYellow;
InternalChildren[1].Colour = Color4.Yellow;
AddInternal(container = new Container<DrawableHitObject>
{
RelativeSizeAxes = Axes.Both,
});
}
protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) =>
new DrawableTestHitObject((TestHitObject)hitObject);
protected override void AddNestedHitObject(DrawableHitObject hitObject) => container.Add(hitObject);
protected override void ClearNestedHitObjects() => container.Clear(false);
}
private class DrawableTestPooledParentHitObject : DrawableTestParentHitObject
{
public DrawableTestPooledParentHitObject()
: base(null)
{
InternalChildren[0].Colour = Color4.LightSeaGreen;
InternalChildren[1].Colour = Color4.Green;
}
}
#endregion #endregion
} }
} }