mirror of
https://github.com/osukey/osukey.git
synced 2025-08-04 07:06:35 +09:00
Merge branch 'master' into keyboard_shortcuts
This commit is contained in:
@ -10,7 +10,7 @@ using osu.Game.Rulesets.Osu.Mods;
|
||||
namespace osu.Game.Tests.Beatmaps
|
||||
{
|
||||
[TestFixture]
|
||||
public class BeatmapDifficultyManagerTest
|
||||
public class BeatmapDifficultyCacheTest
|
||||
{
|
||||
[Test]
|
||||
public void TestKeyEqualsWithDifferentModInstances()
|
@ -707,6 +707,69 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.That(third.ControlPoints[5].Type.Value, Is.EqualTo(null));
|
||||
Assert.That(third.ControlPoints[6].Position.Value, Is.EqualTo(new Vector2(480, 0)));
|
||||
Assert.That(third.ControlPoints[6].Type.Value, Is.EqualTo(null));
|
||||
|
||||
// Last control point duplicated
|
||||
var fourth = ((IHasPath)decoded.HitObjects[3]).Path;
|
||||
|
||||
Assert.That(fourth.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(fourth.ControlPoints[0].Type.Value, Is.EqualTo(PathType.Bezier));
|
||||
Assert.That(fourth.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(1, 1)));
|
||||
Assert.That(fourth.ControlPoints[1].Type.Value, Is.EqualTo(null));
|
||||
Assert.That(fourth.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(2, 2)));
|
||||
Assert.That(fourth.ControlPoints[2].Type.Value, Is.EqualTo(null));
|
||||
Assert.That(fourth.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(3, 3)));
|
||||
Assert.That(fourth.ControlPoints[3].Type.Value, Is.EqualTo(null));
|
||||
Assert.That(fourth.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(3, 3)));
|
||||
Assert.That(fourth.ControlPoints[4].Type.Value, Is.EqualTo(null));
|
||||
|
||||
// Last control point in segment duplicated
|
||||
var fifth = ((IHasPath)decoded.HitObjects[4]).Path;
|
||||
|
||||
Assert.That(fifth.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(fifth.ControlPoints[0].Type.Value, Is.EqualTo(PathType.Bezier));
|
||||
Assert.That(fifth.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(1, 1)));
|
||||
Assert.That(fifth.ControlPoints[1].Type.Value, Is.EqualTo(null));
|
||||
Assert.That(fifth.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(2, 2)));
|
||||
Assert.That(fifth.ControlPoints[2].Type.Value, Is.EqualTo(null));
|
||||
Assert.That(fifth.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(3, 3)));
|
||||
Assert.That(fifth.ControlPoints[3].Type.Value, Is.EqualTo(null));
|
||||
Assert.That(fifth.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(3, 3)));
|
||||
Assert.That(fifth.ControlPoints[4].Type.Value, Is.EqualTo(null));
|
||||
|
||||
Assert.That(fifth.ControlPoints[5].Position.Value, Is.EqualTo(new Vector2(4, 4)));
|
||||
Assert.That(fifth.ControlPoints[5].Type.Value, Is.EqualTo(PathType.Bezier));
|
||||
Assert.That(fifth.ControlPoints[6].Position.Value, Is.EqualTo(new Vector2(5, 5)));
|
||||
Assert.That(fifth.ControlPoints[6].Type.Value, Is.EqualTo(null));
|
||||
|
||||
// Implicit perfect-curve multi-segment(Should convert to bezier to match stable)
|
||||
var sixth = ((IHasPath)decoded.HitObjects[5]).Path;
|
||||
|
||||
Assert.That(sixth.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(sixth.ControlPoints[0].Type.Value == PathType.Bezier);
|
||||
Assert.That(sixth.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(75, 145)));
|
||||
Assert.That(sixth.ControlPoints[1].Type.Value == null);
|
||||
Assert.That(sixth.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(170, 75)));
|
||||
|
||||
Assert.That(sixth.ControlPoints[2].Type.Value == PathType.Bezier);
|
||||
Assert.That(sixth.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(300, 145)));
|
||||
Assert.That(sixth.ControlPoints[3].Type.Value == null);
|
||||
Assert.That(sixth.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(410, 20)));
|
||||
Assert.That(sixth.ControlPoints[4].Type.Value == null);
|
||||
|
||||
// Explicit perfect-curve multi-segment(Should not convert to bezier)
|
||||
var seventh = ((IHasPath)decoded.HitObjects[6]).Path;
|
||||
|
||||
Assert.That(seventh.ControlPoints[0].Position.Value, Is.EqualTo(Vector2.Zero));
|
||||
Assert.That(seventh.ControlPoints[0].Type.Value == PathType.PerfectCurve);
|
||||
Assert.That(seventh.ControlPoints[1].Position.Value, Is.EqualTo(new Vector2(75, 145)));
|
||||
Assert.That(seventh.ControlPoints[1].Type.Value == null);
|
||||
Assert.That(seventh.ControlPoints[2].Position.Value, Is.EqualTo(new Vector2(170, 75)));
|
||||
|
||||
Assert.That(seventh.ControlPoints[2].Type.Value == PathType.PerfectCurve);
|
||||
Assert.That(seventh.ControlPoints[3].Position.Value, Is.EqualTo(new Vector2(300, 145)));
|
||||
Assert.That(seventh.ControlPoints[3].Type.Value == null);
|
||||
Assert.That(seventh.ControlPoints[4].Position.Value, Is.EqualTo(new Vector2(410, 20)));
|
||||
Assert.That(seventh.ControlPoints[4].Type.Value == null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,14 @@ using osu.Game.IO;
|
||||
using osu.Game.IO.Serialization;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Taiko;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.Formats
|
||||
{
|
||||
@ -45,6 +49,33 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.IsTrue(areComboColoursEqual(decodedAfterEncode.beatmapSkin.Configuration, decoded.beatmapSkin.Configuration));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEncodeMultiSegmentSliderWithFloatingPointError()
|
||||
{
|
||||
var beatmap = new Beatmap
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new Slider
|
||||
{
|
||||
Position = new Vector2(0.6f),
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(Vector2.Zero, PathType.Bezier),
|
||||
new PathControlPoint(new Vector2(0.5f)),
|
||||
new PathControlPoint(new Vector2(0.51f)), // This is actually on the same position as the previous one in legacy beatmaps (truncated to int).
|
||||
new PathControlPoint(new Vector2(1f), PathType.Bezier),
|
||||
new PathControlPoint(new Vector2(2f))
|
||||
})
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
var decodedAfterEncode = decodeFromLegacy(encodeToLegacy((beatmap, new TestLegacySkin(beatmaps_resource_store, string.Empty))), string.Empty);
|
||||
var decodedSlider = (Slider)decodedAfterEncode.beatmap.HitObjects[0];
|
||||
Assert.That(decodedSlider.Path.ControlPoints.Count, Is.EqualTo(5));
|
||||
}
|
||||
|
||||
private bool areComboColoursEqual(IHasComboColours a, IHasComboColours b)
|
||||
{
|
||||
// equal to null, no need to SequenceEqual
|
||||
@ -137,6 +168,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
protected override Texture GetBackground() => throw new NotImplementedException();
|
||||
|
||||
protected override Track GetBeatmapTrack() => throw new NotImplementedException();
|
||||
|
||||
public override Stream GetStream(string storagePath) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,6 +95,26 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOutOfOrderStartTimes()
|
||||
{
|
||||
var decoder = new LegacyStoryboardDecoder();
|
||||
|
||||
using (var resStream = TestResources.OpenResource("out-of-order-starttimes.osb"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
{
|
||||
var storyboard = decoder.Decode(stream);
|
||||
|
||||
StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
|
||||
Assert.AreEqual(2, background.Elements.Count);
|
||||
|
||||
Assert.AreEqual(1500, background.Elements[0].StartTime);
|
||||
Assert.AreEqual(1000, background.Elements[1].StartTime);
|
||||
|
||||
Assert.AreEqual(1000, storyboard.EarliestEventTime);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeVariableWithSuffix()
|
||||
{
|
||||
@ -109,5 +129,25 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
Assert.AreEqual(3456, ((StoryboardSprite)background.Elements.Single()).InitialPosition.X);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeOutOfRangeLoopAnimationType()
|
||||
{
|
||||
var decoder = new LegacyStoryboardDecoder();
|
||||
|
||||
using (var resStream = TestResources.OpenResource("animation-types.osb"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
{
|
||||
var storyboard = decoder.Decode(stream);
|
||||
|
||||
StoryboardLayer foreground = storyboard.Layers.Single(l => l.Depth == 0);
|
||||
Assert.AreEqual(AnimationLoopType.LoopForever, ((StoryboardAnimation)foreground.Elements[0]).LoopType);
|
||||
Assert.AreEqual(AnimationLoopType.LoopOnce, ((StoryboardAnimation)foreground.Elements[1]).LoopType);
|
||||
Assert.AreEqual(AnimationLoopType.LoopForever, ((StoryboardAnimation)foreground.Elements[2]).LoopType);
|
||||
Assert.AreEqual(AnimationLoopType.LoopOnce, ((StoryboardAnimation)foreground.Elements[3]).LoopType);
|
||||
Assert.AreEqual(AnimationLoopType.LoopForever, ((StoryboardAnimation)foreground.Elements[4]).LoopType);
|
||||
Assert.AreEqual(AnimationLoopType.LoopForever, ((StoryboardAnimation)foreground.Elements[5]).LoopType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ using osu.Framework.Platform;
|
||||
using osu.Game.IPC;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
@ -264,7 +265,7 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
|
||||
// change filename
|
||||
var firstFile = new FileInfo(Directory.GetFiles(extractedFolder).First());
|
||||
firstFile.MoveTo(Path.Combine(firstFile.DirectoryName, $"{firstFile.Name}-changed{firstFile.Extension}"));
|
||||
firstFile.MoveTo(Path.Combine(firstFile.DirectoryName.AsNonNull(), $"{firstFile.Name}-changed{firstFile.Extension}"));
|
||||
|
||||
using (var zip = ZipArchive.Create())
|
||||
{
|
||||
@ -852,6 +853,21 @@ namespace osu.Game.Tests.Beatmaps.IO
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<BeatmapSetInfo> LoadQuickOszIntoOsu(OsuGameBase osu)
|
||||
{
|
||||
var temp = TestResources.GetQuickTestBeatmapForImport();
|
||||
|
||||
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||
|
||||
var importedSet = await manager.Import(new ImportTask(temp));
|
||||
|
||||
ensureLoaded(osu);
|
||||
|
||||
waitForOrAssert(() => !File.Exists(temp), "Temporary file still exists after standard import", 5000);
|
||||
|
||||
return manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.ID);
|
||||
}
|
||||
|
||||
public static async Task<BeatmapSetInfo> LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false)
|
||||
{
|
||||
var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);
|
||||
|
@ -21,6 +21,27 @@ namespace osu.Game.Tests.Chat
|
||||
Assert.AreEqual(36, result.Links[0].Length);
|
||||
}
|
||||
|
||||
[TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123#osu/456")]
|
||||
[TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123#osu/456?whatever")]
|
||||
[TestCase(LinkAction.OpenBeatmap, "456", "https://dev.ppy.sh/beatmapsets/123/456")]
|
||||
[TestCase(LinkAction.External, null, "https://dev.ppy.sh/beatmapsets/abc/def")]
|
||||
[TestCase(LinkAction.OpenBeatmapSet, "123", "https://dev.ppy.sh/beatmapsets/123")]
|
||||
[TestCase(LinkAction.OpenBeatmapSet, "123", "https://dev.ppy.sh/beatmapsets/123/whatever")]
|
||||
[TestCase(LinkAction.External, null, "https://dev.ppy.sh/beatmapsets/abc")]
|
||||
public void TestBeatmapLinks(LinkAction expectedAction, string expectedArg, string link)
|
||||
{
|
||||
MessageFormatter.WebsiteRootUrl = "dev.ppy.sh";
|
||||
|
||||
Message result = MessageFormatter.FormatMessage(new Message { Content = link });
|
||||
|
||||
Assert.AreEqual(result.Content, result.DisplayContent);
|
||||
Assert.AreEqual(1, result.Links.Count);
|
||||
Assert.AreEqual(expectedAction, result.Links[0].Action);
|
||||
Assert.AreEqual(expectedArg, result.Links[0].Argument);
|
||||
if (expectedAction == LinkAction.External)
|
||||
Assert.AreEqual(link, result.Links[0].Url);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMultipleComplexLinks()
|
||||
{
|
||||
|
120
osu.Game.Tests/Editing/Checks/CheckAudioQualityTest.cs
Normal file
120
osu.Game.Tests/Editing/Checks/CheckAudioQualityTest.cs
Normal file
@ -0,0 +1,120 @@
|
||||
// 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.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Checks;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Tests.Editing.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class CheckAudioQualityTest
|
||||
{
|
||||
private CheckAudioQuality check;
|
||||
private IBeatmap beatmap;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
check = new CheckAudioQuality();
|
||||
beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata { AudioFile = "abc123.jpg" }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMissing()
|
||||
{
|
||||
// While this is a problem, it is out of scope for this check and is caught by a different one.
|
||||
beatmap.Metadata.AudioFile = null;
|
||||
|
||||
var mock = new Mock<IWorkingBeatmap>();
|
||||
mock.SetupGet(w => w.Beatmap).Returns(beatmap);
|
||||
mock.SetupGet(w => w.Track).Returns((Track)null);
|
||||
|
||||
Assert.That(check.Run(new BeatmapVerifierContext(beatmap, mock.Object)), Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAcceptable()
|
||||
{
|
||||
var context = getContext(192);
|
||||
|
||||
Assert.That(check.Run(context), Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNullBitrate()
|
||||
{
|
||||
var context = getContext(null);
|
||||
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateNoBitrate);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestZeroBitrate()
|
||||
{
|
||||
var context = getContext(0);
|
||||
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateNoBitrate);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTooHighBitrate()
|
||||
{
|
||||
var context = getContext(320);
|
||||
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateTooHighBitrate);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTooLowBitrate()
|
||||
{
|
||||
var context = getContext(64);
|
||||
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckAudioQuality.IssueTemplateTooLowBitrate);
|
||||
}
|
||||
|
||||
private BeatmapVerifierContext getContext(int? audioBitrate)
|
||||
{
|
||||
return new BeatmapVerifierContext(beatmap, getMockWorkingBeatmap(audioBitrate).Object);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the mock of the working beatmap with the given audio properties.
|
||||
/// </summary>
|
||||
/// <param name="audioBitrate">The bitrate of the audio file the beatmap uses.</param>
|
||||
private Mock<IWorkingBeatmap> getMockWorkingBeatmap(int? audioBitrate)
|
||||
{
|
||||
var mockTrack = new Mock<Track>();
|
||||
mockTrack.SetupGet(t => t.Bitrate).Returns(audioBitrate);
|
||||
|
||||
var mockWorkingBeatmap = new Mock<IWorkingBeatmap>();
|
||||
mockWorkingBeatmap.SetupGet(w => w.Beatmap).Returns(beatmap);
|
||||
mockWorkingBeatmap.SetupGet(w => w.Track).Returns(mockTrack.Object);
|
||||
|
||||
return mockWorkingBeatmap;
|
||||
}
|
||||
}
|
||||
}
|
136
osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs
Normal file
136
osu.Game.Tests/Editing/Checks/CheckBackgroundQualityTest.cs
Normal file
@ -0,0 +1,136 @@
|
||||
// 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.IO;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Checks;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using FileInfo = osu.Game.IO.FileInfo;
|
||||
|
||||
namespace osu.Game.Tests.Editing.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class CheckBackgroundQualityTest
|
||||
{
|
||||
private CheckBackgroundQuality check;
|
||||
private IBeatmap beatmap;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
check = new CheckBackgroundQuality();
|
||||
beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata { BackgroundFile = "abc123.jpg" },
|
||||
BeatmapSet = new BeatmapSetInfo
|
||||
{
|
||||
Files = new List<BeatmapSetFileInfo>(new[]
|
||||
{
|
||||
new BeatmapSetFileInfo
|
||||
{
|
||||
Filename = "abc123.jpg",
|
||||
FileInfo = new FileInfo
|
||||
{
|
||||
Hash = "abcdef"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMissing()
|
||||
{
|
||||
// While this is a problem, it is out of scope for this check and is caught by a different one.
|
||||
beatmap.Metadata.BackgroundFile = null;
|
||||
var context = getContext(null, System.Array.Empty<byte>());
|
||||
|
||||
Assert.That(check.Run(context), Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAcceptable()
|
||||
{
|
||||
var context = getContext(new Texture(1920, 1080));
|
||||
|
||||
Assert.That(check.Run(context), Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTooHighResolution()
|
||||
{
|
||||
var context = getContext(new Texture(3840, 2160));
|
||||
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooHighResolution);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLowResolution()
|
||||
{
|
||||
var context = getContext(new Texture(640, 480));
|
||||
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateLowResolution);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTooLowResolution()
|
||||
{
|
||||
var context = getContext(new Texture(100, 100));
|
||||
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooLowResolution);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTooUncompressed()
|
||||
{
|
||||
var context = getContext(new Texture(1920, 1080), new byte[1024 * 1024 * 3]);
|
||||
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckBackgroundQuality.IssueTemplateTooUncompressed);
|
||||
}
|
||||
|
||||
private BeatmapVerifierContext getContext(Texture background, [CanBeNull] byte[] fileBytes = null)
|
||||
{
|
||||
return new BeatmapVerifierContext(beatmap, getMockWorkingBeatmap(background, fileBytes).Object);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the mock of the working beatmap with the given background and filesize.
|
||||
/// </summary>
|
||||
/// <param name="background">The texture of the background.</param>
|
||||
/// <param name="fileBytes">The bytes that represent the background file.</param>
|
||||
private Mock<IWorkingBeatmap> getMockWorkingBeatmap(Texture background, [CanBeNull] byte[] fileBytes = null)
|
||||
{
|
||||
var stream = new MemoryStream(fileBytes ?? new byte[1024 * 1024]);
|
||||
|
||||
var mock = new Mock<IWorkingBeatmap>();
|
||||
mock.SetupGet(w => w.Beatmap).Returns(beatmap);
|
||||
mock.SetupGet(w => w.Background).Returns(background);
|
||||
mock.Setup(w => w.GetStream(It.IsAny<string>())).Returns(stream);
|
||||
|
||||
return mock;
|
||||
}
|
||||
}
|
||||
}
|
194
osu.Game.Tests/Editing/Checks/CheckConcurrentObjectsTest.cs
Normal file
194
osu.Game.Tests/Editing/Checks/CheckConcurrentObjectsTest.cs
Normal file
@ -0,0 +1,194 @@
|
||||
// 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.Edit.Checks;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Tests.Editing.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class CheckConcurrentObjectsTest
|
||||
{
|
||||
private CheckConcurrentObjects check;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
check = new CheckConcurrentObjects();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCirclesSeparate()
|
||||
{
|
||||
assertOk(new List<HitObject>
|
||||
{
|
||||
new HitCircle { StartTime = 100 },
|
||||
new HitCircle { StartTime = 150 }
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCirclesConcurrent()
|
||||
{
|
||||
assertConcurrentSame(new List<HitObject>
|
||||
{
|
||||
new HitCircle { StartTime = 100 },
|
||||
new HitCircle { StartTime = 100 }
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCirclesAlmostConcurrent()
|
||||
{
|
||||
assertConcurrentSame(new List<HitObject>
|
||||
{
|
||||
new HitCircle { StartTime = 100 },
|
||||
new HitCircle { StartTime = 101 }
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSlidersSeparate()
|
||||
{
|
||||
assertOk(new List<HitObject>
|
||||
{
|
||||
getSliderMock(startTime: 100, endTime: 400.75d).Object,
|
||||
getSliderMock(startTime: 500, endTime: 900.75d).Object
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSlidersConcurrent()
|
||||
{
|
||||
assertConcurrentSame(new List<HitObject>
|
||||
{
|
||||
getSliderMock(startTime: 100, endTime: 400.75d).Object,
|
||||
getSliderMock(startTime: 300, endTime: 700.75d).Object
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSlidersAlmostConcurrent()
|
||||
{
|
||||
assertConcurrentSame(new List<HitObject>
|
||||
{
|
||||
getSliderMock(startTime: 100, endTime: 400.75d).Object,
|
||||
getSliderMock(startTime: 402, endTime: 902.75d).Object
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSliderAndCircleConcurrent()
|
||||
{
|
||||
assertConcurrentDifferent(new List<HitObject>
|
||||
{
|
||||
getSliderMock(startTime: 100, endTime: 400.75d).Object,
|
||||
new HitCircle { StartTime = 300 }
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestManyObjectsConcurrent()
|
||||
{
|
||||
var hitobjects = new List<HitObject>
|
||||
{
|
||||
getSliderMock(startTime: 100, endTime: 400.75d).Object,
|
||||
getSliderMock(startTime: 200, endTime: 500.75d).Object,
|
||||
new HitCircle { StartTime = 300 }
|
||||
};
|
||||
|
||||
var issues = check.Run(getContext(hitobjects)).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(3));
|
||||
Assert.That(issues.Where(issue => issue.Template is CheckConcurrentObjects.IssueTemplateConcurrentDifferent).ToList(), Has.Count.EqualTo(2));
|
||||
Assert.That(issues.Any(issue => issue.Template is CheckConcurrentObjects.IssueTemplateConcurrentSame));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHoldNotesSeparateOnSameColumn()
|
||||
{
|
||||
assertOk(new List<HitObject>
|
||||
{
|
||||
getHoldNoteMock(startTime: 100, endTime: 400.75d, column: 1).Object,
|
||||
getHoldNoteMock(startTime: 500, endTime: 900.75d, column: 1).Object
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHoldNotesConcurrentOnDifferentColumns()
|
||||
{
|
||||
assertOk(new List<HitObject>
|
||||
{
|
||||
getHoldNoteMock(startTime: 100, endTime: 400.75d, column: 1).Object,
|
||||
getHoldNoteMock(startTime: 300, endTime: 700.75d, column: 2).Object
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHoldNotesConcurrentOnSameColumn()
|
||||
{
|
||||
assertConcurrentSame(new List<HitObject>
|
||||
{
|
||||
getHoldNoteMock(startTime: 100, endTime: 400.75d, column: 1).Object,
|
||||
getHoldNoteMock(startTime: 300, endTime: 700.75d, column: 1).Object
|
||||
});
|
||||
}
|
||||
|
||||
private Mock<Slider> getSliderMock(double startTime, double endTime, int repeats = 0)
|
||||
{
|
||||
var mock = new Mock<Slider>();
|
||||
mock.SetupGet(s => s.StartTime).Returns(startTime);
|
||||
mock.As<IHasRepeats>().Setup(r => r.RepeatCount).Returns(repeats);
|
||||
mock.As<IHasDuration>().Setup(d => d.EndTime).Returns(endTime);
|
||||
|
||||
return mock;
|
||||
}
|
||||
|
||||
private Mock<HoldNote> getHoldNoteMock(double startTime, double endTime, int column)
|
||||
{
|
||||
var mock = new Mock<HoldNote>();
|
||||
mock.SetupGet(s => s.StartTime).Returns(startTime);
|
||||
mock.As<IHasDuration>().Setup(d => d.EndTime).Returns(endTime);
|
||||
mock.As<IHasColumn>().Setup(c => c.Column).Returns(column);
|
||||
|
||||
return mock;
|
||||
}
|
||||
|
||||
private void assertOk(List<HitObject> hitobjects)
|
||||
{
|
||||
Assert.That(check.Run(getContext(hitobjects)), Is.Empty);
|
||||
}
|
||||
|
||||
private void assertConcurrentSame(List<HitObject> hitobjects, int count = 1)
|
||||
{
|
||||
var issues = check.Run(getContext(hitobjects)).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(count));
|
||||
Assert.That(issues.All(issue => issue.Template is CheckConcurrentObjects.IssueTemplateConcurrentSame));
|
||||
}
|
||||
|
||||
private void assertConcurrentDifferent(List<HitObject> hitobjects, int count = 1)
|
||||
{
|
||||
var issues = check.Run(getContext(hitobjects)).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(count));
|
||||
Assert.That(issues.All(issue => issue.Template is CheckConcurrentObjects.IssueTemplateConcurrentDifferent));
|
||||
}
|
||||
|
||||
private BeatmapVerifierContext getContext(List<HitObject> hitobjects)
|
||||
{
|
||||
var beatmap = new Beatmap<HitObject> { HitObjects = hitobjects };
|
||||
return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
}
|
||||
}
|
||||
}
|
77
osu.Game.Tests/Editing/Checks/CheckFilePresenceTest.cs
Normal file
77
osu.Game.Tests/Editing/Checks/CheckFilePresenceTest.cs
Normal file
@ -0,0 +1,77 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Checks;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Tests.Editing.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class CheckFilePresenceTest
|
||||
{
|
||||
private CheckBackgroundPresence check;
|
||||
private IBeatmap beatmap;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
check = new CheckBackgroundPresence();
|
||||
beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
Metadata = new BeatmapMetadata { BackgroundFile = "abc123.jpg" },
|
||||
BeatmapSet = new BeatmapSetInfo
|
||||
{
|
||||
Files = new List<BeatmapSetFileInfo>(new[]
|
||||
{
|
||||
new BeatmapSetFileInfo
|
||||
{
|
||||
Filename = "abc123.jpg",
|
||||
FileInfo = new FileInfo { Hash = "abcdef" }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBackgroundSetAndInFiles()
|
||||
{
|
||||
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
Assert.That(check.Run(context), Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBackgroundSetAndNotInFiles()
|
||||
{
|
||||
beatmap.BeatmapInfo.BeatmapSet.Files.Clear();
|
||||
|
||||
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckFilePresence.IssueTemplateDoesNotExist);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBackgroundNotSet()
|
||||
{
|
||||
beatmap.Metadata.BackgroundFile = null;
|
||||
|
||||
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckFilePresence.IssueTemplateNoneSet);
|
||||
}
|
||||
}
|
||||
}
|
159
osu.Game.Tests/Editing/Checks/CheckUnsnappedObjectsTest.cs
Normal file
159
osu.Game.Tests/Editing/Checks/CheckUnsnappedObjectsTest.cs
Normal file
@ -0,0 +1,159 @@
|
||||
// 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.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Checks;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Tests.Editing.Checks
|
||||
{
|
||||
[TestFixture]
|
||||
public class CheckUnsnappedObjectsTest
|
||||
{
|
||||
private CheckUnsnappedObjects check;
|
||||
private ControlPointInfo cpi;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
check = new CheckUnsnappedObjects();
|
||||
|
||||
cpi = new ControlPointInfo();
|
||||
cpi.Add(100, new TimingControlPoint { BeatLength = 100 });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCircleSnapped()
|
||||
{
|
||||
assertOk(new List<HitObject>
|
||||
{
|
||||
new HitCircle { StartTime = 100 }
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCircleUnsnapped1Ms()
|
||||
{
|
||||
assert1Ms(new List<HitObject>
|
||||
{
|
||||
new HitCircle { StartTime = 101 }
|
||||
});
|
||||
|
||||
assert1Ms(new List<HitObject>
|
||||
{
|
||||
new HitCircle { StartTime = 99 }
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCircleUnsnapped2Ms()
|
||||
{
|
||||
assert2Ms(new List<HitObject>
|
||||
{
|
||||
new HitCircle { StartTime = 102 }
|
||||
});
|
||||
|
||||
assert2Ms(new List<HitObject>
|
||||
{
|
||||
new HitCircle { StartTime = 98 }
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSliderSnapped()
|
||||
{
|
||||
// Slider ends are naturally < 1 ms unsnapped because of how SV works.
|
||||
assertOk(new List<HitObject>
|
||||
{
|
||||
getSliderMock(startTime: 100, endTime: 400.75d).Object
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSliderUnsnapped1Ms()
|
||||
{
|
||||
assert1Ms(new List<HitObject>
|
||||
{
|
||||
getSliderMock(startTime: 101, endTime: 401.75d).Object
|
||||
}, count: 2);
|
||||
|
||||
// End is only off by 0.25 ms, hence count 1.
|
||||
assert1Ms(new List<HitObject>
|
||||
{
|
||||
getSliderMock(startTime: 99, endTime: 399.75d).Object
|
||||
}, count: 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSliderUnsnapped2Ms()
|
||||
{
|
||||
assert2Ms(new List<HitObject>
|
||||
{
|
||||
getSliderMock(startTime: 102, endTime: 402.75d).Object
|
||||
}, count: 2);
|
||||
|
||||
// Start and end are 2 ms and 1.25 ms off respectively, hence two different issues in one object.
|
||||
var hitObjects = new List<HitObject>
|
||||
{
|
||||
getSliderMock(startTime: 98, endTime: 398.75d).Object
|
||||
};
|
||||
|
||||
var issues = check.Run(getContext(hitObjects)).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(2));
|
||||
Assert.That(issues.Any(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateSmallUnsnap));
|
||||
Assert.That(issues.Any(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateLargeUnsnap));
|
||||
}
|
||||
|
||||
private Mock<Slider> getSliderMock(double startTime, double endTime, int repeats = 0)
|
||||
{
|
||||
var mockSlider = new Mock<Slider>();
|
||||
mockSlider.SetupGet(s => s.StartTime).Returns(startTime);
|
||||
mockSlider.As<IHasRepeats>().Setup(r => r.RepeatCount).Returns(repeats);
|
||||
mockSlider.As<IHasDuration>().Setup(d => d.EndTime).Returns(endTime);
|
||||
|
||||
return mockSlider;
|
||||
}
|
||||
|
||||
private void assertOk(List<HitObject> hitObjects)
|
||||
{
|
||||
Assert.That(check.Run(getContext(hitObjects)), Is.Empty);
|
||||
}
|
||||
|
||||
private void assert1Ms(List<HitObject> hitObjects, int count = 1)
|
||||
{
|
||||
var issues = check.Run(getContext(hitObjects)).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(count));
|
||||
Assert.That(issues.All(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateSmallUnsnap));
|
||||
}
|
||||
|
||||
private void assert2Ms(List<HitObject> hitObjects, int count = 1)
|
||||
{
|
||||
var issues = check.Run(getContext(hitObjects)).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(count));
|
||||
Assert.That(issues.All(issue => issue.Template is CheckUnsnappedObjects.IssueTemplateLargeUnsnap));
|
||||
}
|
||||
|
||||
private BeatmapVerifierContext getContext(List<HitObject> hitObjects)
|
||||
{
|
||||
var beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
ControlPointInfo = cpi,
|
||||
HitObjects = hitObjects
|
||||
};
|
||||
|
||||
return new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
}
|
||||
}
|
||||
}
|
175
osu.Game.Tests/Editing/TestSceneHitObjectContainerEventBuffer.cs
Normal file
175
osu.Game.Tests/Editing/TestSceneHitObjectContainerEventBuffer.cs
Normal file
@ -0,0 +1,175 @@
|
||||
// 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;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Edit.Compose;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.Editing
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestSceneHitObjectContainerEventBuffer : OsuTestScene
|
||||
{
|
||||
private readonly TestHitObject testObj = new TestHitObject();
|
||||
|
||||
private TestPlayfield playfield1;
|
||||
private TestPlayfield playfield2;
|
||||
private TestDrawable intermediateDrawable;
|
||||
private HitObjectUsageEventBuffer eventBuffer;
|
||||
|
||||
private HitObject beganUsage;
|
||||
private HitObject finishedUsage;
|
||||
private HitObject transferredUsage;
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
{
|
||||
reset();
|
||||
|
||||
if (eventBuffer != null)
|
||||
{
|
||||
eventBuffer.HitObjectUsageBegan -= onHitObjectUsageBegan;
|
||||
eventBuffer.HitObjectUsageFinished -= onHitObjectUsageFinished;
|
||||
eventBuffer.HitObjectUsageTransferred -= onHitObjectUsageTransferred;
|
||||
}
|
||||
|
||||
var topPlayfield = new TestPlayfield();
|
||||
topPlayfield.AddNested(playfield1 = new TestPlayfield());
|
||||
topPlayfield.AddNested(playfield2 = new TestPlayfield());
|
||||
|
||||
eventBuffer = new HitObjectUsageEventBuffer(topPlayfield);
|
||||
eventBuffer.HitObjectUsageBegan += onHitObjectUsageBegan;
|
||||
eventBuffer.HitObjectUsageFinished += onHitObjectUsageFinished;
|
||||
eventBuffer.HitObjectUsageTransferred += onHitObjectUsageTransferred;
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
topPlayfield,
|
||||
intermediateDrawable = new TestDrawable(),
|
||||
};
|
||||
});
|
||||
|
||||
private void onHitObjectUsageBegan(HitObject obj) => beganUsage = obj;
|
||||
|
||||
private void onHitObjectUsageFinished(HitObject obj) => finishedUsage = obj;
|
||||
|
||||
private void onHitObjectUsageTransferred(HitObject obj, DrawableHitObject drawableObj) => transferredUsage = obj;
|
||||
|
||||
[Test]
|
||||
public void TestUsageBeganAfterAdd()
|
||||
{
|
||||
AddStep("add hitobject", () => playfield1.Add(testObj));
|
||||
addCheckStep(began: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUsageFinishedAfterRemove()
|
||||
{
|
||||
AddStep("add hitobject", () => playfield1.Add(testObj));
|
||||
addResetStep();
|
||||
AddStep("remove hitobject", () => playfield1.Remove(testObj));
|
||||
addCheckStep(finished: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUsageTransferredWhenMovedBetweenPlayfields()
|
||||
{
|
||||
AddStep("add hitobject", () => playfield1.Add(testObj));
|
||||
addResetStep();
|
||||
AddStep("transfer hitobject to other playfield", () =>
|
||||
{
|
||||
playfield1.Remove(testObj);
|
||||
playfield2.Add(testObj);
|
||||
});
|
||||
|
||||
addCheckStep(transferred: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRemoveImmediatelyAfterUsageBegan()
|
||||
{
|
||||
AddStep("add hitobject and schedule removal", () =>
|
||||
{
|
||||
playfield1.Add(testObj);
|
||||
intermediateDrawable.Schedule(() => playfield1.Remove(testObj));
|
||||
});
|
||||
|
||||
addCheckStep(began: true, finished: true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRemoveImmediatelyAfterTransferred()
|
||||
{
|
||||
AddStep("add hitobject", () => playfield1.Add(testObj));
|
||||
addResetStep();
|
||||
AddStep("transfer hitobject to other playfield and schedule removal", () =>
|
||||
{
|
||||
playfield1.Remove(testObj);
|
||||
playfield2.Add(testObj);
|
||||
intermediateDrawable.Schedule(() => playfield2.Remove(testObj));
|
||||
});
|
||||
|
||||
addCheckStep(transferred: true, finished: true);
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
eventBuffer.Update();
|
||||
}
|
||||
|
||||
private void addResetStep() => AddStep("reset", reset);
|
||||
|
||||
private void reset()
|
||||
{
|
||||
beganUsage = null;
|
||||
finishedUsage = null;
|
||||
transferredUsage = null;
|
||||
}
|
||||
|
||||
private void addCheckStep(bool began = false, bool finished = false, bool transferred = false)
|
||||
=> AddAssert($"began = {began}, finished = {finished}, transferred = {transferred}",
|
||||
() => (beganUsage == testObj) == began && (finishedUsage == testObj) == finished && (transferredUsage == testObj) == transferred);
|
||||
|
||||
private class TestPlayfield : Playfield
|
||||
{
|
||||
public TestPlayfield()
|
||||
{
|
||||
RegisterPool<TestHitObject, TestDrawableHitObject>(1);
|
||||
}
|
||||
|
||||
public new void AddNested(Playfield playfield)
|
||||
{
|
||||
AddInternal(playfield);
|
||||
base.AddNested(playfield);
|
||||
}
|
||||
|
||||
protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject)
|
||||
{
|
||||
var entry = base.CreateLifetimeEntry(hitObject);
|
||||
entry.KeepAlive = true;
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestHitObject : HitObject
|
||||
{
|
||||
public override string ToString() => "TestHitObject";
|
||||
}
|
||||
|
||||
private class TestDrawableHitObject : DrawableHitObject
|
||||
{
|
||||
}
|
||||
|
||||
private class TestDrawable : Drawable
|
||||
{
|
||||
public new void Schedule(Action action) => base.Schedule(action);
|
||||
}
|
||||
}
|
||||
}
|
95
osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs
Normal file
95
osu.Game.Tests/Gameplay/TestSceneDrawableHitObject.cs
Normal file
@ -0,0 +1,95 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.Gameplay
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestSceneDrawableHitObject : OsuTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestEntryLifetime()
|
||||
{
|
||||
TestDrawableHitObject dho = null;
|
||||
var initialHitObject = new HitObject
|
||||
{
|
||||
StartTime = 1000
|
||||
};
|
||||
var entry = new TestLifetimeEntry(new HitObject
|
||||
{
|
||||
StartTime = 2000
|
||||
});
|
||||
|
||||
AddStep("Create DHO", () => Child = dho = new TestDrawableHitObject(initialHitObject));
|
||||
|
||||
AddAssert("Correct initial lifetime", () => dho.LifetimeStart == initialHitObject.StartTime - TestDrawableHitObject.INITIAL_LIFETIME_OFFSET);
|
||||
|
||||
AddStep("Apply entry", () => dho.Apply(entry));
|
||||
|
||||
AddAssert("Correct initial lifetime", () => dho.LifetimeStart == entry.HitObject.StartTime - TestLifetimeEntry.INITIAL_LIFETIME_OFFSET);
|
||||
|
||||
AddStep("Set lifetime", () => dho.LifetimeEnd = 3000);
|
||||
AddAssert("Entry lifetime is updated", () => entry.LifetimeEnd == 3000);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestKeepAlive()
|
||||
{
|
||||
TestDrawableHitObject dho = null;
|
||||
TestLifetimeEntry entry = null;
|
||||
AddStep("Create DHO", () =>
|
||||
{
|
||||
dho = new TestDrawableHitObject(null);
|
||||
dho.Apply(entry = new TestLifetimeEntry(new HitObject()));
|
||||
Child = dho;
|
||||
});
|
||||
|
||||
AddStep("KeepAlive = true", () =>
|
||||
{
|
||||
entry.LifetimeStart = 0;
|
||||
entry.LifetimeEnd = 1000;
|
||||
entry.KeepAlive = true;
|
||||
});
|
||||
AddAssert("Lifetime is overriden", () => entry.LifetimeStart == double.MinValue && entry.LifetimeEnd == double.MaxValue);
|
||||
|
||||
AddStep("Set LifetimeStart", () => dho.LifetimeStart = 500);
|
||||
AddStep("KeepAlive = false", () => entry.KeepAlive = false);
|
||||
AddAssert("Lifetime is correct", () => entry.LifetimeStart == 500 && entry.LifetimeEnd == 1000);
|
||||
|
||||
AddStep("Set LifetimeStart while KeepAlive", () =>
|
||||
{
|
||||
entry.KeepAlive = true;
|
||||
dho.LifetimeStart = double.MinValue;
|
||||
entry.KeepAlive = false;
|
||||
});
|
||||
AddAssert("Lifetime is changed", () => entry.LifetimeStart == double.MinValue && entry.LifetimeEnd == 1000);
|
||||
}
|
||||
|
||||
private class TestDrawableHitObject : DrawableHitObject
|
||||
{
|
||||
public const double INITIAL_LIFETIME_OFFSET = 100;
|
||||
protected override double InitialLifetimeOffset => INITIAL_LIFETIME_OFFSET;
|
||||
|
||||
public TestDrawableHitObject(HitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class TestLifetimeEntry : HitObjectLifetimeEntry
|
||||
{
|
||||
public const double INITIAL_LIFETIME_OFFSET = 200;
|
||||
protected override double InitialLifetimeOffset => INITIAL_LIFETIME_OFFSET;
|
||||
|
||||
public TestLifetimeEntry(HitObject hitObject)
|
||||
: base(hitObject)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.Gameplay
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestSceneGameplayClockContainer : OsuTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestStartThenElapsedTime()
|
||||
{
|
||||
GameplayClockContainer gcc = null;
|
||||
|
||||
AddStep("create container", () =>
|
||||
{
|
||||
var working = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
working.LoadTrack();
|
||||
|
||||
Add(gcc = new GameplayClockContainer(working, 0));
|
||||
});
|
||||
|
||||
AddStep("start track", () => gcc.Start());
|
||||
AddUntilStep("elapsed greater than zero", () => gcc.GameplayClock.ElapsedFrameTime > 0);
|
||||
}
|
||||
}
|
||||
}
|
@ -121,7 +121,7 @@ namespace osu.Game.Tests.Gameplay
|
||||
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => throw new NotImplementedException();
|
||||
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
|
@ -0,0 +1,58 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.Gameplay
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestSceneMasterGameplayClockContainer : OsuTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestStartThenElapsedTime()
|
||||
{
|
||||
GameplayClockContainer gcc = null;
|
||||
|
||||
AddStep("create container", () =>
|
||||
{
|
||||
var working = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
working.LoadTrack();
|
||||
|
||||
Add(gcc = new MasterGameplayClockContainer(working, 0));
|
||||
});
|
||||
|
||||
AddStep("start clock", () => gcc.Start());
|
||||
AddUntilStep("elapsed greater than zero", () => gcc.GameplayClock.ElapsedFrameTime > 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestElapseThenReset()
|
||||
{
|
||||
GameplayClockContainer gcc = null;
|
||||
|
||||
AddStep("create container", () =>
|
||||
{
|
||||
var working = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
working.LoadTrack();
|
||||
|
||||
Add(gcc = new MasterGameplayClockContainer(working, 0));
|
||||
});
|
||||
|
||||
AddStep("start clock", () => gcc.Start());
|
||||
AddUntilStep("current time greater 2000", () => gcc.GameplayClock.CurrentTime > 2000);
|
||||
|
||||
double timeAtReset = 0;
|
||||
AddStep("reset clock", () =>
|
||||
{
|
||||
timeAtReset = gcc.GameplayClock.CurrentTime;
|
||||
gcc.Reset();
|
||||
});
|
||||
|
||||
AddAssert("current time < time at reset", () => gcc.GameplayClock.CurrentTime < timeAtReset);
|
||||
}
|
||||
}
|
||||
}
|
85
osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs
Normal file
85
osu.Game.Tests/Gameplay/TestSceneProxyContainer.cs
Normal file
@ -0,0 +1,85 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.Gameplay
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestSceneProxyContainer : OsuTestScene
|
||||
{
|
||||
private HitObjectContainer hitObjectContainer;
|
||||
private ProxyContainer proxyContainer;
|
||||
private readonly ManualClock clock = new ManualClock();
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
Child = new Container
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
hitObjectContainer = new HitObjectContainer(),
|
||||
proxyContainer = new ProxyContainer()
|
||||
},
|
||||
Clock = new FramedClock(clock)
|
||||
};
|
||||
clock.CurrentTime = 0;
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestProxyLifetimeManagement()
|
||||
{
|
||||
AddStep("Add proxy drawables", () =>
|
||||
{
|
||||
addProxy(new TestDrawableHitObject(1000));
|
||||
addProxy(new TestDrawableHitObject(3000));
|
||||
addProxy(new TestDrawableHitObject(5000));
|
||||
});
|
||||
|
||||
AddStep("time = 1000", () => clock.CurrentTime = 1000);
|
||||
AddAssert("One proxy is alive", () => proxyContainer.AliveChildren.Count == 1);
|
||||
AddStep("time = 5000", () => clock.CurrentTime = 5000);
|
||||
AddAssert("One proxy is alive", () => proxyContainer.AliveChildren.Count == 1);
|
||||
AddStep("time = 6000", () => clock.CurrentTime = 6000);
|
||||
AddAssert("No proxy is alive", () => proxyContainer.AliveChildren.Count == 0);
|
||||
}
|
||||
|
||||
private void addProxy(DrawableHitObject drawableHitObject)
|
||||
{
|
||||
hitObjectContainer.Add(drawableHitObject);
|
||||
proxyContainer.AddProxy(drawableHitObject);
|
||||
}
|
||||
|
||||
private class ProxyContainer : LifetimeManagementContainer
|
||||
{
|
||||
public IReadOnlyList<Drawable> AliveChildren => AliveInternalChildren;
|
||||
|
||||
public void AddProxy(Drawable d) => AddInternal(d.CreateProxy());
|
||||
}
|
||||
|
||||
private class TestDrawableHitObject : DrawableHitObject
|
||||
{
|
||||
protected override double InitialLifetimeOffset => 100;
|
||||
|
||||
public TestDrawableHitObject(double startTime)
|
||||
: base(new HitObject { StartTime = startTime })
|
||||
{
|
||||
}
|
||||
|
||||
protected override void UpdateInitialTransforms()
|
||||
{
|
||||
LifetimeEnd = LifetimeStart + 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,13 +10,17 @@ using NUnit.Framework;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Graphics.Audio;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Storyboards;
|
||||
@ -27,15 +31,15 @@ using osu.Game.Tests.Visual;
|
||||
namespace osu.Game.Tests.Gameplay
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestSceneStoryboardSamples : OsuTestScene
|
||||
public class TestSceneStoryboardSamples : OsuTestScene, IStorageResourceProvider
|
||||
{
|
||||
[Test]
|
||||
public void TestRetrieveTopLevelSample()
|
||||
{
|
||||
ISkin skin = null;
|
||||
SampleChannel channel = null;
|
||||
ISample channel = null;
|
||||
|
||||
AddStep("create skin", () => skin = new TestSkin("test-sample", Audio));
|
||||
AddStep("create skin", () => skin = new TestSkin("test-sample", this));
|
||||
AddStep("retrieve sample", () => channel = skin.GetSample(new SampleInfo("test-sample")));
|
||||
|
||||
AddAssert("sample is non-null", () => channel != null);
|
||||
@ -45,9 +49,9 @@ namespace osu.Game.Tests.Gameplay
|
||||
public void TestRetrieveSampleInSubFolder()
|
||||
{
|
||||
ISkin skin = null;
|
||||
SampleChannel channel = null;
|
||||
ISample channel = null;
|
||||
|
||||
AddStep("create skin", () => skin = new TestSkin("folder/test-sample", Audio));
|
||||
AddStep("create skin", () => skin = new TestSkin("folder/test-sample", this));
|
||||
AddStep("retrieve sample", () => channel = skin.GetSample(new SampleInfo("folder/test-sample")));
|
||||
|
||||
AddAssert("sample is non-null", () => channel != null);
|
||||
@ -64,17 +68,47 @@ namespace osu.Game.Tests.Gameplay
|
||||
var working = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
working.LoadTrack();
|
||||
|
||||
Add(gameplayContainer = new GameplayClockContainer(working, 0));
|
||||
|
||||
gameplayContainer.Add(sample = new DrawableStoryboardSample(new StoryboardSampleInfo(string.Empty, 0, 1))
|
||||
Add(gameplayContainer = new MasterGameplayClockContainer(working, 0)
|
||||
{
|
||||
Clock = gameplayContainer.GameplayClock
|
||||
IsPaused = { Value = true },
|
||||
Child = new FrameStabilityContainer
|
||||
{
|
||||
Child = sample = new DrawableStoryboardSample(new StoryboardSampleInfo(string.Empty, 0, 1))
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
AddStep("reset clock", () => gameplayContainer.Start());
|
||||
|
||||
AddUntilStep("sample played", () => sample.RequestedPlaying);
|
||||
AddUntilStep("sample has lifetime end", () => sample.LifetimeEnd < double.MaxValue);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSampleHasLifetimeEndWithInitialClockTime()
|
||||
{
|
||||
GameplayClockContainer gameplayContainer = null;
|
||||
DrawableStoryboardSample sample = null;
|
||||
|
||||
AddStep("create container", () =>
|
||||
{
|
||||
var working = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
working.LoadTrack();
|
||||
|
||||
Add(gameplayContainer = new MasterGameplayClockContainer(working, 1000, true)
|
||||
{
|
||||
IsPaused = { Value = true },
|
||||
Child = new FrameStabilityContainer
|
||||
{
|
||||
Child = sample = new DrawableStoryboardSample(new StoryboardSampleInfo(string.Empty, 0, 1))
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
AddStep("start time", () => gameplayContainer.Start());
|
||||
|
||||
AddUntilStep("sample playback succeeded", () => sample.LifetimeEnd < double.MaxValue);
|
||||
AddUntilStep("sample not played", () => !sample.RequestedPlaying);
|
||||
AddUntilStep("sample has lifetime end", () => sample.LifetimeEnd < double.MaxValue);
|
||||
}
|
||||
|
||||
[TestCase(typeof(OsuModDoubleTime), 1.5)]
|
||||
@ -88,6 +122,7 @@ namespace osu.Game.Tests.Gameplay
|
||||
public void TestSamplePlaybackWithRateMods(Type expectedMod, double expectedRate)
|
||||
{
|
||||
GameplayClockContainer gameplayContainer = null;
|
||||
StoryboardSampleInfo sampleInfo = null;
|
||||
TestDrawableStoryboardSample sample = null;
|
||||
|
||||
Mod testedMod = Activator.CreateInstance(expectedMod) as Mod;
|
||||
@ -99,23 +134,23 @@ namespace osu.Game.Tests.Gameplay
|
||||
break;
|
||||
|
||||
case ModTimeRamp m:
|
||||
m.InitialRate.Value = m.FinalRate.Value = expectedRate;
|
||||
m.FinalRate.Value = m.InitialRate.Value = expectedRate;
|
||||
break;
|
||||
}
|
||||
|
||||
AddStep("setup storyboard sample", () =>
|
||||
{
|
||||
Beatmap.Value = new TestCustomSkinWorkingBeatmap(new OsuRuleset().RulesetInfo, Audio);
|
||||
Beatmap.Value = new TestCustomSkinWorkingBeatmap(new OsuRuleset().RulesetInfo, this);
|
||||
SelectedMods.Value = new[] { testedMod };
|
||||
|
||||
var beatmapSkinSourceContainer = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin);
|
||||
|
||||
Add(gameplayContainer = new GameplayClockContainer(Beatmap.Value, 0)
|
||||
Add(gameplayContainer = new MasterGameplayClockContainer(Beatmap.Value, 0)
|
||||
{
|
||||
Child = beatmapSkinSourceContainer
|
||||
});
|
||||
|
||||
beatmapSkinSourceContainer.Add(sample = new TestDrawableStoryboardSample(new StoryboardSampleInfo("test-sample", 1, 1))
|
||||
beatmapSkinSourceContainer.Add(sample = new TestDrawableStoryboardSample(sampleInfo = new StoryboardSampleInfo("test-sample", 1, 1))
|
||||
{
|
||||
Clock = gameplayContainer.GameplayClock
|
||||
});
|
||||
@ -123,13 +158,16 @@ namespace osu.Game.Tests.Gameplay
|
||||
|
||||
AddStep("start", () => gameplayContainer.Start());
|
||||
|
||||
AddAssert("sample playback rate matches mod rates", () => sample.ChildrenOfType<DrawableSample>().First().AggregateFrequency.Value == expectedRate);
|
||||
AddAssert("sample playback rate matches mod rates", () =>
|
||||
testedMod != null && Precision.AlmostEquals(
|
||||
sample.ChildrenOfType<DrawableSample>().First().AggregateFrequency.Value,
|
||||
((IApplicableToRate)testedMod).ApplyToRate(sampleInfo.StartTime)));
|
||||
}
|
||||
|
||||
private class TestSkin : LegacySkin
|
||||
{
|
||||
public TestSkin(string resourceName, AudioManager audioManager)
|
||||
: base(DefaultLegacySkin.Info, new TestResourceStore(resourceName), audioManager, "skin.ini")
|
||||
public TestSkin(string resourceName, IStorageResourceProvider resources)
|
||||
: base(DefaultLegacySkin.Info, new TestResourceStore(resourceName), resources, "skin.ini")
|
||||
{
|
||||
}
|
||||
}
|
||||
@ -158,15 +196,15 @@ namespace osu.Game.Tests.Gameplay
|
||||
|
||||
private class TestCustomSkinWorkingBeatmap : ClockBackedTestWorkingBeatmap
|
||||
{
|
||||
private readonly AudioManager audio;
|
||||
private readonly IStorageResourceProvider resources;
|
||||
|
||||
public TestCustomSkinWorkingBeatmap(RulesetInfo ruleset, AudioManager audio)
|
||||
: base(ruleset, null, audio)
|
||||
public TestCustomSkinWorkingBeatmap(RulesetInfo ruleset, IStorageResourceProvider resources)
|
||||
: base(ruleset, null, resources.AudioManager)
|
||||
{
|
||||
this.audio = audio;
|
||||
this.resources = resources;
|
||||
}
|
||||
|
||||
protected override ISkin GetSkin() => new TestSkin("test-sample", audio);
|
||||
protected override ISkin GetSkin() => new TestSkin("test-sample", resources);
|
||||
}
|
||||
|
||||
private class TestDrawableStoryboardSample : DrawableStoryboardSample
|
||||
@ -176,5 +214,13 @@ namespace osu.Game.Tests.Gameplay
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#region IResourceStorageProvider
|
||||
|
||||
public AudioManager AudioManager => Audio;
|
||||
public IResourceStore<byte[]> Files => null;
|
||||
public IResourceStore<TextureUpload> CreateTextureLoaderStore(IResourceStore<byte[]> underlyingStore) => null;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ namespace osu.Game.Tests.Input
|
||||
=> AddStep($"make window {mode}", () => frameworkConfigManager.GetBindable<WindowMode>(FrameworkSetting.WindowMode).Value = mode);
|
||||
|
||||
private void setGameSideModeTo(OsuConfineMouseMode mode)
|
||||
=> AddStep($"set {mode} game-side", () => Game.LocalConfig.Set(OsuSetting.ConfineMouseMode, mode));
|
||||
=> AddStep($"set {mode} game-side", () => Game.LocalConfig.SetValue(OsuSetting.ConfineMouseMode, mode));
|
||||
|
||||
private void setLocalUserPlayingTo(bool playing)
|
||||
=> AddStep($"local user {(playing ? "playing" : "not playing")}", () => Game.LocalUserPlaying.Value = playing);
|
||||
|
36
osu.Game.Tests/Mods/ModSettingsEqualityComparison.cs
Normal file
36
osu.Game.Tests/Mods/ModSettingsEqualityComparison.cs
Normal file
@ -0,0 +1,36 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
|
||||
namespace osu.Game.Tests.Mods
|
||||
{
|
||||
[TestFixture]
|
||||
public class ModSettingsEqualityComparison
|
||||
{
|
||||
[Test]
|
||||
public void Test()
|
||||
{
|
||||
var mod1 = new OsuModDoubleTime { SpeedChange = { Value = 1.25 } };
|
||||
var mod2 = new OsuModDoubleTime { SpeedChange = { Value = 1.26 } };
|
||||
var mod3 = new OsuModDoubleTime { SpeedChange = { Value = 1.26 } };
|
||||
var apiMod1 = new APIMod(mod1);
|
||||
var apiMod2 = new APIMod(mod2);
|
||||
var apiMod3 = new APIMod(mod3);
|
||||
|
||||
Assert.That(mod1, Is.Not.EqualTo(mod2));
|
||||
Assert.That(apiMod1, Is.Not.EqualTo(apiMod2));
|
||||
|
||||
Assert.That(mod2, Is.EqualTo(mod2));
|
||||
Assert.That(apiMod2, Is.EqualTo(apiMod2));
|
||||
|
||||
Assert.That(mod2, Is.EqualTo(mod3));
|
||||
Assert.That(apiMod2, Is.EqualTo(apiMod3));
|
||||
|
||||
Assert.That(mod3, Is.EqualTo(mod2));
|
||||
Assert.That(apiMod3, Is.EqualTo(apiMod2));
|
||||
}
|
||||
}
|
||||
}
|
160
osu.Game.Tests/Mods/ModUtilsTest.cs
Normal file
160
osu.Game.Tests/Mods/ModUtilsTest.cs
Normal file
@ -0,0 +1,160 @@
|
||||
// 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;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Tests.Mods
|
||||
{
|
||||
[TestFixture]
|
||||
public class ModUtilsTest
|
||||
{
|
||||
[Test]
|
||||
public void TestModIsCompatibleByItself()
|
||||
{
|
||||
var mod = new Mock<CustomMod1>();
|
||||
Assert.That(ModUtils.CheckCompatibleSet(new[] { mod.Object }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestIncompatibleThroughTopLevel()
|
||||
{
|
||||
var mod1 = new Mock<CustomMod1>();
|
||||
var mod2 = new Mock<CustomMod2>();
|
||||
|
||||
mod1.Setup(m => m.IncompatibleMods).Returns(new[] { mod2.Object.GetType() });
|
||||
|
||||
// Test both orderings.
|
||||
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod1.Object, mod2.Object }), Is.False);
|
||||
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod2.Object, mod1.Object }), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMultiModIncompatibleWithTopLevel()
|
||||
{
|
||||
var mod1 = new Mock<CustomMod1>();
|
||||
|
||||
// The nested mod.
|
||||
var mod2 = new Mock<CustomMod2>();
|
||||
mod2.Setup(m => m.IncompatibleMods).Returns(new[] { mod1.Object.GetType() });
|
||||
|
||||
var multiMod = new MultiMod(new MultiMod(mod2.Object));
|
||||
|
||||
// Test both orderings.
|
||||
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { multiMod, mod1.Object }), Is.False);
|
||||
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod1.Object, multiMod }), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTopLevelIncompatibleWithMultiMod()
|
||||
{
|
||||
// The nested mod.
|
||||
var mod1 = new Mock<CustomMod1>();
|
||||
var multiMod = new MultiMod(new MultiMod(mod1.Object));
|
||||
|
||||
var mod2 = new Mock<CustomMod2>();
|
||||
mod2.Setup(m => m.IncompatibleMods).Returns(new[] { typeof(CustomMod1) });
|
||||
|
||||
// Test both orderings.
|
||||
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { multiMod, mod2.Object }), Is.False);
|
||||
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod2.Object, multiMod }), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCompatibleMods()
|
||||
{
|
||||
var mod1 = new Mock<CustomMod1>();
|
||||
var mod2 = new Mock<CustomMod2>();
|
||||
|
||||
// Test both orderings.
|
||||
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod1.Object, mod2.Object }), Is.True);
|
||||
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod2.Object, mod1.Object }), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestIncompatibleThroughBaseType()
|
||||
{
|
||||
var mod1 = new Mock<CustomMod1>();
|
||||
var mod2 = new Mock<CustomMod2>();
|
||||
mod2.Setup(m => m.IncompatibleMods).Returns(new[] { typeof(Mod) });
|
||||
|
||||
// Test both orderings.
|
||||
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod1.Object, mod2.Object }), Is.False);
|
||||
Assert.That(ModUtils.CheckCompatibleSet(new Mod[] { mod2.Object, mod1.Object }), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAllowedThroughMostDerivedType()
|
||||
{
|
||||
var mod = new Mock<CustomMod1>();
|
||||
Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { mod.Object.GetType() }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNotAllowedThroughBaseType()
|
||||
{
|
||||
var mod = new Mock<CustomMod1>();
|
||||
Assert.That(ModUtils.CheckAllowed(new[] { mod.Object }, new[] { typeof(Mod) }), Is.False);
|
||||
}
|
||||
|
||||
private static readonly object[] invalid_mod_test_scenarios =
|
||||
{
|
||||
// incompatible pair.
|
||||
new object[]
|
||||
{
|
||||
new Mod[] { new OsuModDoubleTime(), new OsuModHalfTime() },
|
||||
new[] { typeof(OsuModDoubleTime), typeof(OsuModHalfTime) }
|
||||
},
|
||||
// incompatible pair with derived class.
|
||||
new object[]
|
||||
{
|
||||
new Mod[] { new OsuModNightcore(), new OsuModHalfTime() },
|
||||
new[] { typeof(OsuModNightcore), typeof(OsuModHalfTime) }
|
||||
},
|
||||
// system mod.
|
||||
new object[]
|
||||
{
|
||||
new Mod[] { new OsuModDoubleTime(), new OsuModTouchDevice() },
|
||||
new[] { typeof(OsuModTouchDevice) }
|
||||
},
|
||||
// multi mod.
|
||||
new object[]
|
||||
{
|
||||
new Mod[] { new MultiMod(new OsuModHalfTime()), new OsuModHalfTime() },
|
||||
new[] { typeof(MultiMod) }
|
||||
},
|
||||
// valid pair.
|
||||
new object[]
|
||||
{
|
||||
new Mod[] { new OsuModDoubleTime(), new OsuModHardRock() },
|
||||
null
|
||||
}
|
||||
};
|
||||
|
||||
[TestCaseSource(nameof(invalid_mod_test_scenarios))]
|
||||
public void TestInvalidModScenarios(Mod[] inputMods, Type[] expectedInvalid)
|
||||
{
|
||||
bool isValid = ModUtils.CheckValidForGameplay(inputMods, out var invalid);
|
||||
|
||||
Assert.That(isValid, Is.EqualTo(expectedInvalid == null));
|
||||
|
||||
if (isValid)
|
||||
Assert.IsNull(invalid);
|
||||
else
|
||||
Assert.That(invalid.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid));
|
||||
}
|
||||
|
||||
public abstract class CustomMod1 : Mod
|
||||
{
|
||||
}
|
||||
|
||||
public abstract class CustomMod2 : Mod
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
73
osu.Game.Tests/Mods/SettingsSourceAttributeTest.cs
Normal file
73
osu.Game.Tests/Mods/SettingsSourceAttributeTest.cs
Normal file
@ -0,0 +1,73 @@
|
||||
// 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Overlays.Settings;
|
||||
|
||||
namespace osu.Game.Tests.Mods
|
||||
{
|
||||
[TestFixture]
|
||||
public class SettingsSourceAttributeTest
|
||||
{
|
||||
[Test]
|
||||
public void TestOrdering()
|
||||
{
|
||||
var objectWithSettings = new ClassWithSettings();
|
||||
|
||||
var orderedSettings = objectWithSettings.GetOrderedSettingsSourceProperties().ToArray();
|
||||
|
||||
Assert.That(orderedSettings, Has.Length.EqualTo(4));
|
||||
|
||||
Assert.That(orderedSettings[0].Item2.Name, Is.EqualTo(nameof(ClassWithSettings.FirstSetting)));
|
||||
Assert.That(orderedSettings[1].Item2.Name, Is.EqualTo(nameof(ClassWithSettings.SecondSetting)));
|
||||
Assert.That(orderedSettings[2].Item2.Name, Is.EqualTo(nameof(ClassWithSettings.ThirdSetting)));
|
||||
Assert.That(orderedSettings[3].Item2.Name, Is.EqualTo(nameof(ClassWithSettings.UnorderedSetting)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCustomControl()
|
||||
{
|
||||
var objectWithCustomSettingControl = new ClassWithCustomSettingControl();
|
||||
var settings = objectWithCustomSettingControl.CreateSettingsControls().ToArray();
|
||||
|
||||
Assert.That(settings, Has.Length.EqualTo(1));
|
||||
Assert.That(settings[0], Is.TypeOf<CustomSettingsControl>());
|
||||
}
|
||||
|
||||
private class ClassWithSettings
|
||||
{
|
||||
[SettingSource("Unordered setting", "Should be last")]
|
||||
public BindableFloat UnorderedSetting { get; set; } = new BindableFloat();
|
||||
|
||||
[SettingSource("Second setting", "Another description", 2)]
|
||||
public BindableBool SecondSetting { get; set; } = new BindableBool();
|
||||
|
||||
[SettingSource("First setting", "A description", 1)]
|
||||
public BindableDouble FirstSetting { get; set; } = new BindableDouble();
|
||||
|
||||
[SettingSource("Third setting", "Yet another description", 3)]
|
||||
public BindableInt ThirdSetting { get; set; } = new BindableInt();
|
||||
}
|
||||
|
||||
private class ClassWithCustomSettingControl
|
||||
{
|
||||
[SettingSource("Custom setting", "Should be a custom control", SettingControlType = typeof(CustomSettingsControl))]
|
||||
public BindableInt UnorderedSetting { get; set; } = new BindableInt();
|
||||
}
|
||||
|
||||
private class CustomSettingsControl : SettingsItem<int>
|
||||
{
|
||||
protected override Drawable CreateControl() => new CustomControl();
|
||||
|
||||
private class CustomControl : Drawable, IHasCurrentValue<int>
|
||||
{
|
||||
public Bindable<int> Current { get; set; } = new Bindable<int>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
91
osu.Game.Tests/NonVisual/ClosestBeatDivisorTest.cs
Normal file
91
osu.Game.Tests/NonVisual/ClosestBeatDivisorTest.cs
Normal file
@ -0,0 +1,91 @@
|
||||
// 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 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<double> divisors, IReadOnlyList<double> closestDivisors, ControlPointInfo cpi, double step = 1)
|
||||
{
|
||||
List<HitObject> hitobjects = new List<HitObject>();
|
||||
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.ControlPointInfo.GetClosestBeatDivisor(beatmap.HitObjects[i].StartTime), $"at index {i}");
|
||||
}
|
||||
}
|
||||
}
|
@ -246,5 +246,32 @@ namespace osu.Game.Tests.NonVisual
|
||||
Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(0));
|
||||
Assert.That(cpi.AllControlPoints.Count, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCreateCopyIsDeepClone()
|
||||
{
|
||||
var cpi = new ControlPointInfo();
|
||||
|
||||
cpi.Add(1000, new TimingControlPoint { BeatLength = 500 });
|
||||
|
||||
var cpiCopy = cpi.CreateCopy();
|
||||
|
||||
cpiCopy.Add(2000, new TimingControlPoint { BeatLength = 500 });
|
||||
|
||||
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
|
||||
Assert.That(cpiCopy.Groups.Count, Is.EqualTo(2));
|
||||
|
||||
Assert.That(cpi.TimingPoints.Count, Is.EqualTo(1));
|
||||
Assert.That(cpiCopy.TimingPoints.Count, Is.EqualTo(2));
|
||||
|
||||
Assert.That(cpi.TimingPoints[0], Is.Not.SameAs(cpiCopy.TimingPoints[0]));
|
||||
Assert.That(cpi.TimingPoints[0].BeatLengthBindable, Is.Not.SameAs(cpiCopy.TimingPoints[0].BeatLengthBindable));
|
||||
|
||||
Assert.That(cpi.TimingPoints[0].BeatLength, Is.EqualTo(cpiCopy.TimingPoints[0].BeatLength));
|
||||
|
||||
cpi.TimingPoints[0].BeatLength = 800;
|
||||
|
||||
Assert.That(cpi.TimingPoints[0].BeatLength, Is.Not.EqualTo(cpiCopy.TimingPoints[0].BeatLength));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
using (var host = new CustomTestHeadlessGameHost())
|
||||
{
|
||||
using (var storageConfig = new StorageConfigManager(host.InitialStorage))
|
||||
storageConfig.Set(StorageConfig.FullPath, customPath);
|
||||
storageConfig.SetValue(StorageConfig.FullPath, customPath);
|
||||
|
||||
try
|
||||
{
|
||||
@ -73,7 +73,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
using (var host = new CustomTestHeadlessGameHost())
|
||||
{
|
||||
using (var storageConfig = new StorageConfigManager(host.InitialStorage))
|
||||
storageConfig.Set(StorageConfig.FullPath, customPath);
|
||||
storageConfig.SetValue(StorageConfig.FullPath, customPath);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -144,6 +144,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
public override string Name => nameof(ModA);
|
||||
public override string Acronym => nameof(ModA);
|
||||
public override string Description => string.Empty;
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModIncompatibleWithA), typeof(ModIncompatibleWithAAndB) };
|
||||
@ -152,6 +153,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
private class ModB : Mod
|
||||
{
|
||||
public override string Name => nameof(ModB);
|
||||
public override string Description => string.Empty;
|
||||
public override string Acronym => nameof(ModB);
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
@ -162,6 +164,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
public override string Name => nameof(ModC);
|
||||
public override string Acronym => nameof(ModC);
|
||||
public override string Description => string.Empty;
|
||||
public override double ScoreMultiplier => 1;
|
||||
}
|
||||
|
||||
@ -169,6 +172,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
public override string Name => $"Incompatible With {nameof(ModA)}";
|
||||
public override string Acronym => $"Incompatible With {nameof(ModA)}";
|
||||
public override string Description => string.Empty;
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModA) };
|
||||
@ -187,6 +191,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
public override string Name => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}";
|
||||
public override string Acronym => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}";
|
||||
public override string Description => string.Empty;
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModA), typeof(ModB) };
|
||||
@ -212,7 +217,7 @@ namespace osu.Game.Tests.NonVisual
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap)
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
@ -4,8 +4,10 @@
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Filter;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Carousel;
|
||||
using osu.Game.Screens.Select.Filter;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual.Filtering
|
||||
{
|
||||
@ -214,5 +216,31 @@ namespace osu.Game.Tests.NonVisual.Filtering
|
||||
|
||||
Assert.AreEqual(filtered, carouselItem.Filtered.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCustomRulesetCriteria([Values(null, true, false)] bool? matchCustomCriteria)
|
||||
{
|
||||
var beatmap = getExampleBeatmap();
|
||||
|
||||
var customCriteria = matchCustomCriteria is bool match ? new CustomCriteria(match) : null;
|
||||
var criteria = new FilterCriteria { RulesetCriteria = customCriteria };
|
||||
var carouselItem = new CarouselBeatmap(beatmap);
|
||||
carouselItem.Filter(criteria);
|
||||
|
||||
Assert.AreEqual(matchCustomCriteria == false, carouselItem.Filtered.Value);
|
||||
}
|
||||
|
||||
private class CustomCriteria : IRulesetFilterCriteria
|
||||
{
|
||||
private readonly bool match;
|
||||
|
||||
public CustomCriteria(bool shouldMatch)
|
||||
{
|
||||
match = shouldMatch;
|
||||
}
|
||||
|
||||
public bool Matches(BeatmapInfo beatmap) => match;
|
||||
public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) => false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,9 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Filter;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Filter;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual.Filtering
|
||||
{
|
||||
@ -194,5 +196,63 @@ namespace osu.Game.Tests.NonVisual.Filtering
|
||||
Assert.AreEqual(1, filterCriteria.SearchTerms.Length);
|
||||
Assert.AreEqual("double\"quote", filterCriteria.Artist.SearchTerm);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOperatorParsing()
|
||||
{
|
||||
const string query = "artist=><something";
|
||||
var filterCriteria = new FilterCriteria();
|
||||
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||
Assert.AreEqual("><something", filterCriteria.Artist.SearchTerm);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUnrecognisedKeywordIsIgnored()
|
||||
{
|
||||
const string query = "unrecognised=keyword";
|
||||
var filterCriteria = new FilterCriteria();
|
||||
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||
Assert.AreEqual("unrecognised=keyword", filterCriteria.SearchText);
|
||||
}
|
||||
|
||||
[TestCase("cs=nope")]
|
||||
[TestCase("bpm>=bad")]
|
||||
[TestCase("divisor<nah")]
|
||||
[TestCase("status=noidea")]
|
||||
public void TestInvalidKeywordValueIsIgnored(string query)
|
||||
{
|
||||
var filterCriteria = new FilterCriteria();
|
||||
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||
Assert.AreEqual(query, filterCriteria.SearchText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCustomKeywordIsParsed()
|
||||
{
|
||||
var customCriteria = new CustomFilterCriteria();
|
||||
const string query = "custom=readme unrecognised=keyword";
|
||||
var filterCriteria = new FilterCriteria { RulesetCriteria = customCriteria };
|
||||
FilterQueryParser.ApplyQueries(filterCriteria, query);
|
||||
Assert.AreEqual("readme", customCriteria.CustomValue);
|
||||
Assert.AreEqual("unrecognised=keyword", filterCriteria.SearchText.Trim());
|
||||
}
|
||||
|
||||
private class CustomFilterCriteria : IRulesetFilterCriteria
|
||||
{
|
||||
public string CustomValue { get; set; }
|
||||
|
||||
public bool Matches(BeatmapInfo beatmap) => true;
|
||||
|
||||
public bool TryParseCustomKeywordCriteria(string key, Operator op, string value)
|
||||
{
|
||||
if (key == "custom" && op == Operator.Equal)
|
||||
{
|
||||
CustomValue = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
26
osu.Game.Tests/NonVisual/FormatUtilsTest.cs
Normal file
26
osu.Game.Tests/NonVisual/FormatUtilsTest.cs
Normal file
@ -0,0 +1,26 @@
|
||||
// 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.Globalization;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
[TestFixture]
|
||||
public class FormatUtilsTest
|
||||
{
|
||||
[TestCase(0, "0.00%")]
|
||||
[TestCase(0.01, "1.00%")]
|
||||
[TestCase(0.9899, "98.99%")]
|
||||
[TestCase(0.989999, "98.99%")]
|
||||
[TestCase(0.99, "99.00%")]
|
||||
[TestCase(0.9999, "99.99%")]
|
||||
[TestCase(0.999999, "99.99%")]
|
||||
[TestCase(1, "100.00%")]
|
||||
public void TestAccuracyFormatting(double input, string expectedOutput)
|
||||
{
|
||||
Assert.AreEqual(expectedOutput, input.FormatAccuracy(CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
@ -20,27 +21,14 @@ namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
handler = new TestInputHandler(replay = new Replay
|
||||
{
|
||||
Frames = new List<ReplayFrame>
|
||||
{
|
||||
new TestReplayFrame(0),
|
||||
new TestReplayFrame(1000),
|
||||
new TestReplayFrame(2000),
|
||||
new TestReplayFrame(3000, true),
|
||||
new TestReplayFrame(4000, true),
|
||||
new TestReplayFrame(5000, true),
|
||||
new TestReplayFrame(7000, true),
|
||||
new TestReplayFrame(8000),
|
||||
}
|
||||
HasReceivedAllFrames = false
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNormalPlayback()
|
||||
{
|
||||
Assert.IsNull(handler.CurrentFrame);
|
||||
|
||||
confirmCurrentFrame(null);
|
||||
confirmNextFrame(0);
|
||||
setReplayFrames();
|
||||
|
||||
setTime(0, 0);
|
||||
confirmCurrentFrame(0);
|
||||
@ -107,6 +95,8 @@ namespace osu.Game.Tests.NonVisual
|
||||
[Test]
|
||||
public void TestIntroTime()
|
||||
{
|
||||
setReplayFrames();
|
||||
|
||||
setTime(-1000, -1000);
|
||||
confirmCurrentFrame(null);
|
||||
confirmNextFrame(0);
|
||||
@ -123,6 +113,8 @@ namespace osu.Game.Tests.NonVisual
|
||||
[Test]
|
||||
public void TestBasicRewind()
|
||||
{
|
||||
setReplayFrames();
|
||||
|
||||
setTime(2800, 0);
|
||||
setTime(2800, 1000);
|
||||
setTime(2800, 2000);
|
||||
@ -133,34 +125,35 @@ namespace osu.Game.Tests.NonVisual
|
||||
// pivot without crossing a frame boundary
|
||||
setTime(2700, 2700);
|
||||
confirmCurrentFrame(2);
|
||||
confirmNextFrame(1);
|
||||
confirmNextFrame(3);
|
||||
|
||||
// cross current frame boundary; should not yet update frame
|
||||
setTime(1980, 1980);
|
||||
// cross current frame boundary
|
||||
setTime(1980, 2000);
|
||||
confirmCurrentFrame(2);
|
||||
confirmNextFrame(1);
|
||||
confirmNextFrame(3);
|
||||
|
||||
setTime(1200, 1200);
|
||||
confirmCurrentFrame(2);
|
||||
confirmNextFrame(1);
|
||||
confirmCurrentFrame(1);
|
||||
confirmNextFrame(2);
|
||||
|
||||
// ensure each frame plays out until start
|
||||
setTime(-500, 1000);
|
||||
confirmCurrentFrame(1);
|
||||
confirmNextFrame(0);
|
||||
confirmNextFrame(2);
|
||||
|
||||
setTime(-500, 0);
|
||||
confirmCurrentFrame(0);
|
||||
confirmNextFrame(null);
|
||||
confirmNextFrame(1);
|
||||
|
||||
setTime(-500, -500);
|
||||
confirmCurrentFrame(0);
|
||||
confirmNextFrame(null);
|
||||
confirmCurrentFrame(null);
|
||||
confirmNextFrame(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRewindInsideImportantSection()
|
||||
{
|
||||
setReplayFrames();
|
||||
fastForwardToPoint(3000);
|
||||
|
||||
setTime(4000, 4000);
|
||||
@ -168,12 +161,12 @@ namespace osu.Game.Tests.NonVisual
|
||||
confirmNextFrame(5);
|
||||
|
||||
setTime(3500, null);
|
||||
confirmCurrentFrame(4);
|
||||
confirmNextFrame(3);
|
||||
confirmCurrentFrame(3);
|
||||
confirmNextFrame(4);
|
||||
|
||||
setTime(3000, 3000);
|
||||
confirmCurrentFrame(3);
|
||||
confirmNextFrame(2);
|
||||
confirmNextFrame(4);
|
||||
|
||||
setTime(3500, null);
|
||||
confirmCurrentFrame(3);
|
||||
@ -187,46 +180,175 @@ namespace osu.Game.Tests.NonVisual
|
||||
confirmCurrentFrame(4);
|
||||
confirmNextFrame(5);
|
||||
|
||||
setTime(4000, null);
|
||||
setTime(4000, 4000);
|
||||
confirmCurrentFrame(4);
|
||||
confirmNextFrame(5);
|
||||
|
||||
setTime(3500, null);
|
||||
confirmCurrentFrame(4);
|
||||
confirmNextFrame(3);
|
||||
confirmCurrentFrame(3);
|
||||
confirmNextFrame(4);
|
||||
|
||||
setTime(3000, 3000);
|
||||
confirmCurrentFrame(3);
|
||||
confirmNextFrame(2);
|
||||
confirmNextFrame(4);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRewindOutOfImportantSection()
|
||||
{
|
||||
setReplayFrames();
|
||||
fastForwardToPoint(3500);
|
||||
|
||||
confirmCurrentFrame(3);
|
||||
confirmNextFrame(4);
|
||||
|
||||
setTime(3200, null);
|
||||
// next frame doesn't change even though direction reversed, because of important section.
|
||||
confirmCurrentFrame(3);
|
||||
confirmNextFrame(4);
|
||||
|
||||
setTime(3000, null);
|
||||
setTime(3000, 3000);
|
||||
confirmCurrentFrame(3);
|
||||
confirmNextFrame(4);
|
||||
|
||||
setTime(2800, 2800);
|
||||
confirmCurrentFrame(3);
|
||||
confirmNextFrame(2);
|
||||
confirmCurrentFrame(2);
|
||||
confirmNextFrame(3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestReplayStreaming()
|
||||
{
|
||||
// no frames are arrived yet
|
||||
setTime(0, null);
|
||||
setTime(1000, null);
|
||||
Assert.IsTrue(handler.WaitingForFrame, "Should be waiting for the first frame");
|
||||
|
||||
replay.Frames.Add(new TestReplayFrame(0));
|
||||
replay.Frames.Add(new TestReplayFrame(1000));
|
||||
|
||||
// should always play from beginning
|
||||
setTime(1000, 0);
|
||||
confirmCurrentFrame(0);
|
||||
Assert.IsFalse(handler.WaitingForFrame, "Should not be waiting yet");
|
||||
setTime(1000, 1000);
|
||||
confirmCurrentFrame(1);
|
||||
confirmNextFrame(null);
|
||||
Assert.IsTrue(handler.WaitingForFrame, "Should be waiting");
|
||||
|
||||
// cannot seek beyond the last frame
|
||||
setTime(1500, null);
|
||||
confirmCurrentFrame(1);
|
||||
|
||||
setTime(-100, 0);
|
||||
confirmCurrentFrame(0);
|
||||
|
||||
// can seek to the point before the first frame, however
|
||||
setTime(-100, -100);
|
||||
confirmCurrentFrame(null);
|
||||
confirmNextFrame(0);
|
||||
|
||||
fastForwardToPoint(1000);
|
||||
setTime(3000, null);
|
||||
replay.Frames.Add(new TestReplayFrame(2000));
|
||||
confirmCurrentFrame(1);
|
||||
setTime(1000, 1000);
|
||||
setTime(3000, 2000);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMultipleFramesSameTime()
|
||||
{
|
||||
replay.Frames.Add(new TestReplayFrame(0));
|
||||
replay.Frames.Add(new TestReplayFrame(0));
|
||||
replay.Frames.Add(new TestReplayFrame(1000));
|
||||
replay.Frames.Add(new TestReplayFrame(1000));
|
||||
replay.Frames.Add(new TestReplayFrame(2000));
|
||||
|
||||
// forward direction is prioritized when multiple frames have the same time.
|
||||
setTime(0, 0);
|
||||
setTime(0, 0);
|
||||
|
||||
setTime(2000, 1000);
|
||||
setTime(2000, 1000);
|
||||
|
||||
setTime(1000, 1000);
|
||||
setTime(1000, 1000);
|
||||
setTime(-100, 1000);
|
||||
setTime(-100, 0);
|
||||
setTime(-100, 0);
|
||||
setTime(-100, -100);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestReplayFramesSortStability()
|
||||
{
|
||||
const double repeating_time = 5000;
|
||||
|
||||
// add a collection of frames in shuffled order time-wise; each frame also stores its original index to check stability later.
|
||||
// data is hand-picked and breaks if the unstable List<T>.Sort() is used.
|
||||
// in theory this can still return a false-positive with another unstable algorithm if extremely unlucky,
|
||||
// but there is no conceivable fool-proof way to prevent that anyways.
|
||||
replay.Frames.AddRange(new[]
|
||||
{
|
||||
repeating_time,
|
||||
0,
|
||||
3000,
|
||||
repeating_time,
|
||||
repeating_time,
|
||||
6000,
|
||||
9000,
|
||||
repeating_time,
|
||||
repeating_time,
|
||||
1000,
|
||||
11000,
|
||||
21000,
|
||||
4000,
|
||||
repeating_time,
|
||||
repeating_time,
|
||||
8000,
|
||||
2000,
|
||||
7000,
|
||||
repeating_time,
|
||||
repeating_time,
|
||||
10000
|
||||
}.Select((time, index) => new TestReplayFrame(time, true, index)));
|
||||
|
||||
replay.HasReceivedAllFrames = true;
|
||||
|
||||
// create a new handler with the replay for the sort to be performed.
|
||||
handler = new TestInputHandler(replay);
|
||||
|
||||
// ensure sort stability by checking that the frames with time == repeating_time are sorted in ascending frame index order themselves.
|
||||
var repeatingTimeFramesData = replay.Frames
|
||||
.Cast<TestReplayFrame>()
|
||||
.Where(f => f.Time == repeating_time)
|
||||
.Select(f => f.FrameIndex);
|
||||
|
||||
Assert.That(repeatingTimeFramesData, Is.Ordered.Ascending);
|
||||
}
|
||||
|
||||
private void setReplayFrames()
|
||||
{
|
||||
replay.Frames = new List<ReplayFrame>
|
||||
{
|
||||
new TestReplayFrame(0),
|
||||
new TestReplayFrame(1000),
|
||||
new TestReplayFrame(2000),
|
||||
new TestReplayFrame(3000, true),
|
||||
new TestReplayFrame(4000, true),
|
||||
new TestReplayFrame(5000, true),
|
||||
new TestReplayFrame(7000, true),
|
||||
new TestReplayFrame(8000),
|
||||
};
|
||||
replay.HasReceivedAllFrames = true;
|
||||
}
|
||||
|
||||
private void fastForwardToPoint(double destination)
|
||||
{
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
if (handler.SetFrameFromTime(destination) == null)
|
||||
var time = handler.SetFrameFromTime(destination);
|
||||
if (time == null || time == destination)
|
||||
return;
|
||||
}
|
||||
|
||||
@ -235,43 +357,29 @@ namespace osu.Game.Tests.NonVisual
|
||||
|
||||
private void setTime(double set, double? expect)
|
||||
{
|
||||
Assert.AreEqual(expect, handler.SetFrameFromTime(set));
|
||||
Assert.AreEqual(expect, handler.SetFrameFromTime(set), "Unexpected return value");
|
||||
}
|
||||
|
||||
private void confirmCurrentFrame(int? frame)
|
||||
{
|
||||
if (frame.HasValue)
|
||||
{
|
||||
Assert.IsNotNull(handler.CurrentFrame);
|
||||
Assert.AreEqual(replay.Frames[frame.Value].Time, handler.CurrentFrame.Time);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsNull(handler.CurrentFrame);
|
||||
}
|
||||
Assert.AreEqual(frame is int x ? replay.Frames[x].Time : (double?)null, handler.CurrentFrame?.Time, "Unexpected current frame");
|
||||
}
|
||||
|
||||
private void confirmNextFrame(int? frame)
|
||||
{
|
||||
if (frame.HasValue)
|
||||
{
|
||||
Assert.IsNotNull(handler.NextFrame);
|
||||
Assert.AreEqual(replay.Frames[frame.Value].Time, handler.NextFrame.Time);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsNull(handler.NextFrame);
|
||||
}
|
||||
Assert.AreEqual(frame is int x ? replay.Frames[x].Time : (double?)null, handler.NextFrame?.Time, "Unexpected next frame");
|
||||
}
|
||||
|
||||
private class TestReplayFrame : ReplayFrame
|
||||
{
|
||||
public readonly bool IsImportant;
|
||||
public readonly int FrameIndex;
|
||||
|
||||
public TestReplayFrame(double time, bool isImportant = false)
|
||||
public TestReplayFrame(double time, bool isImportant = false, int frameIndex = 0)
|
||||
: base(time)
|
||||
{
|
||||
IsImportant = isImportant;
|
||||
FrameIndex = frameIndex;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,115 +0,0 @@
|
||||
// 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;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Rulesets.Difficulty.Utils;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
[TestFixture]
|
||||
public class LimitedCapacityStackTest
|
||||
{
|
||||
private const int capacity = 3;
|
||||
|
||||
private LimitedCapacityStack<int> stack;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
stack = new LimitedCapacityStack<int>(capacity);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmptyStack()
|
||||
{
|
||||
Assert.AreEqual(0, stack.Count);
|
||||
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
int unused = stack[0];
|
||||
});
|
||||
|
||||
int count = 0;
|
||||
foreach (var unused in stack)
|
||||
count++;
|
||||
|
||||
Assert.AreEqual(0, count);
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
public void TestInRangeElements(int count)
|
||||
{
|
||||
// e.g. 0 -> 1 -> 2
|
||||
for (int i = 0; i < count; i++)
|
||||
stack.Push(i);
|
||||
|
||||
Assert.AreEqual(count, stack.Count);
|
||||
|
||||
// e.g. 2 -> 1 -> 0 (reverse order)
|
||||
for (int i = 0; i < stack.Count; i++)
|
||||
Assert.AreEqual(count - 1 - i, stack[i]);
|
||||
|
||||
// e.g. indices 3, 4, 5, 6 (out of range)
|
||||
for (int i = stack.Count; i < stack.Count + capacity; i++)
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
int unused = stack[i];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(4)]
|
||||
[TestCase(5)]
|
||||
[TestCase(6)]
|
||||
public void TestOverflowElements(int count)
|
||||
{
|
||||
// e.g. 0 -> 1 -> 2 -> 3
|
||||
for (int i = 0; i < count; i++)
|
||||
stack.Push(i);
|
||||
|
||||
Assert.AreEqual(capacity, stack.Count);
|
||||
|
||||
// e.g. 3 -> 2 -> 1 (reverse order)
|
||||
for (int i = 0; i < stack.Count; i++)
|
||||
Assert.AreEqual(count - 1 - i, stack[i]);
|
||||
|
||||
// e.g. indices 3, 4, 5, 6 (out of range)
|
||||
for (int i = stack.Count; i < stack.Count + capacity; i++)
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
int unused = stack[i];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(3)]
|
||||
[TestCase(4)]
|
||||
[TestCase(5)]
|
||||
[TestCase(6)]
|
||||
public void TestEnumerator(int count)
|
||||
{
|
||||
// e.g. 0 -> 1 -> 2 -> 3
|
||||
for (int i = 0; i < count; i++)
|
||||
stack.Push(i);
|
||||
|
||||
int enumeratorCount = 0;
|
||||
int expectedValue = count - 1;
|
||||
|
||||
foreach (var item in stack)
|
||||
{
|
||||
Assert.AreEqual(expectedValue, item);
|
||||
enumeratorCount++;
|
||||
expectedValue--;
|
||||
}
|
||||
|
||||
Assert.AreEqual(stack.Count, enumeratorCount);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
// 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.Linq;
|
||||
using Humanizer;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Tests.Visual.Multiplayer;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual.Multiplayer
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class StatefulMultiplayerClientTest : MultiplayerTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestPlayingUserTracking()
|
||||
{
|
||||
int id = 2000;
|
||||
|
||||
AddRepeatStep("add some users", () => Client.AddUser(new User { Id = id++ }), 5);
|
||||
checkPlayingUserCount(0);
|
||||
|
||||
changeState(3, MultiplayerUserState.WaitingForLoad);
|
||||
checkPlayingUserCount(3);
|
||||
|
||||
changeState(3, MultiplayerUserState.Playing);
|
||||
checkPlayingUserCount(3);
|
||||
|
||||
changeState(3, MultiplayerUserState.Results);
|
||||
checkPlayingUserCount(0);
|
||||
|
||||
changeState(6, MultiplayerUserState.WaitingForLoad);
|
||||
checkPlayingUserCount(6);
|
||||
|
||||
AddStep("another user left", () => Client.RemoveUser((Client.Room?.Users.Last().User).AsNonNull()));
|
||||
checkPlayingUserCount(5);
|
||||
|
||||
AddStep("leave room", () => Client.LeaveRoom());
|
||||
checkPlayingUserCount(0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPlayingUsersUpdatedOnJoin()
|
||||
{
|
||||
AddStep("leave room", () => Client.LeaveRoom());
|
||||
AddUntilStep("wait for room part", () => Client.Room == null);
|
||||
|
||||
AddStep("create room initially in gameplay", () =>
|
||||
{
|
||||
Room.RoomID.Value = null;
|
||||
Client.RoomSetupAction = room =>
|
||||
{
|
||||
room.State = MultiplayerRoomState.Playing;
|
||||
room.Users.Add(new MultiplayerRoomUser(PLAYER_1_ID)
|
||||
{
|
||||
User = new User { Id = PLAYER_1_ID },
|
||||
State = MultiplayerUserState.Playing
|
||||
});
|
||||
};
|
||||
|
||||
RoomManager.CreateRoom(Room);
|
||||
});
|
||||
|
||||
AddUntilStep("wait for room join", () => Client.Room != null);
|
||||
checkPlayingUserCount(1);
|
||||
}
|
||||
|
||||
private void checkPlayingUserCount(int expectedCount)
|
||||
=> AddAssert($"{"user".ToQuantity(expectedCount)} playing", () => Client.CurrentMatchPlayingUserIds.Count == expectedCount);
|
||||
|
||||
private void changeState(int userCount, MultiplayerUserState state)
|
||||
=> AddStep($"{"user".ToQuantity(userCount)} in {state}", () =>
|
||||
{
|
||||
for (int i = 0; i < userCount; ++i)
|
||||
{
|
||||
var userId = Client.Room?.Users[i].UserID ?? throw new AssertionException("Room cannot be null!");
|
||||
Client.ChangeUserState(userId, state);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
106
osu.Game.Tests/NonVisual/OngoingOperationTrackerTest.cs
Normal file
106
osu.Game.Tests/NonVisual/OngoingOperationTrackerTest.cs
Normal file
@ -0,0 +1,106 @@
|
||||
// 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;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.OnlinePlay;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class OngoingOperationTrackerTest : OsuTestScene
|
||||
{
|
||||
private OngoingOperationTracker tracker;
|
||||
private IBindable<bool> operationInProgress;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUp()
|
||||
{
|
||||
AddStep("create tracker", () => Child = tracker = new OngoingOperationTracker());
|
||||
AddStep("bind to operation status", () => operationInProgress = tracker.InProgress.GetBoundCopy());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOperationTracking()
|
||||
{
|
||||
IDisposable firstOperation = null;
|
||||
IDisposable secondOperation = null;
|
||||
|
||||
AddStep("begin first operation", () => firstOperation = tracker.BeginOperation());
|
||||
AddAssert("first operation in progress", () => operationInProgress.Value);
|
||||
|
||||
AddStep("cannot start another operation",
|
||||
() => Assert.Throws<InvalidOperationException>(() => tracker.BeginOperation()));
|
||||
|
||||
AddStep("end first operation", () => firstOperation.Dispose());
|
||||
AddAssert("first operation is ended", () => !operationInProgress.Value);
|
||||
|
||||
AddStep("start second operation", () => secondOperation = tracker.BeginOperation());
|
||||
AddAssert("second operation in progress", () => operationInProgress.Value);
|
||||
|
||||
AddStep("dispose first operation again", () => firstOperation.Dispose());
|
||||
AddAssert("second operation still in progress", () => operationInProgress.Value);
|
||||
|
||||
AddStep("dispose second operation", () => secondOperation.Dispose());
|
||||
AddAssert("second operation is ended", () => !operationInProgress.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOperationDisposalAfterTracker()
|
||||
{
|
||||
IDisposable operation = null;
|
||||
|
||||
AddStep("begin operation", () => operation = tracker.BeginOperation());
|
||||
AddStep("dispose tracker", () => tracker.Expire());
|
||||
AddStep("end operation", () => operation.Dispose());
|
||||
AddAssert("operation is ended", () => !operationInProgress.Value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestOperationDisposalAfterScreenExit()
|
||||
{
|
||||
TestScreenWithTracker screen = null;
|
||||
OsuScreenStack stack;
|
||||
IDisposable operation = null;
|
||||
|
||||
AddStep("create screen with tracker", () =>
|
||||
{
|
||||
Child = stack = new OsuScreenStack
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both
|
||||
};
|
||||
|
||||
stack.Push(screen = new TestScreenWithTracker());
|
||||
});
|
||||
AddUntilStep("wait for loaded", () => screen.IsLoaded);
|
||||
|
||||
AddStep("begin operation", () => operation = screen.OngoingOperationTracker.BeginOperation());
|
||||
AddAssert("operation in progress", () => screen.OngoingOperationTracker.InProgress.Value);
|
||||
|
||||
AddStep("dispose after screen exit", () =>
|
||||
{
|
||||
screen.Exit();
|
||||
operation.Dispose();
|
||||
});
|
||||
AddAssert("operation ended", () => !screen.OngoingOperationTracker.InProgress.Value);
|
||||
}
|
||||
|
||||
private class TestScreenWithTracker : OsuScreen
|
||||
{
|
||||
public OngoingOperationTracker OngoingOperationTracker { get; private set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChild = OngoingOperationTracker = new OngoingOperationTracker();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
143
osu.Game.Tests/NonVisual/ReverseQueueTest.cs
Normal file
143
osu.Game.Tests/NonVisual/ReverseQueueTest.cs
Normal file
@ -0,0 +1,143 @@
|
||||
// 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;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Rulesets.Difficulty.Utils;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
[TestFixture]
|
||||
public class ReverseQueueTest
|
||||
{
|
||||
private ReverseQueue<char> queue;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
queue = new ReverseQueue<char>(4);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmptyQueue()
|
||||
{
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
char unused = queue[0];
|
||||
});
|
||||
|
||||
int count = 0;
|
||||
foreach (var unused in queue)
|
||||
count++;
|
||||
|
||||
Assert.AreEqual(0, count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEnqueue()
|
||||
{
|
||||
// Assert correct values and reverse index after enqueueing
|
||||
queue.Enqueue('a');
|
||||
queue.Enqueue('b');
|
||||
queue.Enqueue('c');
|
||||
|
||||
Assert.AreEqual('c', queue[0]);
|
||||
Assert.AreEqual('b', queue[1]);
|
||||
Assert.AreEqual('a', queue[2]);
|
||||
|
||||
// Assert correct values and reverse index after enqueueing beyond initial capacity of 4
|
||||
queue.Enqueue('d');
|
||||
queue.Enqueue('e');
|
||||
queue.Enqueue('f');
|
||||
|
||||
Assert.AreEqual('f', queue[0]);
|
||||
Assert.AreEqual('e', queue[1]);
|
||||
Assert.AreEqual('d', queue[2]);
|
||||
Assert.AreEqual('c', queue[3]);
|
||||
Assert.AreEqual('b', queue[4]);
|
||||
Assert.AreEqual('a', queue[5]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDequeue()
|
||||
{
|
||||
queue.Enqueue('a');
|
||||
queue.Enqueue('b');
|
||||
queue.Enqueue('c');
|
||||
queue.Enqueue('d');
|
||||
queue.Enqueue('e');
|
||||
queue.Enqueue('f');
|
||||
|
||||
// Assert correct item return and no longer in queue after dequeueing
|
||||
Assert.AreEqual('a', queue[5]);
|
||||
var dequeuedItem = queue.Dequeue();
|
||||
|
||||
Assert.AreEqual('a', dequeuedItem);
|
||||
Assert.AreEqual(5, queue.Count);
|
||||
Assert.AreEqual('f', queue[0]);
|
||||
Assert.AreEqual('b', queue[4]);
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
char unused = queue[5];
|
||||
});
|
||||
|
||||
// Assert correct state after enough enqueues and dequeues to wrap around array (queue.start = 0 again)
|
||||
queue.Enqueue('g');
|
||||
queue.Enqueue('h');
|
||||
queue.Enqueue('i');
|
||||
queue.Dequeue();
|
||||
queue.Dequeue();
|
||||
queue.Dequeue();
|
||||
queue.Dequeue();
|
||||
queue.Dequeue();
|
||||
queue.Dequeue();
|
||||
queue.Dequeue();
|
||||
|
||||
Assert.AreEqual(1, queue.Count);
|
||||
Assert.AreEqual('i', queue[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestClear()
|
||||
{
|
||||
queue.Enqueue('a');
|
||||
queue.Enqueue('b');
|
||||
queue.Enqueue('c');
|
||||
queue.Enqueue('d');
|
||||
queue.Enqueue('e');
|
||||
queue.Enqueue('f');
|
||||
|
||||
// Assert queue is empty after clearing
|
||||
queue.Clear();
|
||||
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
{
|
||||
char unused = queue[0];
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEnumerator()
|
||||
{
|
||||
queue.Enqueue('a');
|
||||
queue.Enqueue('b');
|
||||
queue.Enqueue('c');
|
||||
queue.Enqueue('d');
|
||||
queue.Enqueue('e');
|
||||
queue.Enqueue('f');
|
||||
|
||||
char[] expectedValues = { 'f', 'e', 'd', 'c', 'b', 'a' };
|
||||
int expectedValueIndex = 0;
|
||||
|
||||
// Assert items are enumerated in correct order
|
||||
foreach (var item in queue)
|
||||
{
|
||||
Assert.AreEqual(expectedValues[expectedValueIndex], item);
|
||||
expectedValueIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
osu.Game.Tests/NonVisual/SessionStaticsTest.cs
Normal file
47
osu.Game.Tests/NonVisual/SessionStaticsTest.cs
Normal file
@ -0,0 +1,47 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Input;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
[TestFixture]
|
||||
public class SessionStaticsTest
|
||||
{
|
||||
private SessionStatics sessionStatics;
|
||||
private IdleTracker sessionIdleTracker;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
sessionStatics = new SessionStatics();
|
||||
sessionIdleTracker = new GameIdleTracker(1000);
|
||||
|
||||
sessionStatics.SetValue(Static.LoginOverlayDisplayed, true);
|
||||
sessionStatics.SetValue(Static.MutedAudioNotificationShownOnce, true);
|
||||
sessionStatics.SetValue(Static.LowBatteryNotificationShownOnce, true);
|
||||
sessionStatics.SetValue(Static.LastHoverSoundPlaybackTime, (double?)1d);
|
||||
|
||||
sessionIdleTracker.IsIdle.BindValueChanged(e =>
|
||||
{
|
||||
if (e.NewValue)
|
||||
sessionStatics.ResetValues();
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Timeout(2000)]
|
||||
public void TestSessionStaticsReset()
|
||||
{
|
||||
sessionIdleTracker.IsIdle.BindValueChanged(e =>
|
||||
{
|
||||
Assert.IsTrue(sessionStatics.GetBindable<bool>(Static.LoginOverlayDisplayed).IsDefault);
|
||||
Assert.IsTrue(sessionStatics.GetBindable<bool>(Static.MutedAudioNotificationShownOnce).IsDefault);
|
||||
Assert.IsTrue(sessionStatics.GetBindable<bool>(Static.LowBatteryNotificationShownOnce).IsDefault);
|
||||
Assert.IsTrue(sessionStatics.GetBindable<double?>(Static.LastHoverSoundPlaybackTime).IsDefault);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -59,7 +59,7 @@ namespace osu.Game.Tests.NonVisual.Skinning
|
||||
}
|
||||
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotSupportedException();
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotSupportedException();
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotSupportedException();
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
|
@ -1,296 +0,0 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
[TestFixture]
|
||||
public class StreamingFramedReplayInputHandlerTest
|
||||
{
|
||||
private Replay replay;
|
||||
private TestInputHandler handler;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
handler = new TestInputHandler(replay = new Replay
|
||||
{
|
||||
HasReceivedAllFrames = false,
|
||||
Frames = new List<ReplayFrame>
|
||||
{
|
||||
new TestReplayFrame(0),
|
||||
new TestReplayFrame(1000),
|
||||
new TestReplayFrame(2000),
|
||||
new TestReplayFrame(3000, true),
|
||||
new TestReplayFrame(4000, true),
|
||||
new TestReplayFrame(5000, true),
|
||||
new TestReplayFrame(7000, true),
|
||||
new TestReplayFrame(8000),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNormalPlayback()
|
||||
{
|
||||
Assert.IsNull(handler.CurrentFrame);
|
||||
|
||||
confirmCurrentFrame(null);
|
||||
confirmNextFrame(0);
|
||||
|
||||
setTime(0, 0);
|
||||
confirmCurrentFrame(0);
|
||||
confirmNextFrame(1);
|
||||
|
||||
// if we hit the first frame perfectly, time should progress to it.
|
||||
setTime(1000, 1000);
|
||||
confirmCurrentFrame(1);
|
||||
confirmNextFrame(2);
|
||||
|
||||
// in between non-important frames should progress based on input.
|
||||
setTime(1200, 1200);
|
||||
confirmCurrentFrame(1);
|
||||
|
||||
setTime(1400, 1400);
|
||||
confirmCurrentFrame(1);
|
||||
|
||||
// progressing beyond the next frame should force time to that frame once.
|
||||
setTime(2200, 2000);
|
||||
confirmCurrentFrame(2);
|
||||
|
||||
// second attempt should progress to input time
|
||||
setTime(2200, 2200);
|
||||
confirmCurrentFrame(2);
|
||||
|
||||
// entering important section
|
||||
setTime(3000, 3000);
|
||||
confirmCurrentFrame(3);
|
||||
|
||||
// cannot progress within
|
||||
setTime(3500, null);
|
||||
confirmCurrentFrame(3);
|
||||
|
||||
setTime(4000, 4000);
|
||||
confirmCurrentFrame(4);
|
||||
|
||||
// still cannot progress
|
||||
setTime(4500, null);
|
||||
confirmCurrentFrame(4);
|
||||
|
||||
setTime(5200, 5000);
|
||||
confirmCurrentFrame(5);
|
||||
|
||||
// important section AllowedImportantTimeSpan allowance
|
||||
setTime(5200, 5200);
|
||||
confirmCurrentFrame(5);
|
||||
|
||||
setTime(7200, 7000);
|
||||
confirmCurrentFrame(6);
|
||||
|
||||
setTime(7200, null);
|
||||
confirmCurrentFrame(6);
|
||||
|
||||
// exited important section
|
||||
setTime(8200, 8000);
|
||||
confirmCurrentFrame(7);
|
||||
confirmNextFrame(null);
|
||||
|
||||
setTime(8200, null);
|
||||
confirmCurrentFrame(7);
|
||||
confirmNextFrame(null);
|
||||
|
||||
setTime(8400, null);
|
||||
confirmCurrentFrame(7);
|
||||
confirmNextFrame(null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestIntroTime()
|
||||
{
|
||||
setTime(-1000, -1000);
|
||||
confirmCurrentFrame(null);
|
||||
confirmNextFrame(0);
|
||||
|
||||
setTime(-500, -500);
|
||||
confirmCurrentFrame(null);
|
||||
confirmNextFrame(0);
|
||||
|
||||
setTime(0, 0);
|
||||
confirmCurrentFrame(0);
|
||||
confirmNextFrame(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBasicRewind()
|
||||
{
|
||||
setTime(2800, 0);
|
||||
setTime(2800, 1000);
|
||||
setTime(2800, 2000);
|
||||
setTime(2800, 2800);
|
||||
confirmCurrentFrame(2);
|
||||
confirmNextFrame(3);
|
||||
|
||||
// pivot without crossing a frame boundary
|
||||
setTime(2700, 2700);
|
||||
confirmCurrentFrame(2);
|
||||
confirmNextFrame(1);
|
||||
|
||||
// cross current frame boundary; should not yet update frame
|
||||
setTime(1980, 1980);
|
||||
confirmCurrentFrame(2);
|
||||
confirmNextFrame(1);
|
||||
|
||||
setTime(1200, 1200);
|
||||
confirmCurrentFrame(2);
|
||||
confirmNextFrame(1);
|
||||
|
||||
// ensure each frame plays out until start
|
||||
setTime(-500, 1000);
|
||||
confirmCurrentFrame(1);
|
||||
confirmNextFrame(0);
|
||||
|
||||
setTime(-500, 0);
|
||||
confirmCurrentFrame(0);
|
||||
confirmNextFrame(null);
|
||||
|
||||
setTime(-500, -500);
|
||||
confirmCurrentFrame(0);
|
||||
confirmNextFrame(null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRewindInsideImportantSection()
|
||||
{
|
||||
fastForwardToPoint(3000);
|
||||
|
||||
setTime(4000, 4000);
|
||||
confirmCurrentFrame(4);
|
||||
confirmNextFrame(5);
|
||||
|
||||
setTime(3500, null);
|
||||
confirmCurrentFrame(4);
|
||||
confirmNextFrame(3);
|
||||
|
||||
setTime(3000, 3000);
|
||||
confirmCurrentFrame(3);
|
||||
confirmNextFrame(2);
|
||||
|
||||
setTime(3500, null);
|
||||
confirmCurrentFrame(3);
|
||||
confirmNextFrame(4);
|
||||
|
||||
setTime(4000, 4000);
|
||||
confirmCurrentFrame(4);
|
||||
confirmNextFrame(5);
|
||||
|
||||
setTime(4500, null);
|
||||
confirmCurrentFrame(4);
|
||||
confirmNextFrame(5);
|
||||
|
||||
setTime(4000, null);
|
||||
confirmCurrentFrame(4);
|
||||
confirmNextFrame(5);
|
||||
|
||||
setTime(3500, null);
|
||||
confirmCurrentFrame(4);
|
||||
confirmNextFrame(3);
|
||||
|
||||
setTime(3000, 3000);
|
||||
confirmCurrentFrame(3);
|
||||
confirmNextFrame(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRewindOutOfImportantSection()
|
||||
{
|
||||
fastForwardToPoint(3500);
|
||||
|
||||
confirmCurrentFrame(3);
|
||||
confirmNextFrame(4);
|
||||
|
||||
setTime(3200, null);
|
||||
// next frame doesn't change even though direction reversed, because of important section.
|
||||
confirmCurrentFrame(3);
|
||||
confirmNextFrame(4);
|
||||
|
||||
setTime(3000, null);
|
||||
confirmCurrentFrame(3);
|
||||
confirmNextFrame(4);
|
||||
|
||||
setTime(2800, 2800);
|
||||
confirmCurrentFrame(3);
|
||||
confirmNextFrame(2);
|
||||
}
|
||||
|
||||
private void fastForwardToPoint(double destination)
|
||||
{
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
if (handler.SetFrameFromTime(destination) == null)
|
||||
return;
|
||||
}
|
||||
|
||||
throw new TimeoutException("Seek was never fulfilled");
|
||||
}
|
||||
|
||||
private void setTime(double set, double? expect)
|
||||
{
|
||||
Assert.AreEqual(expect, handler.SetFrameFromTime(set));
|
||||
}
|
||||
|
||||
private void confirmCurrentFrame(int? frame)
|
||||
{
|
||||
if (frame.HasValue)
|
||||
{
|
||||
Assert.IsNotNull(handler.CurrentFrame);
|
||||
Assert.AreEqual(replay.Frames[frame.Value].Time, handler.CurrentFrame.Time);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsNull(handler.CurrentFrame);
|
||||
}
|
||||
}
|
||||
|
||||
private void confirmNextFrame(int? frame)
|
||||
{
|
||||
if (frame.HasValue)
|
||||
{
|
||||
Assert.IsNotNull(handler.NextFrame);
|
||||
Assert.AreEqual(replay.Frames[frame.Value].Time, handler.NextFrame.Time);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.IsNull(handler.NextFrame);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestReplayFrame : ReplayFrame
|
||||
{
|
||||
public readonly bool IsImportant;
|
||||
|
||||
public TestReplayFrame(double time, bool isImportant = false)
|
||||
: base(time)
|
||||
{
|
||||
IsImportant = isImportant;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestInputHandler : FramedReplayInputHandler<TestReplayFrame>
|
||||
{
|
||||
public TestInputHandler(Replay replay)
|
||||
: base(replay)
|
||||
{
|
||||
FrameAccuratePlayback = true;
|
||||
}
|
||||
|
||||
protected override double AllowedImportantTimeSpan => 1000;
|
||||
|
||||
protected override bool IsImportant(TestReplayFrame frame) => frame.IsImportant;
|
||||
}
|
||||
}
|
||||
}
|
111
osu.Game.Tests/NonVisual/TaskChainTest.cs
Normal file
111
osu.Game.Tests/NonVisual/TaskChainTest.cs
Normal file
@ -0,0 +1,111 @@
|
||||
// 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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Utils;
|
||||
|
||||
namespace osu.Game.Tests.NonVisual
|
||||
{
|
||||
[TestFixture]
|
||||
public class TaskChainTest
|
||||
{
|
||||
private TaskChain taskChain;
|
||||
private int currentTask;
|
||||
private CancellationTokenSource globalCancellationToken;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
globalCancellationToken = new CancellationTokenSource();
|
||||
taskChain = new TaskChain();
|
||||
currentTask = 0;
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
globalCancellationToken?.Cancel();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestChainedTasksRunSequentially()
|
||||
{
|
||||
var task1 = addTask();
|
||||
var task2 = addTask();
|
||||
var task3 = addTask();
|
||||
|
||||
task3.mutex.Set();
|
||||
task2.mutex.Set();
|
||||
task1.mutex.Set();
|
||||
|
||||
await Task.WhenAll(task1.task, task2.task, task3.task);
|
||||
|
||||
Assert.That(task1.task.Result, Is.EqualTo(1));
|
||||
Assert.That(task2.task.Result, Is.EqualTo(2));
|
||||
Assert.That(task3.task.Result, Is.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestChainedTaskWithIntermediateCancelRunsInSequence()
|
||||
{
|
||||
var task1 = addTask();
|
||||
var task2 = addTask();
|
||||
var task3 = addTask();
|
||||
|
||||
// Cancel task2, allow task3 to complete.
|
||||
task2.cancellation.Cancel();
|
||||
task2.mutex.Set();
|
||||
task3.mutex.Set();
|
||||
|
||||
// Allow task3 to potentially complete.
|
||||
Thread.Sleep(1000);
|
||||
|
||||
// Allow task1 to complete.
|
||||
task1.mutex.Set();
|
||||
|
||||
// Wait on both tasks.
|
||||
await Task.WhenAll(task1.task, task3.task);
|
||||
|
||||
Assert.That(task1.task.Result, Is.EqualTo(1));
|
||||
Assert.That(task2.task.IsCompleted, Is.False);
|
||||
Assert.That(task3.task.Result, Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestChainedTaskDoesNotCompleteBeforeChildTasks()
|
||||
{
|
||||
var mutex = new ManualResetEventSlim(false);
|
||||
|
||||
var task = taskChain.Add(async () => await Task.Run(() => mutex.Wait(globalCancellationToken.Token)));
|
||||
|
||||
// Allow task to potentially complete
|
||||
Thread.Sleep(1000);
|
||||
|
||||
Assert.That(task.IsCompleted, Is.False);
|
||||
|
||||
// Allow the task to complete.
|
||||
mutex.Set();
|
||||
|
||||
await task;
|
||||
}
|
||||
|
||||
private (Task<int> task, ManualResetEventSlim mutex, CancellationTokenSource cancellation) addTask()
|
||||
{
|
||||
var mutex = new ManualResetEventSlim(false);
|
||||
var completionSource = new TaskCompletionSource<int>();
|
||||
|
||||
var cancellationSource = new CancellationTokenSource();
|
||||
var token = CancellationTokenSource.CreateLinkedTokenSource(cancellationSource.Token, globalCancellationToken.Token);
|
||||
|
||||
taskChain.Add(() =>
|
||||
{
|
||||
mutex.Wait(globalCancellationToken.Token);
|
||||
completionSource.SetResult(Interlocked.Increment(ref currentTask));
|
||||
}, token.Token);
|
||||
|
||||
return (completionSource.Task, mutex, cancellationSource);
|
||||
}
|
||||
}
|
||||
}
|
195
osu.Game.Tests/Online/TestAPIModJsonSerialization.cs
Normal file
195
osu.Game.Tests/Online/TestAPIModJsonSerialization.cs
Normal file
@ -0,0 +1,195 @@
|
||||
// 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 Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Tests.Online
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestAPIModJsonSerialization
|
||||
{
|
||||
[Test]
|
||||
public void TestAcronymIsPreserved()
|
||||
{
|
||||
var apiMod = new APIMod(new TestMod());
|
||||
|
||||
var deserialized = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
|
||||
|
||||
Assert.That(deserialized?.Acronym, Is.EqualTo(apiMod.Acronym));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRawSettingIsPreserved()
|
||||
{
|
||||
var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } });
|
||||
|
||||
var deserialized = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
|
||||
|
||||
Assert.That(deserialized?.Settings, Contains.Key("test_setting").With.ContainValue(2.0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestConvertedModHasCorrectSetting()
|
||||
{
|
||||
var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } });
|
||||
|
||||
var deserialized = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
|
||||
var converted = (TestMod)deserialized?.ToMod(new TestRuleset());
|
||||
|
||||
Assert.That(converted?.TestSetting.Value, Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDeserialiseTimeRampMod()
|
||||
{
|
||||
// Create the mod with values different from default.
|
||||
var apiMod = new APIMod(new TestModTimeRamp
|
||||
{
|
||||
AdjustPitch = { Value = false },
|
||||
InitialRate = { Value = 1.25 },
|
||||
FinalRate = { Value = 0.25 }
|
||||
});
|
||||
|
||||
var deserialised = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
|
||||
var converted = (TestModTimeRamp)deserialised?.ToMod(new TestRuleset());
|
||||
|
||||
Assert.That(converted?.AdjustPitch.Value, Is.EqualTo(false));
|
||||
Assert.That(converted?.InitialRate.Value, Is.EqualTo(1.25));
|
||||
Assert.That(converted?.FinalRate.Value, Is.EqualTo(0.25));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDeserialiseDifficultyAdjustModWithExtendedLimits()
|
||||
{
|
||||
var apiMod = new APIMod(new TestModDifficultyAdjust
|
||||
{
|
||||
OverallDifficulty = { Value = 11 },
|
||||
ExtendedLimits = { Value = true }
|
||||
});
|
||||
|
||||
var deserialised = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
|
||||
var converted = (TestModDifficultyAdjust)deserialised?.ToMod(new TestRuleset());
|
||||
|
||||
Assert.That(converted?.ExtendedLimits.Value, Is.True);
|
||||
Assert.That(converted?.OverallDifficulty.Value, Is.EqualTo(11));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDeserialiseScoreInfoWithEmptyMods()
|
||||
{
|
||||
var score = new ScoreInfo { Ruleset = new OsuRuleset().RulesetInfo };
|
||||
|
||||
var deserialised = JsonConvert.DeserializeObject<ScoreInfo>(JsonConvert.SerializeObject(score));
|
||||
|
||||
if (deserialised != null)
|
||||
deserialised.Ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
Assert.That(deserialised?.Mods.Length, Is.Zero);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDeserialiseScoreInfoWithCustomModSetting()
|
||||
{
|
||||
var score = new ScoreInfo
|
||||
{
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
Mods = new Mod[] { new OsuModDoubleTime { SpeedChange = { Value = 2 } } }
|
||||
};
|
||||
|
||||
var deserialised = JsonConvert.DeserializeObject<ScoreInfo>(JsonConvert.SerializeObject(score));
|
||||
|
||||
if (deserialised != null)
|
||||
deserialised.Ruleset = new OsuRuleset().RulesetInfo;
|
||||
|
||||
Assert.That(((OsuModDoubleTime)deserialised?.Mods[0])?.SpeedChange.Value, Is.EqualTo(2));
|
||||
}
|
||||
|
||||
private class TestRuleset : Ruleset
|
||||
{
|
||||
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[]
|
||||
{
|
||||
new TestMod(),
|
||||
new TestModTimeRamp(),
|
||||
new TestModDifficultyAdjust()
|
||||
};
|
||||
|
||||
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new System.NotImplementedException();
|
||||
|
||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new System.NotImplementedException();
|
||||
|
||||
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new System.NotImplementedException();
|
||||
|
||||
public override string Description { get; } = string.Empty;
|
||||
public override string ShortName { get; } = string.Empty;
|
||||
}
|
||||
|
||||
private class TestMod : Mod
|
||||
{
|
||||
public override string Name => "Test Mod";
|
||||
public override string Acronym => "TM";
|
||||
public override string Description => "This is a test mod.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
[SettingSource("Test")]
|
||||
public BindableNumber<double> TestSetting { get; } = new BindableDouble
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 10,
|
||||
Default = 5,
|
||||
Precision = 0.01,
|
||||
};
|
||||
}
|
||||
|
||||
private class TestModTimeRamp : ModTimeRamp
|
||||
{
|
||||
public override string Name => "Test Mod";
|
||||
public override string Acronym => "TMTR";
|
||||
public override string Description => "This is a test mod.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
[SettingSource("Initial rate", "The starting speed of the track")]
|
||||
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
|
||||
{
|
||||
MinValue = 1,
|
||||
MaxValue = 2,
|
||||
Default = 1.5,
|
||||
Value = 1.5,
|
||||
Precision = 0.01,
|
||||
};
|
||||
|
||||
[SettingSource("Final rate", "The speed increase to ramp towards")]
|
||||
public override BindableNumber<double> FinalRate { get; } = new BindableDouble
|
||||
{
|
||||
MinValue = 0,
|
||||
MaxValue = 1,
|
||||
Default = 0.5,
|
||||
Value = 0.5,
|
||||
Precision = 0.01,
|
||||
};
|
||||
|
||||
[SettingSource("Adjust pitch", "Should pitch be adjusted with speed")]
|
||||
public override BindableBool AdjustPitch { get; } = new BindableBool
|
||||
{
|
||||
Default = true,
|
||||
Value = true
|
||||
};
|
||||
}
|
||||
|
||||
private class TestModDifficultyAdjust : ModDifficultyAdjust
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using MessagePack;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -16,14 +16,14 @@ using osu.Game.Rulesets.UI;
|
||||
namespace osu.Game.Tests.Online
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestAPIModSerialization
|
||||
public class TestAPIModMessagePackSerialization
|
||||
{
|
||||
[Test]
|
||||
public void TestAcronymIsPreserved()
|
||||
{
|
||||
var apiMod = new APIMod(new TestMod());
|
||||
|
||||
var deserialized = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
|
||||
var deserialized = MessagePackSerializer.Deserialize<APIMod>(MessagePackSerializer.Serialize(apiMod));
|
||||
|
||||
Assert.That(deserialized.Acronym, Is.EqualTo(apiMod.Acronym));
|
||||
}
|
||||
@ -33,7 +33,7 @@ namespace osu.Game.Tests.Online
|
||||
{
|
||||
var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } });
|
||||
|
||||
var deserialized = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
|
||||
var deserialized = MessagePackSerializer.Deserialize<APIMod>(MessagePackSerializer.Serialize(apiMod));
|
||||
|
||||
Assert.That(deserialized.Settings, Contains.Key("test_setting").With.ContainValue(2.0));
|
||||
}
|
||||
@ -43,7 +43,7 @@ namespace osu.Game.Tests.Online
|
||||
{
|
||||
var apiMod = new APIMod(new TestMod { TestSetting = { Value = 2 } });
|
||||
|
||||
var deserialized = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
|
||||
var deserialized = MessagePackSerializer.Deserialize<APIMod>(MessagePackSerializer.Serialize(apiMod));
|
||||
var converted = (TestMod)deserialized.ToMod(new TestRuleset());
|
||||
|
||||
Assert.That(converted.TestSetting.Value, Is.EqualTo(2));
|
||||
@ -60,7 +60,7 @@ namespace osu.Game.Tests.Online
|
||||
FinalRate = { Value = 0.25 }
|
||||
});
|
||||
|
||||
var deserialised = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
|
||||
var deserialised = MessagePackSerializer.Deserialize<APIMod>(MessagePackSerializer.Serialize(apiMod));
|
||||
var converted = (TestModTimeRamp)deserialised.ToMod(new TestRuleset());
|
||||
|
||||
Assert.That(converted.AdjustPitch.Value, Is.EqualTo(false));
|
||||
@ -68,6 +68,16 @@ namespace osu.Game.Tests.Online
|
||||
Assert.That(converted.FinalRate.Value, Is.EqualTo(0.25));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDeserialiseEnumMod()
|
||||
{
|
||||
var apiMod = new APIMod(new TestModEnum { TestSetting = { Value = TestEnum.Value2 } });
|
||||
|
||||
var deserialized = MessagePackSerializer.Deserialize<APIMod>(MessagePackSerializer.Serialize(apiMod));
|
||||
|
||||
Assert.That(deserialized.Settings, Contains.Key("test_setting").With.ContainValue(1));
|
||||
}
|
||||
|
||||
private class TestRuleset : Ruleset
|
||||
{
|
||||
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[]
|
||||
@ -90,6 +100,7 @@ namespace osu.Game.Tests.Online
|
||||
{
|
||||
public override string Name => "Test Mod";
|
||||
public override string Acronym => "TM";
|
||||
public override string Description => "This is a test mod.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
[SettingSource("Test")]
|
||||
@ -106,6 +117,7 @@ namespace osu.Game.Tests.Online
|
||||
{
|
||||
public override string Name => "Test Mod";
|
||||
public override string Acronym => "TMTR";
|
||||
public override string Description => "This is a test mod.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
[SettingSource("Initial rate", "The starting speed of the track")]
|
||||
@ -135,5 +147,23 @@ namespace osu.Game.Tests.Online
|
||||
Value = true
|
||||
};
|
||||
}
|
||||
|
||||
private class TestModEnum : Mod
|
||||
{
|
||||
public override string Name => "Test Mod";
|
||||
public override string Acronym => "TM";
|
||||
public override string Description => "This is a test mod.";
|
||||
public override double ScoreMultiplier => 1;
|
||||
|
||||
[SettingSource("Test")]
|
||||
public Bindable<TestEnum> TestSetting { get; } = new Bindable<TestEnum>();
|
||||
}
|
||||
|
||||
private enum TestEnum
|
||||
{
|
||||
Value1 = 0,
|
||||
Value2 = 1,
|
||||
Value3 = 2
|
||||
}
|
||||
}
|
||||
}
|
@ -23,8 +23,10 @@ namespace osu.Game.Tests.Online
|
||||
{
|
||||
case CommentVoteRequest cRequest:
|
||||
cRequest.TriggerSuccess(new CommentBundle());
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
CommentVoteRequest request = null;
|
||||
@ -108,8 +110,10 @@ namespace osu.Game.Tests.Online
|
||||
{
|
||||
case LeaveChannelRequest cRequest:
|
||||
cRequest.TriggerSuccess();
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,188 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.Online
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestSceneOnlinePlayBeatmapAvailabilityTracker : OsuTestScene
|
||||
{
|
||||
private RulesetStore rulesets;
|
||||
private TestBeatmapManager beatmaps;
|
||||
|
||||
private string testBeatmapFile;
|
||||
private BeatmapInfo testBeatmapInfo;
|
||||
private BeatmapSetInfo testBeatmapSet;
|
||||
|
||||
private readonly Bindable<PlaylistItem> selectedItem = new Bindable<PlaylistItem>();
|
||||
private OnlinePlayBeatmapAvailabilityTracker availabilityTracker;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, GameHost host)
|
||||
{
|
||||
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
||||
Dependencies.CacheAs<BeatmapManager>(beatmaps = new TestBeatmapManager(LocalStorage, ContextFactory, rulesets, API, audio, host, Beatmap.Default));
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
beatmaps.AllowImport = new TaskCompletionSource<bool>();
|
||||
|
||||
testBeatmapFile = TestResources.GetQuickTestBeatmapForImport();
|
||||
|
||||
testBeatmapInfo = getTestBeatmapInfo(testBeatmapFile);
|
||||
testBeatmapSet = testBeatmapInfo.BeatmapSet;
|
||||
|
||||
var existing = beatmaps.QueryBeatmapSet(s => s.OnlineBeatmapSetID == testBeatmapSet.OnlineBeatmapSetID);
|
||||
if (existing != null)
|
||||
beatmaps.Delete(existing);
|
||||
|
||||
selectedItem.Value = new PlaylistItem
|
||||
{
|
||||
Beatmap = { Value = testBeatmapInfo },
|
||||
Ruleset = { Value = testBeatmapInfo.Ruleset },
|
||||
};
|
||||
|
||||
Child = availabilityTracker = new OnlinePlayBeatmapAvailabilityTracker
|
||||
{
|
||||
SelectedItem = { BindTarget = selectedItem, }
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestBeatmapDownloadingFlow()
|
||||
{
|
||||
AddAssert("ensure beatmap unavailable", () => !beatmaps.IsAvailableLocally(testBeatmapSet));
|
||||
addAvailabilityCheckStep("state not downloaded", BeatmapAvailability.NotDownloaded);
|
||||
|
||||
AddStep("start downloading", () => beatmaps.Download(testBeatmapSet));
|
||||
addAvailabilityCheckStep("state downloading 0%", () => BeatmapAvailability.Downloading(0.0f));
|
||||
|
||||
AddStep("set progress 40%", () => ((TestDownloadRequest)beatmaps.GetExistingDownload(testBeatmapSet)).SetProgress(0.4f));
|
||||
addAvailabilityCheckStep("state downloading 40%", () => BeatmapAvailability.Downloading(0.4f));
|
||||
|
||||
AddStep("finish download", () => ((TestDownloadRequest)beatmaps.GetExistingDownload(testBeatmapSet)).TriggerSuccess(testBeatmapFile));
|
||||
addAvailabilityCheckStep("state importing", BeatmapAvailability.Importing);
|
||||
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true));
|
||||
AddUntilStep("wait for import", () => beatmaps.CurrentImportTask?.IsCompleted == true);
|
||||
addAvailabilityCheckStep("state locally available", BeatmapAvailability.LocallyAvailable);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTrackerRespectsSoftDeleting()
|
||||
{
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true));
|
||||
AddStep("import beatmap", () => beatmaps.Import(testBeatmapFile).Wait());
|
||||
addAvailabilityCheckStep("state locally available", BeatmapAvailability.LocallyAvailable);
|
||||
|
||||
AddStep("delete beatmap", () => beatmaps.Delete(beatmaps.QueryBeatmapSet(b => b.OnlineBeatmapSetID == testBeatmapSet.OnlineBeatmapSetID)));
|
||||
addAvailabilityCheckStep("state not downloaded", BeatmapAvailability.NotDownloaded);
|
||||
|
||||
AddStep("undelete beatmap", () => beatmaps.Undelete(beatmaps.QueryBeatmapSet(b => b.OnlineBeatmapSetID == testBeatmapSet.OnlineBeatmapSetID)));
|
||||
addAvailabilityCheckStep("state locally available", BeatmapAvailability.LocallyAvailable);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTrackerRespectsChecksum()
|
||||
{
|
||||
AddStep("allow importing", () => beatmaps.AllowImport.SetResult(true));
|
||||
|
||||
AddStep("import altered beatmap", () =>
|
||||
{
|
||||
beatmaps.Import(TestResources.GetTestBeatmapForImport(true)).Wait();
|
||||
});
|
||||
addAvailabilityCheckStep("state still not downloaded", BeatmapAvailability.NotDownloaded);
|
||||
|
||||
AddStep("recreate tracker", () => Child = availabilityTracker = new OnlinePlayBeatmapAvailabilityTracker
|
||||
{
|
||||
SelectedItem = { BindTarget = selectedItem }
|
||||
});
|
||||
addAvailabilityCheckStep("state not downloaded as well", BeatmapAvailability.NotDownloaded);
|
||||
}
|
||||
|
||||
private void addAvailabilityCheckStep(string description, Func<BeatmapAvailability> expected)
|
||||
{
|
||||
AddAssert(description, () => availabilityTracker.Availability.Value.Equals(expected.Invoke()));
|
||||
}
|
||||
|
||||
private static BeatmapInfo getTestBeatmapInfo(string archiveFile)
|
||||
{
|
||||
BeatmapInfo info;
|
||||
|
||||
using (var archive = new ZipArchiveReader(File.OpenRead(archiveFile)))
|
||||
using (var stream = archive.GetStream("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
using (var reader = new LineBufferedReader(stream))
|
||||
{
|
||||
var decoder = Decoder.GetDecoder<Beatmap>(reader);
|
||||
var beatmap = decoder.Decode(reader);
|
||||
|
||||
info = beatmap.BeatmapInfo;
|
||||
info.BeatmapSet.Beatmaps = new List<BeatmapInfo> { info };
|
||||
info.BeatmapSet.Metadata = info.Metadata;
|
||||
info.MD5Hash = stream.ComputeMD5Hash();
|
||||
info.Hash = stream.ComputeSHA2Hash();
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
private class TestBeatmapManager : BeatmapManager
|
||||
{
|
||||
public TaskCompletionSource<bool> AllowImport = new TaskCompletionSource<bool>();
|
||||
|
||||
public Task<BeatmapSetInfo> CurrentImportTask { get; private set; }
|
||||
|
||||
protected override ArchiveDownloadRequest<BeatmapSetInfo> CreateDownloadRequest(BeatmapSetInfo set, bool minimiseDownloadSize)
|
||||
=> new TestDownloadRequest(set);
|
||||
|
||||
public TestBeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, IAPIProvider api, [NotNull] AudioManager audioManager, GameHost host = null, WorkingBeatmap defaultBeatmap = null, bool performOnlineLookups = false)
|
||||
: base(storage, contextFactory, rulesets, api, audioManager, host, defaultBeatmap, performOnlineLookups)
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task<BeatmapSetInfo> Import(BeatmapSetInfo item, ArchiveReader archive = null, bool lowPriority = false, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await AllowImport.Task;
|
||||
return await (CurrentImportTask = base.Import(item, archive, lowPriority, cancellationToken));
|
||||
}
|
||||
}
|
||||
|
||||
private class TestDownloadRequest : ArchiveDownloadRequest<BeatmapSetInfo>
|
||||
{
|
||||
public new void SetProgress(float progress) => base.SetProgress(progress);
|
||||
public new void TriggerSuccess(string filename) => base.TriggerSuccess(filename);
|
||||
|
||||
public TestDownloadRequest(BeatmapSetInfo model)
|
||||
: base(model)
|
||||
{
|
||||
}
|
||||
|
||||
protected override string Target => null;
|
||||
}
|
||||
}
|
||||
}
|
35
osu.Game.Tests/OnlinePlay/StatefulMultiplayerClientTest.cs
Normal file
35
osu.Game.Tests/OnlinePlay/StatefulMultiplayerClientTest.cs
Normal file
@ -0,0 +1,35 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Tests.Visual.Multiplayer;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Tests.OnlinePlay
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class StatefulMultiplayerClientTest : MultiplayerTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestUserAddedOnJoin()
|
||||
{
|
||||
var user = new User { Id = 33 };
|
||||
|
||||
AddRepeatStep("add user multiple times", () => Client.AddUser(user), 3);
|
||||
AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserRemovedOnLeave()
|
||||
{
|
||||
var user = new User { Id = 44 };
|
||||
|
||||
AddStep("add user", () => Client.AddUser(user));
|
||||
AddAssert("room has 2 users", () => Client.Room?.Users.Count == 2);
|
||||
|
||||
AddRepeatStep("remove user multiple times", () => Client.RemoveUser(user), 3);
|
||||
AddAssert("room has 1 user", () => Client.Room?.Users.Count == 1);
|
||||
}
|
||||
}
|
||||
}
|
223
osu.Game.Tests/OnlinePlay/TestSceneCatchUpSyncManager.cs
Normal file
223
osu.Game.Tests/OnlinePlay/TestSceneCatchUpSyncManager.cs
Normal file
@ -0,0 +1,223 @@
|
||||
// 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;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Tests.OnlinePlay
|
||||
{
|
||||
[HeadlessTest]
|
||||
public class TestSceneCatchUpSyncManager : OsuTestScene
|
||||
{
|
||||
private TestManualClock master;
|
||||
private CatchUpSyncManager syncManager;
|
||||
|
||||
private TestSpectatorPlayerClock player1;
|
||||
private TestSpectatorPlayerClock player2;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
syncManager = new CatchUpSyncManager(master = new TestManualClock());
|
||||
syncManager.AddPlayerClock(player1 = new TestSpectatorPlayerClock(1));
|
||||
syncManager.AddPlayerClock(player2 = new TestSpectatorPlayerClock(2));
|
||||
|
||||
Schedule(() => Child = syncManager);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMasterClockStartsWhenAllPlayerClocksHaveFrames()
|
||||
{
|
||||
setWaiting(() => player1, false);
|
||||
assertMasterState(false);
|
||||
assertPlayerClockState(() => player1, false);
|
||||
assertPlayerClockState(() => player2, false);
|
||||
|
||||
setWaiting(() => player2, false);
|
||||
assertMasterState(true);
|
||||
assertPlayerClockState(() => player1, true);
|
||||
assertPlayerClockState(() => player2, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMasterClockDoesNotStartWhenNoneReadyForMaximumDelayTime()
|
||||
{
|
||||
AddWaitStep($"wait {CatchUpSyncManager.MAXIMUM_START_DELAY} milliseconds", (int)Math.Ceiling(CatchUpSyncManager.MAXIMUM_START_DELAY / TimePerAction));
|
||||
assertMasterState(false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMasterClockStartsWhenAnyReadyForMaximumDelayTime()
|
||||
{
|
||||
setWaiting(() => player1, false);
|
||||
AddWaitStep($"wait {CatchUpSyncManager.MAXIMUM_START_DELAY} milliseconds", (int)Math.Ceiling(CatchUpSyncManager.MAXIMUM_START_DELAY / TimePerAction));
|
||||
assertMasterState(true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPlayerClockDoesNotCatchUpWhenSlightlyOutOfSync()
|
||||
{
|
||||
setAllWaiting(false);
|
||||
|
||||
setMasterTime(CatchUpSyncManager.SYNC_TARGET + 1);
|
||||
assertCatchingUp(() => player1, false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPlayerClockStartsCatchingUpWhenTooFarBehind()
|
||||
{
|
||||
setAllWaiting(false);
|
||||
|
||||
setMasterTime(CatchUpSyncManager.MAX_SYNC_OFFSET + 1);
|
||||
assertCatchingUp(() => player1, true);
|
||||
assertCatchingUp(() => player2, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPlayerClockKeepsCatchingUpWhenSlightlyOutOfSync()
|
||||
{
|
||||
setAllWaiting(false);
|
||||
|
||||
setMasterTime(CatchUpSyncManager.MAX_SYNC_OFFSET + 1);
|
||||
setPlayerClockTime(() => player1, CatchUpSyncManager.SYNC_TARGET + 1);
|
||||
assertCatchingUp(() => player1, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPlayerClockStopsCatchingUpWhenInSync()
|
||||
{
|
||||
setAllWaiting(false);
|
||||
|
||||
setMasterTime(CatchUpSyncManager.MAX_SYNC_OFFSET + 2);
|
||||
setPlayerClockTime(() => player1, CatchUpSyncManager.SYNC_TARGET);
|
||||
assertCatchingUp(() => player1, false);
|
||||
assertCatchingUp(() => player2, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPlayerClockDoesNotStopWhenSlightlyAhead()
|
||||
{
|
||||
setAllWaiting(false);
|
||||
|
||||
setPlayerClockTime(() => player1, -CatchUpSyncManager.SYNC_TARGET);
|
||||
assertCatchingUp(() => player1, false);
|
||||
assertPlayerClockState(() => player1, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPlayerClockStopsWhenTooFarAheadAndStartsWhenBackInSync()
|
||||
{
|
||||
setAllWaiting(false);
|
||||
|
||||
setPlayerClockTime(() => player1, -CatchUpSyncManager.SYNC_TARGET - 1);
|
||||
|
||||
// This is a silent catchup, where IsCatchingUp = false but IsRunning = false also.
|
||||
assertCatchingUp(() => player1, false);
|
||||
assertPlayerClockState(() => player1, false);
|
||||
|
||||
setMasterTime(1);
|
||||
assertCatchingUp(() => player1, false);
|
||||
assertPlayerClockState(() => player1, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInSyncPlayerClockDoesNotStartIfWaitingOnFrames()
|
||||
{
|
||||
setAllWaiting(false);
|
||||
|
||||
assertPlayerClockState(() => player1, true);
|
||||
setWaiting(() => player1, true);
|
||||
assertPlayerClockState(() => player1, false);
|
||||
}
|
||||
|
||||
private void setWaiting(Func<TestSpectatorPlayerClock> playerClock, bool waiting)
|
||||
=> AddStep($"set player clock {playerClock().Id} waiting = {waiting}", () => playerClock().WaitingOnFrames.Value = waiting);
|
||||
|
||||
private void setAllWaiting(bool waiting) => AddStep($"set all player clocks waiting = {waiting}", () =>
|
||||
{
|
||||
player1.WaitingOnFrames.Value = waiting;
|
||||
player2.WaitingOnFrames.Value = waiting;
|
||||
});
|
||||
|
||||
private void setMasterTime(double time)
|
||||
=> AddStep($"set master = {time}", () => master.Seek(time));
|
||||
|
||||
/// <summary>
|
||||
/// clock.Time = master.Time - offsetFromMaster
|
||||
/// </summary>
|
||||
private void setPlayerClockTime(Func<TestSpectatorPlayerClock> playerClock, double offsetFromMaster)
|
||||
=> AddStep($"set player clock {playerClock().Id} = master - {offsetFromMaster}", () => playerClock().Seek(master.CurrentTime - offsetFromMaster));
|
||||
|
||||
private void assertMasterState(bool running)
|
||||
=> AddAssert($"master clock {(running ? "is" : "is not")} running", () => master.IsRunning == running);
|
||||
|
||||
private void assertCatchingUp(Func<TestSpectatorPlayerClock> playerClock, bool catchingUp) =>
|
||||
AddAssert($"player clock {playerClock().Id} {(catchingUp ? "is" : "is not")} catching up", () => playerClock().IsCatchingUp == catchingUp);
|
||||
|
||||
private void assertPlayerClockState(Func<TestSpectatorPlayerClock> playerClock, bool running)
|
||||
=> AddAssert($"player clock {playerClock().Id} {(running ? "is" : "is not")} running", () => playerClock().IsRunning == running);
|
||||
|
||||
private class TestSpectatorPlayerClock : TestManualClock, ISpectatorPlayerClock
|
||||
{
|
||||
public Bindable<bool> WaitingOnFrames { get; } = new Bindable<bool>(true);
|
||||
|
||||
public bool IsCatchingUp { get; set; }
|
||||
|
||||
public IFrameBasedClock Source
|
||||
{
|
||||
set => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public readonly int Id;
|
||||
|
||||
public TestSpectatorPlayerClock(int id)
|
||||
{
|
||||
Id = id;
|
||||
|
||||
WaitingOnFrames.BindValueChanged(waiting =>
|
||||
{
|
||||
if (waiting.NewValue)
|
||||
Stop();
|
||||
else
|
||||
Start();
|
||||
});
|
||||
}
|
||||
|
||||
public void ProcessFrame()
|
||||
{
|
||||
}
|
||||
|
||||
public double ElapsedFrameTime => 0;
|
||||
|
||||
public double FramesPerSecond => 0;
|
||||
|
||||
public FrameTimeInfo TimeInfo => default;
|
||||
}
|
||||
|
||||
private class TestManualClock : ManualClock, IAdjustableClock
|
||||
{
|
||||
public void Start() => IsRunning = true;
|
||||
|
||||
public void Stop() => IsRunning = false;
|
||||
|
||||
public bool Seek(double position)
|
||||
{
|
||||
CurrentTime = position;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
}
|
||||
|
||||
public void ResetSpeedAdjustments()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
@ -15,6 +15,28 @@ namespace osu.Game.Tests.Resources
|
||||
|
||||
public static Stream GetTestBeatmapStream(bool virtualTrack = false) => OpenResource($"Archives/241526 Soleily - Renatus{(virtualTrack ? "_virtual" : "")}.osz");
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a path to a copy of a shortened (~10 second) beatmap archive with a virtual track.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is intended for use in tests which need to run to completion as soon as possible and don't need to test a full length beatmap.</remarks>
|
||||
/// <returns>A path to a copy of a beatmap archive (osz). Should be deleted after use.</returns>
|
||||
public static string GetQuickTestBeatmapForImport()
|
||||
{
|
||||
var tempPath = Path.GetTempFileName() + ".osz";
|
||||
using (var stream = OpenResource("Archives/241526 Soleily - Renatus_virtual_quick.osz"))
|
||||
using (var newFile = File.Create(tempPath))
|
||||
stream.CopyTo(newFile);
|
||||
|
||||
Assert.IsTrue(File.Exists(tempPath));
|
||||
return tempPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a path to a copy of a full-fledged beatmap archive.
|
||||
/// </summary>
|
||||
/// <param name="virtualTrack">Whether the audio track should be virtual.</param>
|
||||
/// <returns>A path to a copy of a beatmap archive (osz). Should be deleted after use.</returns>
|
||||
public static string GetTestBeatmapForImport(bool virtualTrack = false)
|
||||
{
|
||||
var tempPath = Path.GetTempFileName() + ".osz";
|
||||
|
9
osu.Game.Tests/Resources/animation-types.osb
Normal file
9
osu.Game.Tests/Resources/animation-types.osb
Normal file
@ -0,0 +1,9 @@
|
||||
osu file format v14
|
||||
|
||||
[Events]
|
||||
Animation,Foreground,Centre,"forever-string.png",330,240,10,108,LoopForever
|
||||
Animation,Foreground,Centre,"once-string.png",330,240,10,108,LoopOnce
|
||||
Animation,Foreground,Centre,"forever-number.png",330,240,10,108,0
|
||||
Animation,Foreground,Centre,"once-number.png",330,240,10,108,1
|
||||
Animation,Foreground,Centre,"undefined-number.png",330,240,10,108,16
|
||||
Animation,Foreground,Centre,"omitted.png",330,240,10,108
|
@ -9,3 +9,16 @@ osu file format v128
|
||||
|
||||
// Implicit multi-segment
|
||||
32,192,3000,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800
|
||||
|
||||
// Last control point duplicated
|
||||
0,0,4000,2,0,B|1:1|2:2|3:3|3:3,2,200
|
||||
|
||||
// Last control point in segment duplicated
|
||||
0,0,5000,2,0,B|1:1|2:2|3:3|3:3|B|4:4|5:5,2,200
|
||||
|
||||
// Implicit perfect-curve multi-segment (Should convert to bezier to match stable)
|
||||
0,0,6000,2,0,P|75:145|170:75|170:75|300:145|410:20,1,475,0:0:0:0:
|
||||
|
||||
// Explicit perfect-curve multi-segment (Should not convert to bezier)
|
||||
0,0,7000,2,0,P|75:145|P|170:75|300:145|410:20,1,650,0:0:0:0:
|
||||
|
||||
|
6
osu.Game.Tests/Resources/out-of-order-starttimes.osb
Normal file
6
osu.Game.Tests/Resources/out-of-order-starttimes.osb
Normal file
@ -0,0 +1,6 @@
|
||||
[Events]
|
||||
//Storyboard Layer 0 (Background)
|
||||
Sprite,Background,TopCentre,"img.jpg",320,240
|
||||
F,0,1500,1600,0,1
|
||||
Sprite,Background,TopCentre,"img.jpg",320,240
|
||||
F,0,1000,1100,0,1
|
2
osu.Game.Tests/Resources/skin-with-space.ini
Normal file
2
osu.Game.Tests/Resources/skin-with-space.ini
Normal file
@ -0,0 +1,2 @@
|
||||
[General]
|
||||
Version: 2
|
113
osu.Game.Tests/Rulesets/Mods/ModTimeRampTest.cs
Normal file
113
osu.Game.Tests/Rulesets/Mods/ModTimeRampTest.cs
Normal file
@ -0,0 +1,113 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
|
||||
namespace osu.Game.Tests.Rulesets.Mods
|
||||
{
|
||||
[TestFixture]
|
||||
public class ModTimeRampTest
|
||||
{
|
||||
private const double start_time = 1000;
|
||||
private const double duration = 9000;
|
||||
|
||||
private TrackVirtual track;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
track = new TrackVirtual(20_000);
|
||||
}
|
||||
|
||||
[TestCase(0, 1)]
|
||||
[TestCase(start_time, 1)]
|
||||
[TestCase(start_time + duration * ModTimeRamp.FINAL_RATE_PROGRESS / 2, 1.25)]
|
||||
[TestCase(start_time + duration * ModTimeRamp.FINAL_RATE_PROGRESS, 1.5)]
|
||||
[TestCase(start_time + duration, 1.5)]
|
||||
[TestCase(15000, 1.5)]
|
||||
public void TestModWindUp(double time, double expectedRate)
|
||||
{
|
||||
var beatmap = createSingleSpinnerBeatmap();
|
||||
var mod = new ModWindUp();
|
||||
mod.ApplyToBeatmap(beatmap);
|
||||
mod.ApplyToTrack(track);
|
||||
|
||||
seekTrackAndUpdateMod(mod, time);
|
||||
|
||||
Assert.That(mod.SpeedChange.Value, Is.EqualTo(expectedRate));
|
||||
}
|
||||
|
||||
[TestCase(0, 1)]
|
||||
[TestCase(start_time, 1)]
|
||||
[TestCase(start_time + duration * ModTimeRamp.FINAL_RATE_PROGRESS / 2, 0.75)]
|
||||
[TestCase(start_time + duration * ModTimeRamp.FINAL_RATE_PROGRESS, 0.5)]
|
||||
[TestCase(start_time + duration, 0.5)]
|
||||
[TestCase(15000, 0.5)]
|
||||
public void TestModWindDown(double time, double expectedRate)
|
||||
{
|
||||
var beatmap = createSingleSpinnerBeatmap();
|
||||
var mod = new ModWindDown
|
||||
{
|
||||
FinalRate = { Value = 0.5 }
|
||||
};
|
||||
mod.ApplyToBeatmap(beatmap);
|
||||
mod.ApplyToTrack(track);
|
||||
|
||||
seekTrackAndUpdateMod(mod, time);
|
||||
|
||||
Assert.That(mod.SpeedChange.Value, Is.EqualTo(expectedRate));
|
||||
}
|
||||
|
||||
[TestCase(0, 1)]
|
||||
[TestCase(start_time, 1)]
|
||||
[TestCase(2 * start_time, 1.5)]
|
||||
public void TestZeroDurationMap(double time, double expectedRate)
|
||||
{
|
||||
var beatmap = createSingleObjectBeatmap();
|
||||
var mod = new ModWindUp();
|
||||
mod.ApplyToBeatmap(beatmap);
|
||||
mod.ApplyToTrack(track);
|
||||
|
||||
seekTrackAndUpdateMod(mod, time);
|
||||
|
||||
Assert.That(mod.SpeedChange.Value, Is.EqualTo(expectedRate));
|
||||
}
|
||||
|
||||
private void seekTrackAndUpdateMod(ModTimeRamp mod, double time)
|
||||
{
|
||||
track.Seek(time);
|
||||
// update the mod via a fake playfield to re-calculate the current rate.
|
||||
mod.Update(null);
|
||||
}
|
||||
|
||||
private static Beatmap createSingleSpinnerBeatmap()
|
||||
{
|
||||
return new Beatmap
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new Spinner
|
||||
{
|
||||
StartTime = start_time,
|
||||
Duration = duration
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static Beatmap createSingleObjectBeatmap()
|
||||
{
|
||||
return new Beatmap
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new HitCircle { StartTime = start_time }
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
@ -51,7 +50,7 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
||||
};
|
||||
scoreProcessor.ApplyResult(judgementResult);
|
||||
|
||||
Assert.IsTrue(Precision.AlmostEquals(expectedScore, scoreProcessor.TotalScore.Value));
|
||||
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(expectedScore).Within(0.5d));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -77,27 +76,28 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
||||
[TestCase(ScoringMode.Standardised, HitResult.Miss, HitResult.Great, 0)] // (3 * 0) / (4 * 300) * 300_000 + (0 / 4) * 700_000
|
||||
[TestCase(ScoringMode.Standardised, HitResult.Meh, HitResult.Great, 387_500)] // (3 * 50) / (4 * 300) * 300_000 + (2 / 4) * 700_000
|
||||
[TestCase(ScoringMode.Standardised, HitResult.Ok, HitResult.Great, 425_000)] // (3 * 100) / (4 * 300) * 300_000 + (2 / 4) * 700_000
|
||||
[TestCase(ScoringMode.Standardised, HitResult.Good, HitResult.Perfect, 478_571)] // (3 * 200) / (4 * 350) * 300_000 + (2 / 4) * 700_000
|
||||
[TestCase(ScoringMode.Standardised, HitResult.Good, HitResult.Perfect, 492_857)] // (3 * 200) / (4 * 350) * 300_000 + (2 / 4) * 700_000
|
||||
[TestCase(ScoringMode.Standardised, HitResult.Great, HitResult.Great, 575_000)] // (3 * 300) / (4 * 300) * 300_000 + (2 / 4) * 700_000
|
||||
[TestCase(ScoringMode.Standardised, HitResult.Perfect, HitResult.Perfect, 575_000)] // (3 * 350) / (4 * 350) * 300_000 + (2 / 4) * 700_000
|
||||
[TestCase(ScoringMode.Standardised, HitResult.SmallTickMiss, HitResult.SmallTickHit, 700_000)] // (3 * 0) / (4 * 10) * 300_000 + 700_000 (max combo 0)
|
||||
[TestCase(ScoringMode.Standardised, HitResult.SmallTickHit, HitResult.SmallTickHit, 925_000)] // (3 * 10) / (4 * 10) * 300_000 + 700_000 (max combo 0)
|
||||
[TestCase(ScoringMode.Standardised, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)] // (3 * 0) / (4 * 30) * 300_000 + (0 / 4) * 700_000
|
||||
[TestCase(ScoringMode.Standardised, HitResult.LargeTickHit, HitResult.LargeTickHit, 575_000)] // (3 * 30) / (4 * 30) * 300_000 + (0 / 4) * 700_000
|
||||
[TestCase(ScoringMode.Standardised, HitResult.SmallBonus, HitResult.SmallBonus, 700_030)] // 0 * 300_000 + 700_000 (max combo 0) + 3 * 10 (bonus points)
|
||||
[TestCase(ScoringMode.Standardised, HitResult.LargeBonus, HitResult.LargeBonus, 700_150)] // 0 * 300_000 + 700_000 (max combo 0) + 3 * 50 (bonus points)
|
||||
[TestCase(ScoringMode.Standardised, HitResult.SmallBonus, HitResult.SmallBonus, 1_000_030)] // 1 * 300_000 + 700_000 (max combo 0) + 3 * 10 (bonus points)
|
||||
[TestCase(ScoringMode.Standardised, HitResult.LargeBonus, HitResult.LargeBonus, 1_000_150)] // 1 * 300_000 + 700_000 (max combo 0) + 3 * 50 (bonus points)
|
||||
[TestCase(ScoringMode.Classic, HitResult.Miss, HitResult.Great, 0)] // (0 * 4 * 300) * (1 + 0 / 25)
|
||||
[TestCase(ScoringMode.Classic, HitResult.Meh, HitResult.Great, 156)] // (((3 * 50) / (4 * 300)) * 4 * 300) * (1 + 1 / 25)
|
||||
[TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 312)] // (((3 * 100) / (4 * 300)) * 4 * 300) * (1 + 1 / 25)
|
||||
[TestCase(ScoringMode.Classic, HitResult.Good, HitResult.Perfect, 535)] // (((3 * 200) / (4 * 350)) * 4 * 300) * (1 + 1 / 25)
|
||||
[TestCase(ScoringMode.Classic, HitResult.Good, HitResult.Perfect, 594)] // (((3 * 200) / (4 * 350)) * 4 * 300) * (1 + 1 / 25)
|
||||
[TestCase(ScoringMode.Classic, HitResult.Great, HitResult.Great, 936)] // (((3 * 300) / (4 * 300)) * 4 * 300) * (1 + 1 / 25)
|
||||
[TestCase(ScoringMode.Classic, HitResult.Perfect, HitResult.Perfect, 936)] // (((3 * 350) / (4 * 350)) * 4 * 300) * (1 + 1 / 25)
|
||||
[TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, HitResult.SmallTickHit, 0)] // (0 * 1 * 300) * (1 + 0 / 25)
|
||||
[TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 225)] // (((3 * 10) / (4 * 10)) * 1 * 300) * (1 + 0 / 25)
|
||||
[TestCase(ScoringMode.Classic, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)] // (0 * 4 * 300) * (1 + 0 / 25)
|
||||
[TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 936)] // (((3 * 50) / (4 * 50)) * 4 * 300) * (1 + 1 / 25)
|
||||
[TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 30)] // (0 * 1 * 300) * (1 + 0 / 25) + 3 * 10 (bonus points)
|
||||
[TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 150)] // (0 * 1 * 300) * (1 + 0 / 25) * 3 * 50 (bonus points)
|
||||
// TODO: The following two cases don't match expectations currently (a single hit is registered in acc portion when it shouldn't be). See https://github.com/ppy/osu/issues/12604.
|
||||
[TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 330)] // (1 * 1 * 300) * (1 + 0 / 25) + 3 * 10 (bonus points)
|
||||
[TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 450)] // (1 * 1 * 300) * (1 + 0 / 25) + 3 * 50 (bonus points)
|
||||
public void TestFourVariousResultsOneMiss(ScoringMode scoringMode, HitResult hitResult, HitResult maxResult, int expectedScore)
|
||||
{
|
||||
var minResult = new TestJudgement(hitResult).MinResult;
|
||||
@ -118,7 +118,7 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
||||
scoreProcessor.ApplyResult(judgementResult);
|
||||
}
|
||||
|
||||
Assert.IsTrue(Precision.AlmostEquals(expectedScore, scoreProcessor.TotalScore.Value, 0.5));
|
||||
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(expectedScore).Within(0.5d));
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
@ -158,7 +158,7 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
||||
};
|
||||
scoreProcessor.ApplyResult(lastJudgementResult);
|
||||
|
||||
Assert.IsTrue(Precision.AlmostEquals(expectedScore, scoreProcessor.TotalScore.Value, 0.5));
|
||||
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(expectedScore).Within(0.5d));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -169,7 +169,7 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
||||
scoreProcessor.Mode.Value = scoringMode;
|
||||
scoreProcessor.ApplyBeatmap(new TestBeatmap(new RulesetInfo()));
|
||||
|
||||
Assert.IsTrue(Precision.AlmostEquals(0, scoreProcessor.TotalScore.Value));
|
||||
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
|
||||
}
|
||||
|
||||
[TestCase(HitResult.IgnoreHit, HitResult.IgnoreMiss)]
|
||||
@ -287,6 +287,23 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
||||
Assert.AreEqual(expectedReturnValue, hitResult.IsScorable());
|
||||
}
|
||||
|
||||
[TestCase(HitResult.Perfect, 1_000_000)]
|
||||
[TestCase(HitResult.SmallTickHit, 1_000_000)]
|
||||
[TestCase(HitResult.LargeTickHit, 1_000_000)]
|
||||
[TestCase(HitResult.SmallBonus, 1_000_000 + Judgement.SMALL_BONUS_SCORE)]
|
||||
[TestCase(HitResult.LargeBonus, 1_000_000 + Judgement.LARGE_BONUS_SCORE)]
|
||||
public void TestGetScoreWithExternalStatistics(HitResult result, int expectedScore)
|
||||
{
|
||||
var statistic = new Dictionary<HitResult, int> { { result, 1 } };
|
||||
|
||||
scoreProcessor.ApplyBeatmap(new Beatmap
|
||||
{
|
||||
HitObjects = { new TestHitObject(result) }
|
||||
});
|
||||
|
||||
Assert.That(scoreProcessor.GetImmediateScore(ScoringMode.Standardised, result.AffectsCombo() ? 1 : 0, statistic), Is.EqualTo(expectedScore).Within(0.5d));
|
||||
}
|
||||
|
||||
private class TestJudgement : Judgement
|
||||
{
|
||||
public override HitResult MaxResult { get; }
|
||||
|
@ -9,7 +9,6 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -106,9 +105,9 @@ namespace osu.Game.Tests.Rulesets
|
||||
IsDisposed = true;
|
||||
}
|
||||
|
||||
public SampleChannel Get(string name) => null;
|
||||
public Sample Get(string name) => null;
|
||||
|
||||
public Task<SampleChannel> GetAsync(string name) => null;
|
||||
public Task<Sample> GetAsync(string name) => null;
|
||||
|
||||
public Stream GetStream(string name) => null;
|
||||
|
||||
@ -119,9 +118,13 @@ namespace osu.Game.Tests.Rulesets
|
||||
public BindableNumber<double> Frequency => throw new NotImplementedException();
|
||||
public BindableNumber<double> Tempo => throw new NotImplementedException();
|
||||
|
||||
public void AddAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable) => throw new NotImplementedException();
|
||||
public void BindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException();
|
||||
|
||||
public void RemoveAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable) => throw new NotImplementedException();
|
||||
public void UnbindAdjustments(IAggregateAudioAdjustment component) => throw new NotImplementedException();
|
||||
|
||||
public void AddAdjustment(AdjustableProperty type, IBindable<double> adjustBindable) => throw new NotImplementedException();
|
||||
|
||||
public void RemoveAdjustment(AdjustableProperty type, IBindable<double> adjustBindable) => throw new NotImplementedException();
|
||||
|
||||
public void RemoveAllAdjustments(AdjustableProperty type) => throw new NotImplementedException();
|
||||
|
||||
|
@ -113,6 +113,31 @@ namespace osu.Game.Tests.Skins.IO
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestImportUpperCasedOskArchive()
|
||||
{
|
||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(ImportSkinTest)))
|
||||
{
|
||||
try
|
||||
{
|
||||
var osu = LoadOsuIntoHost(host);
|
||||
|
||||
var imported = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "skin1.OsK"));
|
||||
|
||||
Assert.That(imported.Name, Is.EqualTo("name 1"));
|
||||
Assert.That(imported.Creator, Is.EqualTo("author 1"));
|
||||
|
||||
var imported2 = await loadSkinIntoOsu(osu, new ZipArchiveReader(createOsk("name 1", "author 1"), "skin1.oSK"));
|
||||
|
||||
Assert.That(imported2.Hash, Is.EqualTo(imported.Hash));
|
||||
}
|
||||
finally
|
||||
{
|
||||
host.Exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MemoryStream createOsk(string name, string author)
|
||||
{
|
||||
var zipStream = new MemoryStream();
|
||||
|
@ -91,6 +91,15 @@ namespace osu.Game.Tests.Skins
|
||||
Assert.AreEqual(2.0m, decoder.Decode(stream).LegacyVersion);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStripWhitespace()
|
||||
{
|
||||
var decoder = new LegacySkinDecoder();
|
||||
using (var resStream = TestResources.OpenResource("skin-with-space.ini"))
|
||||
using (var stream = new LineBufferedReader(resStream))
|
||||
Assert.AreEqual(2.0m, decoder.Decode(stream).LegacyVersion);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDecodeLatestVersion()
|
||||
{
|
||||
|
@ -219,7 +219,7 @@ namespace osu.Game.Tests.Skins
|
||||
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => skin.GetTexture(componentName, wrapModeS, wrapModeT);
|
||||
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo);
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo);
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => skin.GetConfig<TLookup, TValue>(lookup);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Configuration.Tracking;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.IO.Stores;
|
||||
|
@ -58,7 +58,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
// reset API response in statics to avoid test crosstalk.
|
||||
statics.Set<APISeasonalBackgrounds>(Static.SeasonalBackgrounds, null);
|
||||
statics.SetValue<APISeasonalBackgrounds>(Static.SeasonalBackgrounds, null);
|
||||
textureStore.PerformedLookups.Clear();
|
||||
dummyAPI.SetState(APIState.Online);
|
||||
|
||||
@ -135,18 +135,20 @@ namespace osu.Game.Tests.Visual.Background
|
||||
dummyAPI.HandleRequest = request =>
|
||||
{
|
||||
if (dummyAPI.State.Value != APIState.Online || !(request is GetSeasonalBackgroundsRequest backgroundsRequest))
|
||||
return;
|
||||
return false;
|
||||
|
||||
backgroundsRequest.TriggerSuccess(new APISeasonalBackgrounds
|
||||
{
|
||||
Backgrounds = seasonal_background_urls.Select(url => new APISeasonalBackground { Url = url }).ToList(),
|
||||
EndDate = endDate
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
});
|
||||
|
||||
private void setSeasonalBackgroundMode(SeasonalBackgroundMode mode)
|
||||
=> AddStep($"set seasonal mode to {mode}", () => config.Set(OsuSetting.SeasonalBackgroundMode, mode));
|
||||
=> AddStep($"set seasonal mode to {mode}", () => config.SetValue(OsuSetting.SeasonalBackgroundMode, mode));
|
||||
|
||||
private void createLoader()
|
||||
=> AddStep("create loader", () =>
|
||||
|
@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
|
||||
Dependencies.Cache(new OsuConfigManager(LocalStorage));
|
||||
|
||||
manager.Import(TestResources.GetTestBeatmapForImport()).Wait();
|
||||
manager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
|
||||
|
||||
Beatmap.SetDefault();
|
||||
}
|
||||
@ -65,6 +65,21 @@ namespace osu.Game.Tests.Visual.Background
|
||||
stack.Push(songSelect = new DummySongSelect());
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// User settings should always be ignored on song select screen.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestUserSettingsIgnoredOnSongSelect()
|
||||
{
|
||||
setupUserSettings();
|
||||
AddUntilStep("Screen is undimmed", () => songSelect.IsBackgroundUndimmed());
|
||||
AddUntilStep("Screen using background blur", () => songSelect.IsBackgroundBlur());
|
||||
performFullSetup();
|
||||
AddStep("Exit to song select", () => player.Exit());
|
||||
AddUntilStep("Screen is undimmed", () => songSelect.IsBackgroundUndimmed());
|
||||
AddUntilStep("Screen using background blur", () => songSelect.IsBackgroundBlur());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if <see cref="PlayerLoader"/> properly triggers the visual settings preview when a user hovers over the visual settings panel.
|
||||
/// </summary>
|
||||
@ -82,7 +97,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
});
|
||||
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
AddStep("Stop background preview", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
|
||||
AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect());
|
||||
AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.CheckBackgroundBlur(playerLoader.ExpectedBackgroundBlur));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -106,6 +121,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
public void TestStoryboardBackgroundVisibility()
|
||||
{
|
||||
performFullSetup();
|
||||
AddAssert("Background retained from song select", () => songSelect.IsBackgroundCurrent());
|
||||
createFakeStoryboard();
|
||||
AddStep("Enable Storyboard", () =>
|
||||
{
|
||||
@ -141,9 +157,9 @@ namespace osu.Game.Tests.Visual.Background
|
||||
{
|
||||
performFullSetup();
|
||||
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
AddStep("Enable user dim", () => songSelect.DimEnabled.Value = false);
|
||||
AddStep("Disable user dim", () => songSelect.IgnoreUserSettings.Value = true);
|
||||
AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsUserBlurDisabled());
|
||||
AddStep("Disable user dim", () => songSelect.DimEnabled.Value = true);
|
||||
AddStep("Enable user dim", () => songSelect.IgnoreUserSettings.Value = false);
|
||||
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
}
|
||||
|
||||
@ -160,13 +176,36 @@ namespace osu.Game.Tests.Visual.Background
|
||||
player.ReplacesBackground.Value = true;
|
||||
player.StoryboardEnabled.Value = true;
|
||||
});
|
||||
AddStep("Enable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = true);
|
||||
AddStep("Enable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = false);
|
||||
AddStep("Set dim level to 1", () => songSelect.DimLevel.Value = 1f);
|
||||
AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible);
|
||||
AddStep("Disable user dim", () => player.DimmableStoryboard.EnableUserDim.Value = false);
|
||||
AddStep("Disable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = true);
|
||||
AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStoryboardIgnoreUserSettings()
|
||||
{
|
||||
performFullSetup();
|
||||
createFakeStoryboard();
|
||||
AddStep("Enable replacing background", () => player.ReplacesBackground.Value = true);
|
||||
|
||||
AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible);
|
||||
AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible());
|
||||
|
||||
AddStep("Ignore user settings", () =>
|
||||
{
|
||||
player.ApplyToBackground(b => b.IgnoreUserSettings.Value = true);
|
||||
player.DimmableStoryboard.IgnoreUserSettings.Value = true;
|
||||
});
|
||||
AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible);
|
||||
AddUntilStep("Background is invisible", () => songSelect.IsBackgroundInvisible());
|
||||
|
||||
AddStep("Disable background replacement", () => player.ReplacesBackground.Value = false);
|
||||
AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible);
|
||||
AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the visual settings container retains dim and blur when pausing
|
||||
/// </summary>
|
||||
@ -198,19 +237,9 @@ namespace osu.Game.Tests.Visual.Background
|
||||
})));
|
||||
|
||||
AddUntilStep("Wait for results is current", () => results.IsCurrentScreen());
|
||||
AddUntilStep("Screen is undimmed, original background retained", () =>
|
||||
songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && results.IsBlurCorrect());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if background gets undimmed and unblurred when leaving <see cref="Player"/> for <see cref="PlaySongSelect"/>
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestTransitionOut()
|
||||
{
|
||||
performFullSetup();
|
||||
AddStep("Exit to song select", () => player.Exit());
|
||||
AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.IsBlurCorrect());
|
||||
AddUntilStep("Screen is undimmed, original background retained", () =>
|
||||
songSelect.IsBackgroundUndimmed() && songSelect.IsBackgroundCurrent() && songSelect.CheckBackgroundBlur(results.ExpectedBackgroundBlur));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -224,7 +253,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
AddStep("Resume PlayerLoader", () => player.Restart());
|
||||
AddUntilStep("Screen is dimmed and blur applied", () => songSelect.IsBackgroundDimmed() && songSelect.IsUserBlurApplied());
|
||||
AddStep("Move mouse to center of screen", () => InputManager.MoveMouseTo(playerLoader.ScreenPos));
|
||||
AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && playerLoader.IsBlurCorrect());
|
||||
AddUntilStep("Screen is undimmed and user blur removed", () => songSelect.IsBackgroundUndimmed() && songSelect.CheckBackgroundBlur(playerLoader.ExpectedBackgroundBlur));
|
||||
}
|
||||
|
||||
private void createFakeStoryboard() => AddStep("Create storyboard", () =>
|
||||
@ -274,14 +303,16 @@ namespace osu.Game.Tests.Visual.Background
|
||||
|
||||
private class DummySongSelect : PlaySongSelect
|
||||
{
|
||||
private FadeAccessibleBackground background;
|
||||
|
||||
protected override BackgroundScreen CreateBackground()
|
||||
{
|
||||
FadeAccessibleBackground background = new FadeAccessibleBackground(Beatmap.Value);
|
||||
DimEnabled.BindTo(background.EnableUserDim);
|
||||
background = new FadeAccessibleBackground(Beatmap.Value);
|
||||
IgnoreUserSettings.BindTo(background.IgnoreUserSettings);
|
||||
return background;
|
||||
}
|
||||
|
||||
public readonly Bindable<bool> DimEnabled = new Bindable<bool>();
|
||||
public readonly Bindable<bool> IgnoreUserSettings = new Bindable<bool>();
|
||||
public readonly Bindable<double> DimLevel = new BindableDouble();
|
||||
public readonly Bindable<double> BlurLevel = new BindableDouble();
|
||||
|
||||
@ -294,25 +325,27 @@ namespace osu.Game.Tests.Visual.Background
|
||||
config.BindWith(OsuSetting.BlurLevel, BlurLevel);
|
||||
}
|
||||
|
||||
public bool IsBackgroundDimmed() => ((FadeAccessibleBackground)Background).CurrentColour == OsuColour.Gray(1f - ((FadeAccessibleBackground)Background).CurrentDim);
|
||||
public bool IsBackgroundDimmed() => background.CurrentColour == OsuColour.Gray(1f - background.CurrentDim);
|
||||
|
||||
public bool IsBackgroundUndimmed() => ((FadeAccessibleBackground)Background).CurrentColour == Color4.White;
|
||||
public bool IsBackgroundUndimmed() => background.CurrentColour == Color4.White;
|
||||
|
||||
public bool IsUserBlurApplied() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR);
|
||||
public bool IsUserBlurApplied() => background.CurrentBlur == new Vector2((float)BlurLevel.Value * BackgroundScreenBeatmap.USER_BLUR_FACTOR);
|
||||
|
||||
public bool IsUserBlurDisabled() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(0);
|
||||
public bool IsUserBlurDisabled() => background.CurrentBlur == new Vector2(0);
|
||||
|
||||
public bool IsBackgroundInvisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 0;
|
||||
public bool IsBackgroundInvisible() => background.CurrentAlpha == 0;
|
||||
|
||||
public bool IsBackgroundVisible() => ((FadeAccessibleBackground)Background).CurrentAlpha == 1;
|
||||
public bool IsBackgroundVisible() => background.CurrentAlpha == 1;
|
||||
|
||||
public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
|
||||
public bool IsBackgroundBlur() => background.CurrentBlur == new Vector2(BACKGROUND_BLUR);
|
||||
|
||||
public bool CheckBackgroundBlur(Vector2 expected) => background.CurrentBlur == expected;
|
||||
|
||||
/// <summary>
|
||||
/// Make sure every time a screen gets pushed, the background doesn't get replaced
|
||||
/// </summary>
|
||||
/// <returns>Whether or not the original background (The one created in DummySongSelect) is still the current background</returns>
|
||||
public bool IsBackgroundCurrent() => ((FadeAccessibleBackground)Background).IsCurrentScreen();
|
||||
public bool IsBackgroundCurrent() => background?.IsCurrentScreen() == true;
|
||||
}
|
||||
|
||||
private class FadeAccessibleResults : ResultsScreen
|
||||
@ -324,12 +357,20 @@ namespace osu.Game.Tests.Visual.Background
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
|
||||
|
||||
public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
|
||||
public Vector2 ExpectedBackgroundBlur => new Vector2(BACKGROUND_BLUR);
|
||||
}
|
||||
|
||||
private class LoadBlockingTestPlayer : TestPlayer
|
||||
{
|
||||
protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
|
||||
protected override BackgroundScreen CreateBackground() =>
|
||||
new FadeAccessibleBackground(Beatmap.Value);
|
||||
|
||||
public override void OnEntering(IScreen last)
|
||||
{
|
||||
base.OnEntering(last);
|
||||
|
||||
ApplyToBackground(b => ReplacesBackground.BindTo(b.StoryboardReplacesBackground));
|
||||
}
|
||||
|
||||
public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard;
|
||||
|
||||
@ -354,15 +395,16 @@ namespace osu.Game.Tests.Visual.Background
|
||||
Thread.Sleep(1);
|
||||
|
||||
StoryboardEnabled = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
|
||||
ReplacesBackground.BindTo(Background.StoryboardReplacesBackground);
|
||||
DrawableRuleset.IsPaused.BindTo(IsPaused);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestPlayerLoader : PlayerLoader
|
||||
{
|
||||
private FadeAccessibleBackground background;
|
||||
|
||||
public VisualSettings VisualSettingsPos => VisualSettings;
|
||||
public BackgroundScreen ScreenPos => Background;
|
||||
public BackgroundScreen ScreenPos => background;
|
||||
|
||||
public TestPlayerLoader(Player player)
|
||||
: base(() => player)
|
||||
@ -371,9 +413,9 @@ namespace osu.Game.Tests.Visual.Background
|
||||
|
||||
public void TriggerOnHover() => OnHover(new HoverEvent(new InputState()));
|
||||
|
||||
public bool IsBlurCorrect() => ((FadeAccessibleBackground)Background).CurrentBlur == new Vector2(BACKGROUND_BLUR);
|
||||
public Vector2 ExpectedBackgroundBlur => new Vector2(BACKGROUND_BLUR);
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => new FadeAccessibleBackground(Beatmap.Value);
|
||||
protected override BackgroundScreen CreateBackground() => background = new FadeAccessibleBackground(Beatmap.Value);
|
||||
}
|
||||
|
||||
private class FadeAccessibleBackground : BackgroundScreenBeatmap
|
||||
|
@ -38,13 +38,13 @@ namespace osu.Game.Tests.Visual.Collections
|
||||
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
||||
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, host, Beatmap.Default));
|
||||
|
||||
beatmapManager.Import(TestResources.GetTestBeatmapForImport()).Wait();
|
||||
beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
|
||||
|
||||
base.Content.AddRange(new Drawable[]
|
||||
{
|
||||
manager = new CollectionManager(LocalStorage),
|
||||
Content,
|
||||
dialogOverlay = new DialogOverlay()
|
||||
dialogOverlay = new DialogOverlay(),
|
||||
});
|
||||
|
||||
Dependencies.Cache(manager);
|
||||
@ -134,6 +134,27 @@ namespace osu.Game.Tests.Visual.Collections
|
||||
assertCollectionName(0, "2");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCollectionNameCollisions()
|
||||
{
|
||||
AddStep("add dropdown", () =>
|
||||
{
|
||||
Add(new CollectionFilterDropdown
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Width = 0.4f,
|
||||
}
|
||||
);
|
||||
});
|
||||
AddStep("add two collections with same name", () => manager.Collections.AddRange(new[]
|
||||
{
|
||||
new BeatmapCollection { Name = { Value = "1" } },
|
||||
new BeatmapCollection { Name = { Value = "1" }, Beatmaps = { beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0] } },
|
||||
}));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRemoveCollectionViaButton()
|
||||
{
|
||||
|
@ -81,6 +81,13 @@ namespace osu.Game.Tests.Visual.Components
|
||||
[Test]
|
||||
public void TestMovement()
|
||||
{
|
||||
checkIdleStatus(1, false);
|
||||
checkIdleStatus(2, false);
|
||||
checkIdleStatus(3, false);
|
||||
checkIdleStatus(4, false);
|
||||
|
||||
waitForAllIdle();
|
||||
|
||||
AddStep("move to top right", () => InputManager.MoveMouseTo(box2));
|
||||
|
||||
checkIdleStatus(1, true);
|
||||
@ -102,6 +109,8 @@ namespace osu.Game.Tests.Visual.Components
|
||||
[Test]
|
||||
public void TestTimings()
|
||||
{
|
||||
waitForAllIdle();
|
||||
|
||||
AddStep("move to centre", () => InputManager.MoveMouseTo(Content));
|
||||
|
||||
checkIdleStatus(1, false);
|
||||
@ -140,7 +149,7 @@ namespace osu.Game.Tests.Visual.Components
|
||||
|
||||
private void waitForAllIdle()
|
||||
{
|
||||
AddUntilStep("Wait for all idle", () => box1.IsIdle && box2.IsIdle && box3.IsIdle && box4.IsIdle);
|
||||
AddUntilStep("wait for all idle", () => box1.IsIdle && box2.IsIdle && box3.IsIdle && box4.IsIdle);
|
||||
}
|
||||
|
||||
private class IdleTrackingBox : CompositeDrawable
|
||||
@ -149,7 +158,7 @@ namespace osu.Game.Tests.Visual.Components
|
||||
|
||||
public bool IsIdle => idleTracker.IsIdle.Value;
|
||||
|
||||
public IdleTrackingBox(double timeToIdle)
|
||||
public IdleTrackingBox(int timeToIdle)
|
||||
{
|
||||
Box box;
|
||||
|
||||
@ -158,7 +167,7 @@ namespace osu.Game.Tests.Visual.Components
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
idleTracker = new IdleTracker(timeToIdle),
|
||||
idleTracker = new GameIdleTracker(timeToIdle),
|
||||
box = new Box
|
||||
{
|
||||
Colour = Color4.White,
|
||||
|
@ -61,12 +61,12 @@ namespace osu.Game.Tests.Visual.Components
|
||||
{
|
||||
createPoller(true);
|
||||
|
||||
AddStep("set poll interval to 1", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust);
|
||||
AddStep("set poll interval to 1", () => poller.TimeBetweenPolls.Value = TimePerAction * safety_adjust);
|
||||
checkCount(1);
|
||||
checkCount(2);
|
||||
checkCount(3);
|
||||
|
||||
AddStep("set poll interval to 5", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust * 5);
|
||||
AddStep("set poll interval to 5", () => poller.TimeBetweenPolls.Value = TimePerAction * safety_adjust * 5);
|
||||
checkCount(4);
|
||||
checkCount(4);
|
||||
checkCount(4);
|
||||
@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Components
|
||||
checkCount(5);
|
||||
checkCount(5);
|
||||
|
||||
AddStep("set poll interval to 1", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust);
|
||||
AddStep("set poll interval to 1", () => poller.TimeBetweenPolls.Value = TimePerAction * safety_adjust);
|
||||
checkCount(6);
|
||||
checkCount(7);
|
||||
}
|
||||
@ -87,7 +87,7 @@ namespace osu.Game.Tests.Visual.Components
|
||||
{
|
||||
createPoller(false);
|
||||
|
||||
AddStep("set poll interval to 1", () => poller.TimeBetweenPolls = TimePerAction * safety_adjust * 5);
|
||||
AddStep("set poll interval to 1", () => poller.TimeBetweenPolls.Value = TimePerAction * safety_adjust * 5);
|
||||
checkCount(0);
|
||||
skip();
|
||||
checkCount(0);
|
||||
@ -141,7 +141,7 @@ namespace osu.Game.Tests.Visual.Components
|
||||
|
||||
public class TestSlowPoller : TestPoller
|
||||
{
|
||||
protected override Task Poll() => Task.Delay((int)(TimeBetweenPolls / 2f / Clock.Rate)).ContinueWith(_ => base.Poll());
|
||||
protected override Task Poll() => Task.Delay((int)(TimeBetweenPolls.Value / 2f / Clock.Rate)).ContinueWith(_ => base.Poll());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using static osu.Game.Tests.Visual.Components.TestScenePreviewTrackManager.TestPreviewTrackManager;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Components
|
||||
{
|
||||
@ -100,7 +99,7 @@ namespace osu.Game.Tests.Visual.Components
|
||||
[Test]
|
||||
public void TestNonPresentTrack()
|
||||
{
|
||||
TestPreviewTrack track = null;
|
||||
TestPreviewTrackManager.TestPreviewTrack track = null;
|
||||
|
||||
AddStep("get non-present track", () =>
|
||||
{
|
||||
@ -182,9 +181,9 @@ namespace osu.Game.Tests.Visual.Components
|
||||
AddAssert("track stopped", () => !track.IsRunning);
|
||||
}
|
||||
|
||||
private TestPreviewTrack getTrack() => (TestPreviewTrack)trackManager.Get(null);
|
||||
private TestPreviewTrackManager.TestPreviewTrack getTrack() => (TestPreviewTrackManager.TestPreviewTrack)trackManager.Get(null);
|
||||
|
||||
private TestPreviewTrack getOwnedTrack()
|
||||
private TestPreviewTrackManager.TestPreviewTrack getOwnedTrack()
|
||||
{
|
||||
var track = getTrack();
|
||||
|
||||
|
70
osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs
Normal file
70
osu.Game.Tests/Visual/Editing/TestSceneBlueprintSelection.cs
Normal file
@ -0,0 +1,70 @@
|
||||
// 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
public class TestSceneBlueprintSelection : EditorTestScene
|
||||
{
|
||||
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
|
||||
|
||||
private EditorBlueprintContainer blueprintContainer
|
||||
=> Editor.ChildrenOfType<EditorBlueprintContainer>().First();
|
||||
|
||||
[Test]
|
||||
public void TestSelectedObjectHasPriorityWhenOverlapping()
|
||||
{
|
||||
var firstSlider = new Slider
|
||||
{
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(new Vector2()),
|
||||
new PathControlPoint(new Vector2(150, -50)),
|
||||
new PathControlPoint(new Vector2(300, 0))
|
||||
}),
|
||||
Position = new Vector2(0, 100)
|
||||
};
|
||||
var secondSlider = new Slider
|
||||
{
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(new Vector2()),
|
||||
new PathControlPoint(new Vector2(-50, 50)),
|
||||
new PathControlPoint(new Vector2(-100, 100))
|
||||
}),
|
||||
Position = new Vector2(200, 0)
|
||||
};
|
||||
|
||||
AddStep("add overlapping sliders", () =>
|
||||
{
|
||||
EditorBeatmap.Add(firstSlider);
|
||||
EditorBeatmap.Add(secondSlider);
|
||||
});
|
||||
AddStep("select first slider", () => EditorBeatmap.SelectedHitObjects.Add(firstSlider));
|
||||
|
||||
AddStep("move mouse to common point", () =>
|
||||
{
|
||||
var pos = blueprintContainer.ChildrenOfType<PathControlPointPiece>().ElementAt(1).ScreenSpaceDrawQuad.Centre;
|
||||
InputManager.MoveMouseTo(pos);
|
||||
});
|
||||
AddStep("right click", () => InputManager.Click(MouseButton.Right));
|
||||
|
||||
AddAssert("selection is unchanged", () => EditorBeatmap.SelectedHitObjects.Single() == firstSlider);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,45 +1,50 @@
|
||||
// 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
public class TestSceneComposeSelectBox : OsuTestScene
|
||||
public class TestSceneComposeSelectBox : OsuManualInputManagerTestScene
|
||||
{
|
||||
private Container selectionArea;
|
||||
private SelectionBox selectionBox;
|
||||
|
||||
public TestSceneComposeSelectBox()
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
SelectionBox selectionBox = null;
|
||||
|
||||
AddStep("create box", () =>
|
||||
Child = selectionArea = new Container
|
||||
Child = selectionArea = new Container
|
||||
{
|
||||
Size = new Vector2(400),
|
||||
Position = -new Vector2(150),
|
||||
Anchor = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Size = new Vector2(400),
|
||||
Position = -new Vector2(150),
|
||||
Anchor = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
selectionBox = new SelectionBox
|
||||
{
|
||||
selectionBox = new SelectionBox
|
||||
{
|
||||
CanRotate = true,
|
||||
CanScaleX = true,
|
||||
CanScaleY = true,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
|
||||
OnRotation = handleRotation,
|
||||
OnScale = handleScale
|
||||
}
|
||||
CanRotate = true,
|
||||
CanScaleX = true,
|
||||
CanScaleY = true,
|
||||
|
||||
OnRotation = handleRotation,
|
||||
OnScale = handleScale
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
AddToggleStep("toggle rotation", state => selectionBox.CanRotate = state);
|
||||
AddToggleStep("toggle x", state => selectionBox.CanScaleX = state);
|
||||
AddToggleStep("toggle y", state => selectionBox.CanScaleY = state);
|
||||
}
|
||||
InputManager.MoveMouseTo(selectionBox);
|
||||
InputManager.ReleaseButton(MouseButton.Left);
|
||||
});
|
||||
|
||||
private bool handleScale(Vector2 amount, Anchor reference)
|
||||
{
|
||||
@ -68,5 +73,115 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
selectionArea.Rotation += angle;
|
||||
return true;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRotationHandleShownOnHover()
|
||||
{
|
||||
SelectionBoxRotationHandle rotationHandle = null;
|
||||
|
||||
AddStep("retrieve rotation handle", () => rotationHandle = this.ChildrenOfType<SelectionBoxRotationHandle>().First());
|
||||
|
||||
AddAssert("handle hidden", () => rotationHandle.Alpha == 0);
|
||||
AddStep("hover over handle", () => InputManager.MoveMouseTo(rotationHandle));
|
||||
AddUntilStep("rotation handle shown", () => rotationHandle.Alpha == 1);
|
||||
|
||||
AddStep("move mouse away", () => InputManager.MoveMouseTo(selectionBox));
|
||||
AddUntilStep("handle hidden", () => rotationHandle.Alpha == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRotationHandleShownOnHoveringClosestScaleHandler()
|
||||
{
|
||||
SelectionBoxRotationHandle rotationHandle = null;
|
||||
|
||||
AddStep("retrieve rotation handle", () => rotationHandle = this.ChildrenOfType<SelectionBoxRotationHandle>().First());
|
||||
|
||||
AddAssert("rotation handle hidden", () => rotationHandle.Alpha == 0);
|
||||
AddStep("hover over closest scale handle", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<SelectionBoxScaleHandle>().Single(s => s.Anchor == rotationHandle.Anchor));
|
||||
});
|
||||
AddUntilStep("rotation handle shown", () => rotationHandle.Alpha == 1);
|
||||
|
||||
AddStep("move mouse away", () => InputManager.MoveMouseTo(selectionBox));
|
||||
AddUntilStep("handle hidden", () => rotationHandle.Alpha == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHoverRotationHandleFromScaleHandle()
|
||||
{
|
||||
SelectionBoxRotationHandle rotationHandle = null;
|
||||
|
||||
AddStep("retrieve rotation handle", () => rotationHandle = this.ChildrenOfType<SelectionBoxRotationHandle>().First());
|
||||
|
||||
AddAssert("rotation handle hidden", () => rotationHandle.Alpha == 0);
|
||||
AddStep("hover over closest scale handle", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<SelectionBoxScaleHandle>().Single(s => s.Anchor == rotationHandle.Anchor));
|
||||
});
|
||||
AddUntilStep("rotation handle shown", () => rotationHandle.Alpha == 1);
|
||||
AddAssert("rotation handle not hovered", () => !rotationHandle.IsHovered);
|
||||
|
||||
AddStep("hover over rotation handle", () => InputManager.MoveMouseTo(rotationHandle));
|
||||
AddAssert("rotation handle still shown", () => rotationHandle.Alpha == 1);
|
||||
AddAssert("rotation handle hovered", () => rotationHandle.IsHovered);
|
||||
|
||||
AddStep("move mouse away", () => InputManager.MoveMouseTo(selectionBox));
|
||||
AddUntilStep("handle hidden", () => rotationHandle.Alpha == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHoldingScaleHandleHidesCorrespondingRotationHandle()
|
||||
{
|
||||
SelectionBoxRotationHandle rotationHandle = null;
|
||||
|
||||
AddStep("retrieve rotation handle", () => rotationHandle = this.ChildrenOfType<SelectionBoxRotationHandle>().First());
|
||||
|
||||
AddAssert("rotation handle hidden", () => rotationHandle.Alpha == 0);
|
||||
AddStep("hover over closest scale handle", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<SelectionBoxScaleHandle>().Single(s => s.Anchor == rotationHandle.Anchor));
|
||||
});
|
||||
AddUntilStep("rotation handle shown", () => rotationHandle.Alpha == 1);
|
||||
AddStep("hold scale handle", () => InputManager.PressButton(MouseButton.Left));
|
||||
AddUntilStep("rotation handle hidden", () => rotationHandle.Alpha == 0);
|
||||
|
||||
int i;
|
||||
ScheduledDelegate mouseMove = null;
|
||||
|
||||
AddStep("start dragging", () =>
|
||||
{
|
||||
i = 0;
|
||||
|
||||
mouseMove = Scheduler.AddDelayed(() =>
|
||||
{
|
||||
InputManager.MoveMouseTo(selectionBox.ScreenSpaceDrawQuad.TopLeft + Vector2.One * (5 * ++i));
|
||||
}, 100, true);
|
||||
});
|
||||
AddAssert("rotation handle still hidden", () => rotationHandle.Alpha == 0);
|
||||
|
||||
AddStep("end dragging", () => mouseMove.Cancel());
|
||||
AddAssert("rotation handle still hidden", () => rotationHandle.Alpha == 0);
|
||||
AddStep("unhold left", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
AddUntilStep("rotation handle shown", () => rotationHandle.Alpha == 1);
|
||||
AddStep("move mouse away", () => InputManager.MoveMouseTo(selectionBox, new Vector2(20)));
|
||||
AddUntilStep("rotation handle hidden", () => rotationHandle.Alpha == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that hovering over two handles instantaneously from one to another does not crash or cause issues to the visibility state.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestHoverOverTwoHandlesInstantaneously()
|
||||
{
|
||||
AddStep("hover over top-left scale handle", () =>
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<SelectionBoxScaleHandle>().Single(s => s.Anchor == Anchor.TopLeft)));
|
||||
AddStep("hover over top-right scale handle", () =>
|
||||
InputManager.MoveMouseTo(this.ChildrenOfType<SelectionBoxScaleHandle>().Single(s => s.Anchor == Anchor.TopRight)));
|
||||
AddUntilStep("top-left rotation handle hidden", () =>
|
||||
this.ChildrenOfType<SelectionBoxRotationHandle>().Single(r => r.Anchor == Anchor.TopLeft).Alpha == 0);
|
||||
AddUntilStep("top-right rotation handle shown", () =>
|
||||
this.ChildrenOfType<SelectionBoxRotationHandle>().Single(r => r.Anchor == Anchor.TopRight).Alpha == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,16 @@
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osuTK;
|
||||
|
||||
@ -110,8 +114,9 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddAssert("duration matches", () => ((Spinner)EditorBeatmap.HitObjects.Single()).Duration == 5000);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCopyPaste()
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void TestCopyPaste(bool deselectAfterCopy)
|
||||
{
|
||||
var addedObject = new HitCircle { StartTime = 1000 };
|
||||
|
||||
@ -123,11 +128,22 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
AddStep("move forward in time", () => EditorClock.Seek(2000));
|
||||
|
||||
if (deselectAfterCopy)
|
||||
{
|
||||
AddStep("deselect", () => EditorBeatmap.SelectedHitObjects.Clear());
|
||||
|
||||
AddUntilStep("timeline selection box is not visible", () => Editor.ChildrenOfType<Timeline>().First().ChildrenOfType<SelectionBox>().First().Alpha == 0);
|
||||
AddUntilStep("composer selection box is not visible", () => Editor.ChildrenOfType<HitObjectComposer>().First().ChildrenOfType<SelectionBox>().First().Alpha == 0);
|
||||
}
|
||||
|
||||
AddStep("paste hitobject", () => Editor.Paste());
|
||||
|
||||
AddAssert("are two objects", () => EditorBeatmap.HitObjects.Count == 2);
|
||||
|
||||
AddAssert("new object selected", () => EditorBeatmap.SelectedHitObjects.Single().StartTime == 2000);
|
||||
|
||||
AddUntilStep("timeline selection box is visible", () => Editor.ChildrenOfType<Timeline>().First().ChildrenOfType<EditorSelectionHandler>().First().Alpha > 0);
|
||||
AddUntilStep("composer selection box is visible", () => Editor.ChildrenOfType<HitObjectComposer>().First().ChildrenOfType<EditorSelectionHandler>().First().Alpha > 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
87
osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs
Normal file
87
osu.Game.Tests/Visual/Editing/TestSceneEditorClock.cs
Normal file
@ -0,0 +1,87 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Edit.Components;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneEditorClock : EditorClockTestScene
|
||||
{
|
||||
public TestSceneEditorClock()
|
||||
{
|
||||
Add(new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new TimeInfoContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200, 100)
|
||||
},
|
||||
new PlaybackControl
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(200, 100)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
// ensure that music controller does not change this beatmap due to it
|
||||
// completing naturally as part of the test.
|
||||
Beatmap.Disabled = true;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStopAtTrackEnd()
|
||||
{
|
||||
AddStep("reset clock", () => Clock.Seek(0));
|
||||
|
||||
AddStep("start clock", Clock.Start);
|
||||
AddAssert("clock running", () => Clock.IsRunning);
|
||||
|
||||
AddStep("seek near end", () => Clock.Seek(Clock.TrackLength - 250));
|
||||
AddUntilStep("clock stops", () => !Clock.IsRunning);
|
||||
|
||||
AddAssert("clock stopped at end", () => Clock.CurrentTime == Clock.TrackLength);
|
||||
|
||||
AddStep("start clock again", Clock.Start);
|
||||
AddAssert("clock looped to start", () => Clock.IsRunning && Clock.CurrentTime < 500);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWrapWhenStoppedAtTrackEnd()
|
||||
{
|
||||
AddStep("reset clock", () => Clock.Seek(0));
|
||||
|
||||
AddStep("stop clock", Clock.Stop);
|
||||
AddAssert("clock stopped", () => !Clock.IsRunning);
|
||||
|
||||
AddStep("seek exactly to end", () => Clock.Seek(Clock.TrackLength));
|
||||
AddAssert("clock stopped at end", () => Clock.CurrentTime == Clock.TrackLength);
|
||||
|
||||
AddStep("start clock again", Clock.Start);
|
||||
AddAssert("clock looped to start", () => Clock.IsRunning && Clock.CurrentTime < 500);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
Beatmap.Disabled = false;
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
// 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
public class TestSceneEditorQuickDelete : EditorTestScene
|
||||
{
|
||||
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
|
||||
|
||||
private BlueprintContainer blueprintContainer
|
||||
=> Editor.ChildrenOfType<BlueprintContainer>().First();
|
||||
|
||||
[Test]
|
||||
public void TestQuickDeleteRemovesObject()
|
||||
{
|
||||
var addedObject = new HitCircle { StartTime = 1000 };
|
||||
|
||||
AddStep("add hitobject", () => EditorBeatmap.Add(addedObject));
|
||||
|
||||
AddStep("select added object", () => EditorBeatmap.SelectedHitObjects.Add(addedObject));
|
||||
|
||||
AddStep("move mouse to object", () =>
|
||||
{
|
||||
var pos = blueprintContainer.ChildrenOfType<HitCirclePiece>().First().ScreenSpaceDrawQuad.Centre;
|
||||
InputManager.MoveMouseTo(pos);
|
||||
});
|
||||
AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft));
|
||||
AddStep("right click", () => InputManager.Click(MouseButton.Right));
|
||||
AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft));
|
||||
|
||||
AddAssert("no hitobjects in beatmap", () => EditorBeatmap.HitObjects.Count == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestQuickDeleteRemovesSliderControlPoint()
|
||||
{
|
||||
Slider slider = new Slider { StartTime = 1000 };
|
||||
|
||||
PathControlPoint[] points =
|
||||
{
|
||||
new PathControlPoint(),
|
||||
new PathControlPoint(new Vector2(50, 0)),
|
||||
new PathControlPoint(new Vector2(100, 0))
|
||||
};
|
||||
|
||||
AddStep("add slider", () =>
|
||||
{
|
||||
slider.Path = new SliderPath(points);
|
||||
EditorBeatmap.Add(slider);
|
||||
});
|
||||
|
||||
AddStep("select added slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||
|
||||
AddStep("move mouse to controlpoint", () =>
|
||||
{
|
||||
var pos = blueprintContainer.ChildrenOfType<PathControlPointPiece>().ElementAt(1).ScreenSpaceDrawQuad.Centre;
|
||||
InputManager.MoveMouseTo(pos);
|
||||
});
|
||||
AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft));
|
||||
|
||||
AddStep("right click", () => InputManager.Click(MouseButton.Right));
|
||||
AddAssert("slider has 2 points", () => slider.Path.ControlPoints.Count == 2);
|
||||
|
||||
// second click should nuke the object completely.
|
||||
AddStep("right click", () => InputManager.Click(MouseButton.Right));
|
||||
AddAssert("no hitobjects in beatmap", () => EditorBeatmap.HitObjects.Count == 0);
|
||||
|
||||
AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft));
|
||||
}
|
||||
}
|
||||
}
|
@ -3,11 +3,11 @@
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.Audio;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
@ -19,14 +19,14 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
public void TestSlidingSampleStopsOnSeek()
|
||||
{
|
||||
DrawableSlider slider = null;
|
||||
DrawableSample[] loopingSamples = null;
|
||||
DrawableSample[] onceOffSamples = null;
|
||||
PoolableSkinnableSample[] loopingSamples = null;
|
||||
PoolableSkinnableSample[] onceOffSamples = null;
|
||||
|
||||
AddStep("get first slider", () =>
|
||||
{
|
||||
slider = Editor.ChildrenOfType<DrawableSlider>().OrderBy(s => s.HitObject.StartTime).First();
|
||||
onceOffSamples = slider.ChildrenOfType<DrawableSample>().Where(s => !s.Looping).ToArray();
|
||||
loopingSamples = slider.ChildrenOfType<DrawableSample>().Where(s => s.Looping).ToArray();
|
||||
onceOffSamples = slider.ChildrenOfType<PoolableSkinnableSample>().Where(s => !s.Looping).ToArray();
|
||||
loopingSamples = slider.ChildrenOfType<PoolableSkinnableSample>().Where(s => s.Looping).ToArray();
|
||||
});
|
||||
|
||||
AddStep("start playback", () => EditorClock.Start());
|
||||
|
79
osu.Game.Tests/Visual/Editing/TestSceneEditorSeeking.cs
Normal file
79
osu.Game.Tests/Visual/Editing/TestSceneEditorSeeking.cs
Normal file
@ -0,0 +1,79 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
public class TestSceneEditorSeeking : EditorTestScene
|
||||
{
|
||||
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||
{
|
||||
var beatmap = base.CreateBeatmap(ruleset);
|
||||
|
||||
beatmap.BeatmapInfo.BeatDivisor = 1;
|
||||
|
||||
beatmap.ControlPointInfo = new ControlPointInfo();
|
||||
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 });
|
||||
beatmap.ControlPointInfo.Add(2000, new TimingControlPoint { BeatLength = 500 });
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSnappedSeeking()
|
||||
{
|
||||
AddStep("seek to 0", () => EditorClock.Seek(0));
|
||||
AddAssert("time is 0", () => EditorClock.CurrentTime == 0);
|
||||
|
||||
pressAndCheckTime(Key.Right, 1000);
|
||||
pressAndCheckTime(Key.Right, 2000);
|
||||
pressAndCheckTime(Key.Right, 2500);
|
||||
pressAndCheckTime(Key.Right, 3000);
|
||||
|
||||
pressAndCheckTime(Key.Left, 2500);
|
||||
pressAndCheckTime(Key.Left, 2000);
|
||||
pressAndCheckTime(Key.Left, 1000);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSnappedSeekingAfterControlPointChange()
|
||||
{
|
||||
AddStep("seek to 0", () => EditorClock.Seek(0));
|
||||
AddAssert("time is 0", () => EditorClock.CurrentTime == 0);
|
||||
|
||||
pressAndCheckTime(Key.Right, 1000);
|
||||
pressAndCheckTime(Key.Right, 2000);
|
||||
pressAndCheckTime(Key.Right, 2500);
|
||||
pressAndCheckTime(Key.Right, 3000);
|
||||
|
||||
AddStep("remove 2nd timing point", () =>
|
||||
{
|
||||
EditorBeatmap.BeginChange();
|
||||
var group = EditorBeatmap.ControlPointInfo.GroupAt(2000);
|
||||
EditorBeatmap.ControlPointInfo.RemoveGroup(group);
|
||||
EditorBeatmap.EndChange();
|
||||
});
|
||||
|
||||
pressAndCheckTime(Key.Left, 2000);
|
||||
pressAndCheckTime(Key.Left, 1000);
|
||||
|
||||
pressAndCheckTime(Key.Right, 2000);
|
||||
pressAndCheckTime(Key.Right, 3000);
|
||||
}
|
||||
|
||||
private void pressAndCheckTime(Key key, double expectedTime)
|
||||
{
|
||||
AddStep($"press {key}", () => InputManager.Key(key));
|
||||
AddUntilStep($"time is {expectedTime}", () => Precision.AlmostEquals(expectedTime, EditorClock.CurrentTime, 1));
|
||||
}
|
||||
}
|
||||
}
|
268
osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs
Normal file
268
osu.Game.Tests/Visual/Editing/TestSceneEditorSelection.cs
Normal file
@ -0,0 +1,268 @@
|
||||
// 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;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
public class TestSceneEditorSelection : EditorTestScene
|
||||
{
|
||||
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
|
||||
|
||||
private EditorBlueprintContainer blueprintContainer
|
||||
=> Editor.ChildrenOfType<EditorBlueprintContainer>().First();
|
||||
|
||||
private void moveMouseToObject(Func<HitObject> targetFunc)
|
||||
{
|
||||
AddStep("move mouse to object", () =>
|
||||
{
|
||||
var pos = blueprintContainer.SelectionBlueprints
|
||||
.First(s => s.Item == targetFunc())
|
||||
.ChildrenOfType<HitCirclePiece>()
|
||||
.First().ScreenSpaceDrawQuad.Centre;
|
||||
|
||||
InputManager.MoveMouseTo(pos);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNudgeSelection()
|
||||
{
|
||||
HitCircle[] addedObjects = null;
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
||||
{
|
||||
new HitCircle { StartTime = 100 },
|
||||
new HitCircle { StartTime = 200, Position = new Vector2(100) },
|
||||
new HitCircle { StartTime = 300, Position = new Vector2(200) },
|
||||
new HitCircle { StartTime = 400, Position = new Vector2(300) },
|
||||
}));
|
||||
|
||||
AddStep("select objects", () => EditorBeatmap.SelectedHitObjects.AddRange(addedObjects));
|
||||
|
||||
AddStep("nudge forwards", () => InputManager.Key(Key.K));
|
||||
AddAssert("objects moved forwards in time", () => addedObjects[0].StartTime > 100);
|
||||
|
||||
AddStep("nudge backwards", () => InputManager.Key(Key.J));
|
||||
AddAssert("objects reverted to original position", () => addedObjects[0].StartTime == 100);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBasicSelect()
|
||||
{
|
||||
var addedObject = new HitCircle { StartTime = 100 };
|
||||
AddStep("add hitobject", () => EditorBeatmap.Add(addedObject));
|
||||
|
||||
moveMouseToObject(() => addedObject);
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject);
|
||||
|
||||
var addedObject2 = new HitCircle
|
||||
{
|
||||
StartTime = 100,
|
||||
Position = new Vector2(100),
|
||||
};
|
||||
|
||||
AddStep("add one more hitobject", () => EditorBeatmap.Add(addedObject2));
|
||||
AddAssert("selection unchanged", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject);
|
||||
|
||||
moveMouseToObject(() => addedObject2);
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMultiSelect()
|
||||
{
|
||||
var addedObjects = new[]
|
||||
{
|
||||
new HitCircle { StartTime = 100 },
|
||||
new HitCircle { StartTime = 200, Position = new Vector2(100) },
|
||||
new HitCircle { StartTime = 300, Position = new Vector2(200) },
|
||||
new HitCircle { StartTime = 400, Position = new Vector2(300) },
|
||||
};
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects));
|
||||
|
||||
moveMouseToObject(() => addedObjects[0]);
|
||||
AddStep("click first", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObjects[0]);
|
||||
|
||||
AddStep("hold control", () => InputManager.PressKey(Key.ControlLeft));
|
||||
|
||||
moveMouseToObject(() => addedObjects[1]);
|
||||
AddStep("click second", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1]));
|
||||
|
||||
moveMouseToObject(() => addedObjects[2]);
|
||||
AddStep("click third", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("3 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 3 && EditorBeatmap.SelectedHitObjects.Contains(addedObjects[2]));
|
||||
|
||||
moveMouseToObject(() => addedObjects[1]);
|
||||
AddStep("click second", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && !EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1]));
|
||||
}
|
||||
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void TestMultiSelectFromDrag(bool alreadySelectedBeforeDrag)
|
||||
{
|
||||
HitCircle[] addedObjects = null;
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
||||
{
|
||||
new HitCircle { StartTime = 100 },
|
||||
new HitCircle { StartTime = 200, Position = new Vector2(100) },
|
||||
new HitCircle { StartTime = 300, Position = new Vector2(200) },
|
||||
new HitCircle { StartTime = 400, Position = new Vector2(300) },
|
||||
}));
|
||||
|
||||
moveMouseToObject(() => addedObjects[0]);
|
||||
AddStep("click first", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
AddStep("hold control", () => InputManager.PressKey(Key.ControlLeft));
|
||||
|
||||
moveMouseToObject(() => addedObjects[1]);
|
||||
|
||||
if (alreadySelectedBeforeDrag)
|
||||
AddStep("click second", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
AddStep("mouse down on second", () => InputManager.PressButton(MouseButton.Left));
|
||||
|
||||
AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1]));
|
||||
|
||||
AddStep("drag to centre", () => InputManager.MoveMouseTo(blueprintContainer.ScreenSpaceDrawQuad.Centre));
|
||||
|
||||
AddAssert("positions changed", () => addedObjects[0].Position != Vector2.Zero && addedObjects[1].Position != new Vector2(50));
|
||||
|
||||
AddStep("release control", () => InputManager.ReleaseKey(Key.ControlLeft));
|
||||
AddStep("mouse up", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBasicDeselect()
|
||||
{
|
||||
var addedObject = new HitCircle { StartTime = 100 };
|
||||
AddStep("add hitobject", () => EditorBeatmap.Add(addedObject));
|
||||
|
||||
moveMouseToObject(() => addedObject);
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
AddAssert("hitobject selected", () => EditorBeatmap.SelectedHitObjects.Single() == addedObject);
|
||||
|
||||
AddStep("click away", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(blueprintContainer.ScreenSpaceDrawQuad.Centre);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddAssert("selection lost", () => EditorBeatmap.SelectedHitObjects.Count == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestQuickDeleteRemovesObjectInPlacement()
|
||||
{
|
||||
var addedObject = new HitCircle
|
||||
{
|
||||
StartTime = 0,
|
||||
Position = OsuPlayfield.BASE_SIZE * 0.5f
|
||||
};
|
||||
|
||||
AddStep("add hitobject", () => EditorBeatmap.Add(addedObject));
|
||||
|
||||
AddStep("enter placement mode", () => InputManager.PressKey(Key.Number2));
|
||||
|
||||
moveMouseToObject(() => addedObject);
|
||||
|
||||
AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft));
|
||||
AddStep("right click", () => InputManager.Click(MouseButton.Right));
|
||||
AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft));
|
||||
|
||||
AddAssert("no hitobjects in beatmap", () => EditorBeatmap.HitObjects.Count == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestQuickDeleteRemovesObjectInSelection()
|
||||
{
|
||||
var addedObject = new HitCircle
|
||||
{
|
||||
StartTime = 0,
|
||||
Position = OsuPlayfield.BASE_SIZE * 0.5f
|
||||
};
|
||||
|
||||
AddStep("add hitobject", () => EditorBeatmap.Add(addedObject));
|
||||
|
||||
AddStep("select added object", () => EditorBeatmap.SelectedHitObjects.Add(addedObject));
|
||||
|
||||
moveMouseToObject(() => addedObject);
|
||||
|
||||
AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft));
|
||||
AddStep("right click", () => InputManager.Click(MouseButton.Right));
|
||||
AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft));
|
||||
|
||||
AddAssert("no hitobjects in beatmap", () => EditorBeatmap.HitObjects.Count == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestQuickDeleteRemovesSliderControlPoint()
|
||||
{
|
||||
Slider slider = null;
|
||||
|
||||
PathControlPoint[] points =
|
||||
{
|
||||
new PathControlPoint(),
|
||||
new PathControlPoint(new Vector2(50, 0)),
|
||||
new PathControlPoint(new Vector2(100, 0))
|
||||
};
|
||||
|
||||
AddStep("add slider", () =>
|
||||
{
|
||||
slider = new Slider
|
||||
{
|
||||
StartTime = 1000,
|
||||
Path = new SliderPath(points)
|
||||
};
|
||||
|
||||
EditorBeatmap.Add(slider);
|
||||
});
|
||||
|
||||
AddStep("select added slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||
|
||||
AddStep("move mouse to controlpoint", () =>
|
||||
{
|
||||
var pos = blueprintContainer.ChildrenOfType<PathControlPointPiece>().ElementAt(1).ScreenSpaceDrawQuad.Centre;
|
||||
InputManager.MoveMouseTo(pos);
|
||||
});
|
||||
AddStep("hold shift", () => InputManager.PressKey(Key.ShiftLeft));
|
||||
|
||||
AddStep("right click", () => InputManager.Click(MouseButton.Right));
|
||||
AddAssert("slider has 2 points", () => slider.Path.ControlPoints.Count == 2);
|
||||
|
||||
AddStep("right click", () => InputManager.Click(MouseButton.Right));
|
||||
|
||||
// second click should nuke the object completely.
|
||||
AddAssert("no hitobjects in beatmap", () => EditorBeatmap.HitObjects.Count == 0);
|
||||
|
||||
AddStep("release shift", () => InputManager.ReleaseKey(Key.ShiftLeft));
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Components.Timelines.Summary;
|
||||
using osuTK;
|
||||
|
||||
@ -13,16 +14,29 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
[TestFixture]
|
||||
public class TestSceneEditorSummaryTimeline : EditorClockTestScene
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
[Cached(typeof(EditorBeatmap))]
|
||||
private readonly EditorBeatmap editorBeatmap;
|
||||
|
||||
Add(new SummaryTimeline
|
||||
public TestSceneEditorSummaryTimeline()
|
||||
{
|
||||
editorBeatmap = new EditorBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo));
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
AddStep("create timeline", () =>
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(500, 50)
|
||||
// required for track
|
||||
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
|
||||
|
||||
Add(new SummaryTimeline
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(500, 50)
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
// 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
using static osu.Game.Screens.Edit.Compose.Components.Timeline.TimelineHitObjectBlueprint;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
public class TestSceneTimelineHitObjectBlueprint : TimelineTestScene
|
||||
{
|
||||
public override Drawable CreateTestComponent() => new TimelineBlueprintContainer(Composer);
|
||||
|
||||
[Test]
|
||||
public void TestDisallowZeroDurationObjects()
|
||||
{
|
||||
DragArea dragArea;
|
||||
|
||||
AddStep("add spinner", () =>
|
||||
{
|
||||
EditorBeatmap.Clear();
|
||||
EditorBeatmap.Add(new Spinner
|
||||
{
|
||||
Position = new Vector2(256, 256),
|
||||
StartTime = 2700,
|
||||
Duration = 500
|
||||
});
|
||||
});
|
||||
|
||||
AddStep("hold down drag bar", () =>
|
||||
{
|
||||
// distinguishes between the actual drag bar and its "underlay shadow".
|
||||
dragArea = this.ChildrenOfType<DragArea>().Single(bar => bar.HandlePositionalInput);
|
||||
InputManager.MoveMouseTo(dragArea);
|
||||
InputManager.PressButton(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddStep("try to drag bar past start", () =>
|
||||
{
|
||||
var blueprint = this.ChildrenOfType<TimelineHitObjectBlueprint>().Single();
|
||||
InputManager.MoveMouseTo(blueprint.SelectionQuad.TopLeft - new Vector2(100, 0));
|
||||
InputManager.ReleaseButton(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddAssert("object has non-zero duration", () => EditorBeatmap.HitObjects.OfType<IHasDuration>().Single().Duration > 0);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,22 +23,24 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
protected HitObjectComposer Composer { get; private set; }
|
||||
|
||||
protected EditorBeatmap EditorBeatmap { get; private set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio)
|
||||
{
|
||||
Beatmap.Value = new WaveformTestBeatmap(audio);
|
||||
|
||||
var playable = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset);
|
||||
var editorBeatmap = new EditorBeatmap(playable);
|
||||
EditorBeatmap = new EditorBeatmap(playable);
|
||||
|
||||
Dependencies.Cache(editorBeatmap);
|
||||
Dependencies.CacheAs<IBeatSnapProvider>(editorBeatmap);
|
||||
Dependencies.Cache(EditorBeatmap);
|
||||
Dependencies.CacheAs<IBeatSnapProvider>(EditorBeatmap);
|
||||
|
||||
Composer = playable.BeatmapInfo.Ruleset.CreateInstance().CreateHitObjectComposer().With(d => d.Alpha = 0);
|
||||
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
editorBeatmap,
|
||||
EditorBeatmap,
|
||||
Composer,
|
||||
new FillFlowContainer
|
||||
{
|
||||
@ -51,17 +53,21 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
new AudioVisualiser(),
|
||||
}
|
||||
},
|
||||
TimelineArea = new TimelineArea
|
||||
TimelineArea = new TimelineArea(CreateTestComponent())
|
||||
{
|
||||
Child = CreateTestComponent(),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Size = new Vector2(0.8f, 100),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Clock.Seek(2500);
|
||||
}
|
||||
|
||||
public abstract Drawable CreateTestComponent();
|
||||
|
||||
private class AudioVisualiser : CompositeDrawable
|
||||
@ -108,8 +114,6 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
[Resolved]
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
private bool started;
|
||||
|
||||
public StartStopButton()
|
||||
{
|
||||
BackgroundColour = Color4.SlateGray;
|
||||
@ -121,18 +125,17 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
|
||||
private void onClick()
|
||||
{
|
||||
if (started)
|
||||
{
|
||||
if (editorClock.IsRunning)
|
||||
editorClock.Stop();
|
||||
Text = "Start";
|
||||
}
|
||||
else
|
||||
{
|
||||
editorClock.Start();
|
||||
Text = "Stop";
|
||||
}
|
||||
}
|
||||
|
||||
started = !started;
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Text = editorClock.IsRunning ? "Stop" : "Start";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
private void seekToBreak(int breakIndex)
|
||||
{
|
||||
AddStep($"seek to break {breakIndex}", () => Player.GameplayClockContainer.Seek(destBreak().StartTime));
|
||||
AddUntilStep("wait for seek to complete", () => Player.HUDOverlay.Progress.ReferenceClock.CurrentTime >= destBreak().StartTime);
|
||||
AddUntilStep("wait for seek to complete", () => Player.DrawableRuleset.FrameStableClock.CurrentTime >= destBreak().StartTime);
|
||||
|
||||
BreakPeriod destBreak() => Beatmap.Value.Beatmap.Breaks.ElementAt(breakIndex);
|
||||
}
|
||||
|
@ -1,47 +1,35 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneComboCounter : SkinnableTestScene
|
||||
{
|
||||
private IEnumerable<SkinnableComboCounter> comboCounters => CreatedDrawables.OfType<SkinnableComboCounter>();
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
|
||||
[Cached]
|
||||
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("Create combo counters", () => SetContents(() =>
|
||||
{
|
||||
var comboCounter = new SkinnableComboCounter();
|
||||
comboCounter.Current.Value = 1;
|
||||
return comboCounter;
|
||||
}));
|
||||
AddStep("Create combo counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ComboCounter))));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestComboCounterIncrementing()
|
||||
{
|
||||
AddRepeatStep("increase combo", () =>
|
||||
{
|
||||
foreach (var counter in comboCounters)
|
||||
counter.Current.Value++;
|
||||
}, 10);
|
||||
AddRepeatStep("increase combo", () => scoreProcessor.Combo.Value++, 10);
|
||||
|
||||
AddStep("reset combo", () =>
|
||||
{
|
||||
foreach (var counter in comboCounters)
|
||||
counter.Current.Value = 0;
|
||||
});
|
||||
AddStep("reset combo", () => scoreProcessor.Combo.Value = 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Storyboards;
|
||||
using osuTK;
|
||||
|
||||
@ -50,7 +52,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
cancel();
|
||||
complete();
|
||||
|
||||
AddUntilStep("attempted to push ranking", () => ((FakeRankingPushPlayer)Player).GotoRankingInvoked);
|
||||
AddUntilStep("attempted to push ranking", () => ((FakeRankingPushPlayer)Player).ResultsCreated);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -84,7 +86,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
// wait to ensure there was no attempt of pushing the results screen.
|
||||
AddWaitStep("wait", resultsDisplayWaitCount);
|
||||
AddAssert("no attempt to push ranking", () => !((FakeRankingPushPlayer)Player).GotoRankingInvoked);
|
||||
AddAssert("no attempt to push ranking", () => !((FakeRankingPushPlayer)Player).ResultsCreated);
|
||||
}
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||
@ -110,16 +112,18 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public class FakeRankingPushPlayer : TestPlayer
|
||||
{
|
||||
public bool GotoRankingInvoked;
|
||||
public bool ResultsCreated { get; private set; }
|
||||
|
||||
public FakeRankingPushPlayer()
|
||||
: base(true, true)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void GotoRanking()
|
||||
protected override ResultsScreen CreateResults(ScoreInfo score)
|
||||
{
|
||||
GotoRankingInvoked = true;
|
||||
var results = base.CreateResults(score);
|
||||
ResultsCreated = true;
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
// 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
@ -20,24 +23,28 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; }
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
private void create(HealthProcessor healthProcessor)
|
||||
{
|
||||
AddStep("create layer", () =>
|
||||
{
|
||||
Child = layer = new FailingLayer();
|
||||
layer.BindHealthProcessor(new DrainingHealthProcessor(1));
|
||||
Child = new HealthProcessorContainer(healthProcessor)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = layer = new FailingLayer()
|
||||
};
|
||||
|
||||
layer.ShowHealth.BindTo(showHealth);
|
||||
});
|
||||
|
||||
AddStep("show health", () => showHealth.Value = true);
|
||||
AddStep("enable layer", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true));
|
||||
AddUntilStep("layer is visible", () => layer.IsPresent);
|
||||
AddStep("enable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLayerFading()
|
||||
{
|
||||
create(new DrainingHealthProcessor(0));
|
||||
|
||||
AddSliderStep("current health", 0.0, 1.0, 1.0, val =>
|
||||
{
|
||||
if (layer != null)
|
||||
@ -45,15 +52,17 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
});
|
||||
|
||||
AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
|
||||
AddUntilStep("layer fade is visible", () => layer.Child.Alpha > 0.1f);
|
||||
AddUntilStep("layer fade is visible", () => layer.ChildrenOfType<Container>().First().Alpha > 0.1f);
|
||||
AddStep("set health to 1", () => layer.Current.Value = 1f);
|
||||
AddUntilStep("layer fade is invisible", () => !layer.Child.IsPresent);
|
||||
AddUntilStep("layer fade is invisible", () => !layer.ChildrenOfType<Container>().First().IsPresent);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLayerDisabledViaConfig()
|
||||
{
|
||||
AddStep("disable layer", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, false));
|
||||
create(new DrainingHealthProcessor(0));
|
||||
AddUntilStep("layer is visible", () => layer.IsPresent);
|
||||
AddStep("disable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false));
|
||||
AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
|
||||
AddUntilStep("layer is not visible", () => !layer.IsPresent);
|
||||
}
|
||||
@ -61,7 +70,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestLayerVisibilityWithAccumulatingProcessor()
|
||||
{
|
||||
AddStep("bind accumulating processor", () => layer.BindHealthProcessor(new AccumulatingHealthProcessor(1)));
|
||||
create(new AccumulatingHealthProcessor(1));
|
||||
AddUntilStep("layer is not visible", () => !layer.IsPresent);
|
||||
AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
|
||||
AddUntilStep("layer is not visible", () => !layer.IsPresent);
|
||||
}
|
||||
@ -69,7 +79,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestLayerVisibilityWithDrainingProcessor()
|
||||
{
|
||||
AddStep("bind accumulating processor", () => layer.BindHealthProcessor(new DrainingHealthProcessor(1)));
|
||||
create(new DrainingHealthProcessor(0));
|
||||
AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
|
||||
AddWaitStep("wait for potential fade", 10);
|
||||
AddAssert("layer is still visible", () => layer.IsPresent);
|
||||
@ -78,23 +88,36 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestLayerVisibilityWithDifferentOptions()
|
||||
{
|
||||
create(new DrainingHealthProcessor(0));
|
||||
|
||||
AddStep("set health to 0.10", () => layer.Current.Value = 0.1);
|
||||
|
||||
AddStep("don't show health", () => showHealth.Value = false);
|
||||
AddStep("disable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, false));
|
||||
AddStep("disable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false));
|
||||
AddUntilStep("layer fade is invisible", () => !layer.IsPresent);
|
||||
|
||||
AddStep("don't show health", () => showHealth.Value = false);
|
||||
AddStep("enable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true));
|
||||
AddStep("enable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true));
|
||||
AddUntilStep("layer fade is invisible", () => !layer.IsPresent);
|
||||
|
||||
AddStep("show health", () => showHealth.Value = true);
|
||||
AddStep("disable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, false));
|
||||
AddStep("disable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false));
|
||||
AddUntilStep("layer fade is invisible", () => !layer.IsPresent);
|
||||
|
||||
AddStep("show health", () => showHealth.Value = true);
|
||||
AddStep("enable FadePlayfieldWhenHealthLow", () => config.Set(OsuSetting.FadePlayfieldWhenHealthLow, true));
|
||||
AddStep("enable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, true));
|
||||
AddUntilStep("layer fade is visible", () => layer.IsPresent);
|
||||
}
|
||||
|
||||
private class HealthProcessorContainer : Container
|
||||
{
|
||||
[Cached(typeof(HealthProcessor))]
|
||||
private readonly HealthProcessor healthProcessor;
|
||||
|
||||
public HealthProcessorContainer(HealthProcessor healthProcessor)
|
||||
{
|
||||
this.healthProcessor = healthProcessor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
104
osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs
Normal file
104
osu.Game.Tests/Visual/Gameplay/TestSceneGameplayLeaderboard.cs
Normal file
@ -0,0 +1,104 @@
|
||||
// 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.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSceneGameplayLeaderboard : OsuTestScene
|
||||
{
|
||||
private readonly TestGameplayLeaderboard leaderboard;
|
||||
|
||||
private readonly BindableDouble playerScore = new BindableDouble();
|
||||
|
||||
public TestSceneGameplayLeaderboard()
|
||||
{
|
||||
Add(leaderboard = new TestGameplayLeaderboard
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(2),
|
||||
});
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("reset leaderboard", () =>
|
||||
{
|
||||
leaderboard.Clear();
|
||||
playerScore.Value = 1222333;
|
||||
});
|
||||
|
||||
AddStep("add local player", () => createLeaderboardScore(playerScore, new User { Username = "You", Id = 3 }, true));
|
||||
AddSliderStep("set player score", 50, 5000000, 1222333, v => playerScore.Value = v);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPlayerScore()
|
||||
{
|
||||
var player2Score = new BindableDouble(1234567);
|
||||
var player3Score = new BindableDouble(1111111);
|
||||
|
||||
AddStep("add player 2", () => createLeaderboardScore(player2Score, new User { Username = "Player 2" }));
|
||||
AddStep("add player 3", () => createLeaderboardScore(player3Score, new User { Username = "Player 3" }));
|
||||
|
||||
AddUntilStep("is player 2 position #1", () => leaderboard.CheckPositionByUsername("Player 2", 1));
|
||||
AddUntilStep("is player position #2", () => leaderboard.CheckPositionByUsername("You", 2));
|
||||
AddUntilStep("is player 3 position #3", () => leaderboard.CheckPositionByUsername("Player 3", 3));
|
||||
|
||||
AddStep("set score above player 3", () => player2Score.Value = playerScore.Value - 500);
|
||||
AddUntilStep("is player position #1", () => leaderboard.CheckPositionByUsername("You", 1));
|
||||
AddUntilStep("is player 2 position #2", () => leaderboard.CheckPositionByUsername("Player 2", 2));
|
||||
AddUntilStep("is player 3 position #3", () => leaderboard.CheckPositionByUsername("Player 3", 3));
|
||||
|
||||
AddStep("set score below players", () => player2Score.Value = playerScore.Value - 123456);
|
||||
AddUntilStep("is player position #1", () => leaderboard.CheckPositionByUsername("You", 1));
|
||||
AddUntilStep("is player 3 position #2", () => leaderboard.CheckPositionByUsername("Player 3", 2));
|
||||
AddUntilStep("is player 2 position #3", () => leaderboard.CheckPositionByUsername("Player 2", 3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRandomScores()
|
||||
{
|
||||
int playerNumber = 1;
|
||||
AddRepeatStep("add player with random score", () => createRandomScore(new User { Username = $"Player {playerNumber++}" }), 10);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExistingUsers()
|
||||
{
|
||||
AddStep("add peppy", () => createRandomScore(new User { Username = "peppy", Id = 2 }));
|
||||
AddStep("add smoogipoo", () => createRandomScore(new User { Username = "smoogipoo", Id = 1040328 }));
|
||||
AddStep("add flyte", () => createRandomScore(new User { Username = "flyte", Id = 3103765 }));
|
||||
AddStep("add frenzibyte", () => createRandomScore(new User { Username = "frenzibyte", Id = 14210502 }));
|
||||
}
|
||||
|
||||
private void createRandomScore(User user) => createLeaderboardScore(new BindableDouble(RNG.Next(0, 5_000_000)), user);
|
||||
|
||||
private void createLeaderboardScore(BindableDouble score, User user, bool isTracked = false)
|
||||
{
|
||||
var leaderboardScore = leaderboard.AddPlayer(user, isTracked);
|
||||
leaderboardScore.TotalScore.BindTo(score);
|
||||
}
|
||||
|
||||
private class TestGameplayLeaderboard : GameplayLeaderboard
|
||||
{
|
||||
public bool CheckPositionByUsername(string username, int? expectedPosition)
|
||||
{
|
||||
var scoreItem = this.FirstOrDefault(i => i.User?.Username == username);
|
||||
|
||||
return scoreItem != null && scoreItem.ScorePosition == expectedPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.Audio;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
@ -20,14 +19,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public void TestAllSamplesStopDuringSeek()
|
||||
{
|
||||
DrawableSlider slider = null;
|
||||
DrawableSample[] samples = null;
|
||||
PoolableSkinnableSample[] samples = null;
|
||||
ISamplePlaybackDisabler sampleDisabler = null;
|
||||
|
||||
AddUntilStep("get variables", () =>
|
||||
{
|
||||
sampleDisabler = Player;
|
||||
slider = Player.ChildrenOfType<DrawableSlider>().OrderBy(s => s.HitObject.StartTime).FirstOrDefault();
|
||||
samples = slider?.ChildrenOfType<DrawableSample>().ToArray();
|
||||
samples = slider?.ChildrenOfType<PoolableSkinnableSample>().ToArray();
|
||||
|
||||
return slider != null;
|
||||
});
|
||||
|
@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -19,6 +20,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
private HUDOverlay hudOverlay;
|
||||
|
||||
[Cached]
|
||||
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
||||
|
||||
[Cached(typeof(HealthProcessor))]
|
||||
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
||||
|
||||
// best way to check without exposing.
|
||||
private Drawable hideTarget => hudOverlay.KeyCounter;
|
||||
private FillFlowContainer<KeyCounter> keyCounterFlow => hudOverlay.KeyCounter.ChildrenOfType<FillFlowContainer<KeyCounter>>().First();
|
||||
@ -31,9 +38,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
createNew();
|
||||
|
||||
AddRepeatStep("increase combo", () => { hudOverlay.ComboCounter.Current.Value++; }, 10);
|
||||
AddRepeatStep("increase combo", () => { scoreProcessor.Combo.Value++; }, 10);
|
||||
|
||||
AddStep("reset combo", () => { hudOverlay.ComboCounter.Current.Value = 0; });
|
||||
AddStep("reset combo", () => { scoreProcessor.Combo.Value = 0; });
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -81,7 +88,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddStep("get original config value", () => originalConfigValue = config.Get<HUDVisibilityMode>(OsuSetting.HUDVisibilityMode));
|
||||
|
||||
AddStep("set hud to never show", () => config.Set(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
|
||||
AddStep("set hud to never show", () => config.SetValue(OsuSetting.HUDVisibilityMode, HUDVisibilityMode.Never));
|
||||
|
||||
AddUntilStep("wait for fade", () => !hideTarget.IsPresent);
|
||||
|
||||
@ -91,7 +98,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("stop trigering", () => InputManager.ReleaseKey(Key.ControlLeft));
|
||||
AddUntilStep("wait for fade", () => !hideTarget.IsPresent);
|
||||
|
||||
AddStep("set original config value", () => config.Set(OsuSetting.HUDVisibilityMode, originalConfigValue));
|
||||
AddStep("set original config value", () => config.SetValue(OsuSetting.HUDVisibilityMode, originalConfigValue));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -120,7 +127,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddStep("set keycounter visible false", () =>
|
||||
{
|
||||
config.Set<bool>(OsuSetting.KeyOverlay, false);
|
||||
config.SetValue(OsuSetting.KeyOverlay, false);
|
||||
hudOverlay.KeyCounter.AlwaysVisible.Value = false;
|
||||
});
|
||||
|
||||
@ -132,19 +139,19 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddUntilStep("hidetarget is visible", () => hideTarget.IsPresent);
|
||||
AddAssert("key counters still hidden", () => !keyCounterFlow.IsPresent);
|
||||
|
||||
AddStep("return value", () => config.Set<bool>(OsuSetting.KeyOverlay, keyCounterVisibleValue));
|
||||
AddStep("return value", () => config.SetValue(OsuSetting.KeyOverlay, keyCounterVisibleValue));
|
||||
}
|
||||
|
||||
private void createNew(Action<HUDOverlay> action = null)
|
||||
{
|
||||
AddStep("create overlay", () =>
|
||||
{
|
||||
hudOverlay = new HUDOverlay(null, null, null, Array.Empty<Mod>());
|
||||
hudOverlay = new HUDOverlay(null, Array.Empty<Mod>());
|
||||
|
||||
// Add any key just to display the key counter visually.
|
||||
hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space));
|
||||
|
||||
hudOverlay.ComboCounter.Current.Value = 1;
|
||||
scoreProcessor.Combo.Value = 1;
|
||||
|
||||
action?.Invoke(hudOverlay);
|
||||
|
||||
|
@ -1,32 +1,39 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Catch.Scoring;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Scoring;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko.Scoring;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play.HUD.HitErrorMeters;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneHitErrorMeter : OsuTestScene
|
||||
{
|
||||
private BarHitErrorMeter barMeter;
|
||||
private BarHitErrorMeter barMeter2;
|
||||
private BarHitErrorMeter barMeter3;
|
||||
private ColourHitErrorMeter colourMeter;
|
||||
private ColourHitErrorMeter colourMeter2;
|
||||
private ColourHitErrorMeter colourMeter3;
|
||||
private HitWindows hitWindows;
|
||||
[Cached]
|
||||
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
||||
|
||||
[Cached(typeof(DrawableRuleset))]
|
||||
private TestDrawableRuleset drawableRuleset = new TestDrawableRuleset();
|
||||
|
||||
public TestSceneHitErrorMeter()
|
||||
{
|
||||
@ -34,8 +41,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddRepeatStep("New random judgement", () => newJudgement(), 40);
|
||||
|
||||
AddRepeatStep("New max negative", () => newJudgement(-hitWindows.WindowFor(HitResult.Meh)), 20);
|
||||
AddRepeatStep("New max positive", () => newJudgement(hitWindows.WindowFor(HitResult.Meh)), 20);
|
||||
AddRepeatStep("New max negative", () => newJudgement(-drawableRuleset.HitWindows.WindowFor(HitResult.Meh)), 20);
|
||||
AddRepeatStep("New max positive", () => newJudgement(drawableRuleset.HitWindows.WindowFor(HitResult.Meh)), 20);
|
||||
AddStep("New fixed judgement (50ms)", () => newJudgement(50));
|
||||
|
||||
AddStep("Judgement barrage", () =>
|
||||
@ -85,10 +92,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private void recreateDisplay(HitWindows hitWindows, float overallDifficulty)
|
||||
{
|
||||
this.hitWindows = hitWindows;
|
||||
|
||||
hitWindows?.SetDifficulty(overallDifficulty);
|
||||
|
||||
drawableRuleset.HitWindows = hitWindows;
|
||||
|
||||
Clear();
|
||||
|
||||
Add(new FillFlowContainer
|
||||
@ -105,40 +112,40 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
});
|
||||
|
||||
Add(barMeter = new BarHitErrorMeter(hitWindows, true)
|
||||
Add(new BarHitErrorMeter
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
});
|
||||
|
||||
Add(barMeter2 = new BarHitErrorMeter(hitWindows, false)
|
||||
Add(new BarHitErrorMeter
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
});
|
||||
|
||||
Add(barMeter3 = new BarHitErrorMeter(hitWindows, true)
|
||||
Add(new BarHitErrorMeter
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Rotation = 270,
|
||||
});
|
||||
|
||||
Add(colourMeter = new ColourHitErrorMeter(hitWindows)
|
||||
Add(new ColourHitErrorMeter
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Margin = new MarginPadding { Right = 50 }
|
||||
});
|
||||
|
||||
Add(colourMeter2 = new ColourHitErrorMeter(hitWindows)
|
||||
Add(new ColourHitErrorMeter
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Margin = new MarginPadding { Left = 50 }
|
||||
});
|
||||
|
||||
Add(colourMeter3 = new ColourHitErrorMeter(hitWindows)
|
||||
Add(new ColourHitErrorMeter
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.CentreLeft,
|
||||
@ -149,18 +156,47 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private void newJudgement(double offset = 0)
|
||||
{
|
||||
var judgement = new JudgementResult(new HitObject(), new Judgement())
|
||||
scoreProcessor.ApplyResult(new JudgementResult(new HitCircle { HitWindows = drawableRuleset.HitWindows }, new Judgement())
|
||||
{
|
||||
TimeOffset = offset == 0 ? RNG.Next(-150, 150) : offset,
|
||||
Type = HitResult.Perfect,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
barMeter.OnNewJudgement(judgement);
|
||||
barMeter2.OnNewJudgement(judgement);
|
||||
barMeter3.OnNewJudgement(judgement);
|
||||
colourMeter.OnNewJudgement(judgement);
|
||||
colourMeter2.OnNewJudgement(judgement);
|
||||
colourMeter3.OnNewJudgement(judgement);
|
||||
[SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")]
|
||||
private class TestDrawableRuleset : DrawableRuleset
|
||||
{
|
||||
public HitWindows HitWindows;
|
||||
|
||||
public override IEnumerable<HitObject> Objects => new[] { new HitCircle { HitWindows = HitWindows } };
|
||||
|
||||
public override event Action<JudgementResult> NewResult;
|
||||
public override event Action<JudgementResult> RevertResult;
|
||||
|
||||
public override Playfield Playfield { get; }
|
||||
public override Container Overlays { get; }
|
||||
public override Container FrameStableComponents { get; }
|
||||
public override IFrameStableClock FrameStableClock { get; }
|
||||
public override IReadOnlyList<Mod> Mods { get; }
|
||||
|
||||
public override double GameplayStartTime { get; }
|
||||
public override GameplayCursorContainer Cursor { get; }
|
||||
|
||||
public TestDrawableRuleset()
|
||||
: base(new OsuRuleset())
|
||||
{
|
||||
// won't compile without this.
|
||||
NewResult?.Invoke(null);
|
||||
RevertResult?.Invoke(null);
|
||||
}
|
||||
|
||||
public override void SetReplayScore(Score replayScore) => throw new NotImplementedException();
|
||||
|
||||
public override void SetRecordTarget(Score score) => throw new NotImplementedException();
|
||||
|
||||
public override void RequestResume(Action continueResume) => throw new NotImplementedException();
|
||||
|
||||
public override void CancelResume() => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,11 +46,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[TestCase(0, 0)]
|
||||
[TestCase(-1000, -1000)]
|
||||
[TestCase(-10000, -10000)]
|
||||
public void TestStoryboardProducesCorrectStartTime(double firstStoryboardEvent, double expectedStartTime)
|
||||
public void TestStoryboardProducesCorrectStartTimeSimpleAlpha(double firstStoryboardEvent, double expectedStartTime)
|
||||
{
|
||||
var storyboard = new Storyboard();
|
||||
|
||||
var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero);
|
||||
|
||||
sprite.TimelineGroup.Alpha.Add(Easing.None, firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1);
|
||||
|
||||
storyboard.GetLayer("Background").Add(sprite);
|
||||
@ -64,6 +65,43 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase(1000, 0, false)]
|
||||
[TestCase(0, 0, false)]
|
||||
[TestCase(-1000, -1000, false)]
|
||||
[TestCase(-10000, -10000, false)]
|
||||
[TestCase(1000, 0, true)]
|
||||
[TestCase(0, 0, true)]
|
||||
[TestCase(-1000, -1000, true)]
|
||||
[TestCase(-10000, -10000, true)]
|
||||
public void TestStoryboardProducesCorrectStartTimeFadeInAfterOtherEvents(double firstStoryboardEvent, double expectedStartTime, bool addEventToLoop)
|
||||
{
|
||||
var storyboard = new Storyboard();
|
||||
|
||||
var sprite = new StoryboardSprite("unknown", Anchor.TopLeft, Vector2.Zero);
|
||||
|
||||
// these should be ignored as we have an alpha visibility blocker proceeding this command.
|
||||
sprite.TimelineGroup.Scale.Add(Easing.None, -20000, -18000, 0, 1);
|
||||
var loopGroup = sprite.AddLoop(-20000, 50);
|
||||
loopGroup.Scale.Add(Easing.None, -20000, -18000, 0, 1);
|
||||
|
||||
var target = addEventToLoop ? loopGroup : sprite.TimelineGroup;
|
||||
target.Alpha.Add(Easing.None, firstStoryboardEvent, firstStoryboardEvent + 500, 0, 1);
|
||||
|
||||
// these should be ignored due to being in the future.
|
||||
sprite.TimelineGroup.Alpha.Add(Easing.None, 18000, 20000, 0, 1);
|
||||
loopGroup.Alpha.Add(Easing.None, 18000, 20000, 0, 1);
|
||||
|
||||
storyboard.GetLayer("Background").Add(sprite);
|
||||
|
||||
loadPlayerWithBeatmap(new TestBeatmap(new OsuRuleset().RulesetInfo), storyboard);
|
||||
|
||||
AddAssert($"first frame is {expectedStartTime}", () =>
|
||||
{
|
||||
Debug.Assert(player.FirstFrameClockTime != null);
|
||||
return Precision.AlmostEquals(player.FirstFrameClockTime.Value, expectedStartTime, lenience_ms);
|
||||
});
|
||||
}
|
||||
|
||||
private void loadPlayerWithBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||
{
|
||||
AddStep("create player", () =>
|
||||
@ -92,9 +130,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public double GameplayClockTime => GameplayClockContainer.GameplayClock.CurrentTime;
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
protected override void Update()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
base.Update();
|
||||
|
||||
if (!FirstFrameClockTime.HasValue)
|
||||
{
|
||||
|
@ -56,9 +56,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
pauseAndConfirm();
|
||||
resume();
|
||||
|
||||
confirmClockRunning(false);
|
||||
confirmPauseOverlayShown(false);
|
||||
|
||||
confirmPausedWithNoOverlay();
|
||||
AddStep("click to resume", () => InputManager.Click(MouseButton.Left));
|
||||
|
||||
confirmClockRunning(true);
|
||||
@ -71,15 +69,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddUntilStep("wait for hitobjects", () => Player.HealthProcessor.Health.Value < 1);
|
||||
|
||||
pauseAndConfirm();
|
||||
|
||||
resume();
|
||||
confirmClockRunning(false);
|
||||
confirmPauseOverlayShown(false);
|
||||
|
||||
confirmPausedWithNoOverlay();
|
||||
pauseAndConfirm();
|
||||
|
||||
AddUntilStep("resume overlay is not active", () => Player.DrawableRuleset.ResumeOverlay.State.Value == Visibility.Hidden);
|
||||
confirmPaused();
|
||||
confirmNotExited();
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -94,33 +91,54 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPauseTooSoon()
|
||||
public void TestUserPauseWhenPauseNotAllowed()
|
||||
{
|
||||
AddStep("disable pause support", () => Player.Configuration.AllowPause = false);
|
||||
|
||||
pauseFromUserExitKey();
|
||||
confirmExited();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserPauseDuringCooldownTooSoon()
|
||||
{
|
||||
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
||||
|
||||
pauseAndConfirm();
|
||||
|
||||
resume();
|
||||
pause();
|
||||
pauseFromUserExitKey();
|
||||
|
||||
confirmClockRunning(true);
|
||||
confirmPauseOverlayShown(false);
|
||||
confirmResumed();
|
||||
confirmNotExited();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExitTooSoon()
|
||||
public void TestQuickExitDuringCooldownTooSoon()
|
||||
{
|
||||
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
||||
|
||||
pauseAndConfirm();
|
||||
|
||||
resume();
|
||||
AddStep("pause via exit key", () => Player.ExitViaQuickExit());
|
||||
|
||||
confirmResumed();
|
||||
AddAssert("exited", () => !Player.IsCurrentScreen());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExitSoonAfterResumeSucceeds()
|
||||
{
|
||||
AddStep("seek before gameplay", () => Player.GameplayClockContainer.Seek(-5000));
|
||||
|
||||
pauseAndConfirm();
|
||||
resume();
|
||||
|
||||
AddStep("exit too soon", () => Player.Exit());
|
||||
AddStep("exit quick", () => Player.Exit());
|
||||
|
||||
confirmClockRunning(true);
|
||||
confirmPauseOverlayShown(false);
|
||||
|
||||
AddAssert("not exited", () => Player.IsCurrentScreen());
|
||||
confirmResumed();
|
||||
AddAssert("exited", () => !Player.IsCurrentScreen());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -131,22 +149,37 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
confirmClockRunning(false);
|
||||
|
||||
pause();
|
||||
|
||||
confirmClockRunning(false);
|
||||
confirmPauseOverlayShown(false);
|
||||
AddStep("pause via forced pause", () => Player.Pause());
|
||||
|
||||
confirmPausedWithNoOverlay();
|
||||
AddAssert("fail overlay still shown", () => Player.FailOverlayVisible);
|
||||
|
||||
exitAndConfirm();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExitFromFailedGameplay()
|
||||
public void TestExitFromFailedGameplayAfterFailAnimation()
|
||||
{
|
||||
AddUntilStep("wait for fail", () => Player.HasFailed);
|
||||
AddStep("exit", () => Player.Exit());
|
||||
AddUntilStep("wait for fail overlay shown", () => Player.FailOverlayVisible);
|
||||
|
||||
confirmClockRunning(false);
|
||||
|
||||
AddStep("exit via user pause", () => Player.ExitViaPause());
|
||||
confirmExited();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExitFromFailedGameplayDuringFailAnimation()
|
||||
{
|
||||
AddUntilStep("wait for fail", () => Player.HasFailed);
|
||||
|
||||
// will finish the fail animation and show the fail/pause screen.
|
||||
AddStep("attempt exit via pause key", () => Player.ExitViaPause());
|
||||
AddAssert("fail overlay shown", () => Player.FailOverlayVisible);
|
||||
|
||||
// will actually exit.
|
||||
AddStep("exit via pause key", () => Player.ExitViaPause());
|
||||
confirmExited();
|
||||
}
|
||||
|
||||
@ -245,7 +278,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private void pauseAndConfirm()
|
||||
{
|
||||
pause();
|
||||
pauseFromUserExitKey();
|
||||
confirmPaused();
|
||||
}
|
||||
|
||||
@ -257,7 +290,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private void exitAndConfirm()
|
||||
{
|
||||
AddUntilStep("player not exited", () => Player.IsCurrentScreen());
|
||||
confirmNotExited();
|
||||
AddStep("exit", () => Player.Exit());
|
||||
confirmExited();
|
||||
confirmNoTrackAdjustments();
|
||||
@ -266,7 +299,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
private void confirmPaused()
|
||||
{
|
||||
confirmClockRunning(false);
|
||||
AddAssert("player not exited", () => Player.IsCurrentScreen());
|
||||
confirmNotExited();
|
||||
AddAssert("player not failed", () => !Player.HasFailed);
|
||||
AddAssert("pause overlay shown", () => Player.PauseOverlayVisible);
|
||||
}
|
||||
@ -277,18 +310,22 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
confirmPauseOverlayShown(false);
|
||||
}
|
||||
|
||||
private void confirmExited()
|
||||
private void confirmPausedWithNoOverlay()
|
||||
{
|
||||
AddUntilStep("player exited", () => !Player.IsCurrentScreen());
|
||||
confirmClockRunning(false);
|
||||
confirmPauseOverlayShown(false);
|
||||
}
|
||||
|
||||
private void confirmExited() => AddUntilStep("player exited", () => !Player.IsCurrentScreen());
|
||||
private void confirmNotExited() => AddAssert("player not exited", () => Player.IsCurrentScreen());
|
||||
|
||||
private void confirmNoTrackAdjustments()
|
||||
{
|
||||
AddAssert("track has no adjustments", () => Beatmap.Value.Track.AggregateFrequency.Value == 1);
|
||||
}
|
||||
|
||||
private void restart() => AddStep("restart", () => Player.Restart());
|
||||
private void pause() => AddStep("pause", () => Player.Pause());
|
||||
private void pauseFromUserExitKey() => AddStep("user pause", () => Player.ExitViaPause());
|
||||
private void resume() => AddStep("resume", () => Player.Resume());
|
||||
|
||||
private void confirmPauseOverlayShown(bool isShown) =>
|
||||
@ -307,6 +344,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public bool PauseOverlayVisible => PauseOverlay.State.Value == Visibility.Visible;
|
||||
|
||||
public void ExitViaPause() => PerformExit(true);
|
||||
|
||||
public void ExitViaQuickExit() => PerformExit(false);
|
||||
|
||||
public override void OnEntering(IScreen last)
|
||||
{
|
||||
base.OnEntering(last);
|
||||
|
@ -1,6 +1,7 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -8,21 +9,17 @@ using osu.Framework.Platform;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Storyboards;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
[HeadlessTest] // we alter unsafe properties on the game host to test inactive window state.
|
||||
public class TestScenePauseWhenInactive : OsuPlayerTestScene
|
||||
{
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||
{
|
||||
var beatmap = (Beatmap)base.CreateBeatmap(ruleset);
|
||||
|
||||
beatmap.HitObjects.RemoveAll(h => h.StartTime < 30000);
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private GameHost host { get; set; }
|
||||
|
||||
@ -33,10 +30,57 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
AddStep("resume player", () => Player.GameplayClockContainer.Start());
|
||||
AddAssert("ensure not paused", () => !Player.GameplayClockContainer.IsPaused.Value);
|
||||
|
||||
AddStep("progress time to gameplay", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.GameplayStartTime));
|
||||
AddUntilStep("wait for pause", () => Player.GameplayClockContainer.IsPaused.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests that if a pause from focus lose is performed while in pause cooldown,
|
||||
/// the player will still pause after the cooldown is finished.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestPauseWhileInCooldown()
|
||||
{
|
||||
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
||||
|
||||
AddStep("resume player", () => Player.GameplayClockContainer.Start());
|
||||
AddStep("skip to gameplay", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.GameplayStartTime));
|
||||
|
||||
AddStep("set inactive", () => ((Bindable<bool>)host.IsActive).Value = false);
|
||||
AddUntilStep("wait for pause", () => Player.GameplayClockContainer.IsPaused.Value);
|
||||
|
||||
AddStep("set active", () => ((Bindable<bool>)host.IsActive).Value = true);
|
||||
|
||||
AddStep("resume player", () => Player.Resume());
|
||||
AddAssert("unpaused", () => !Player.GameplayClockContainer.IsPaused.Value);
|
||||
|
||||
bool pauseCooldownActive = false;
|
||||
|
||||
AddStep("set inactive again", () =>
|
||||
{
|
||||
pauseCooldownActive = Player.PauseCooldownActive;
|
||||
((Bindable<bool>)host.IsActive).Value = false;
|
||||
});
|
||||
AddAssert("pause cooldown active", () => pauseCooldownActive);
|
||||
AddUntilStep("wait for pause", () => Player.GameplayClockContainer.IsPaused.Value);
|
||||
AddAssert("time of pause is after gameplay start time", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= Player.DrawableRuleset.GameplayStartTime);
|
||||
}
|
||||
|
||||
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(true, true, true);
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||
{
|
||||
return new Beatmap
|
||||
{
|
||||
HitObjects = new List<HitObject>
|
||||
{
|
||||
new HitCircle { StartTime = 30000 },
|
||||
new HitCircle { StartTime = 35000 },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||
=> new TestWorkingBeatmap(beatmap, storyboard, Audio);
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Play.PlayerSettings;
|
||||
using osu.Game.Utils;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
@ -48,6 +49,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Cached]
|
||||
private readonly VolumeOverlay volumeOverlay;
|
||||
|
||||
[Cached(typeof(BatteryInfo))]
|
||||
private readonly LocalBatteryInfo batteryInfo = new LocalBatteryInfo();
|
||||
|
||||
private readonly ChangelogOverlay changelogOverlay;
|
||||
|
||||
public TestScenePlayerLoader()
|
||||
@ -288,6 +292,33 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase(false, 1.0, false)] // not charging, above cutoff --> no warning
|
||||
[TestCase(true, 0.1, false)] // charging, below cutoff --> no warning
|
||||
[TestCase(false, 0.25, true)] // not charging, at cutoff --> warning
|
||||
public void TestLowBatteryNotification(bool isCharging, double chargeLevel, bool shouldWarn)
|
||||
{
|
||||
AddStep("reset notification lock", () => sessionStatics.GetBindable<bool>(Static.LowBatteryNotificationShownOnce).Value = false);
|
||||
|
||||
// set charge status and level
|
||||
AddStep("load player", () => resetPlayer(false, () =>
|
||||
{
|
||||
batteryInfo.SetCharging(isCharging);
|
||||
batteryInfo.SetChargeLevel(chargeLevel);
|
||||
}));
|
||||
AddUntilStep("wait for player", () => player?.LoadState == LoadState.Ready);
|
||||
AddAssert($"notification {(shouldWarn ? "triggered" : "not triggered")}", () => notificationOverlay.UnreadCount.Value == (shouldWarn ? 1 : 0));
|
||||
AddStep("click notification", () =>
|
||||
{
|
||||
var scrollContainer = (OsuScrollContainer)notificationOverlay.Children.Last();
|
||||
var flowContainer = scrollContainer.Children.OfType<FillFlowContainer<NotificationSection>>().First();
|
||||
var notification = flowContainer.First();
|
||||
|
||||
InputManager.MoveMouseTo(notification);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddUntilStep("wait for player load", () => player.IsLoaded);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEpilepsyWarningEarlyExit()
|
||||
{
|
||||
@ -321,6 +352,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public override string Name => string.Empty;
|
||||
public override string Acronym => string.Empty;
|
||||
public override double ScoreMultiplier => 1;
|
||||
public override string Description => string.Empty;
|
||||
|
||||
public bool Applied { get; private set; }
|
||||
|
||||
@ -348,5 +380,29 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
throw new TimeoutException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mutable dummy BatteryInfo class for <see cref="TestScenePlayerLoader.TestLowBatteryNotification"/>
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
private class LocalBatteryInfo : BatteryInfo
|
||||
{
|
||||
private bool isCharging = true;
|
||||
private double chargeLevel = 1;
|
||||
|
||||
public override bool IsCharging => isCharging;
|
||||
|
||||
public override double ChargeLevel => chargeLevel;
|
||||
|
||||
public void SetCharging(bool value)
|
||||
{
|
||||
isCharging = value;
|
||||
}
|
||||
|
||||
public void SetChargeLevel(double value)
|
||||
{
|
||||
chargeLevel = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,10 +17,12 @@ using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Legacy;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
@ -129,6 +131,31 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddUntilStep("no DHOs shown", () => !this.ChildrenOfType<DrawableTestHitObject>().Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestApplyHitResultOnKilled()
|
||||
{
|
||||
ManualClock clock = null;
|
||||
bool anyJudged = false;
|
||||
|
||||
void onNewResult(JudgementResult _) => anyJudged = true;
|
||||
|
||||
var beatmap = new Beatmap();
|
||||
beatmap.HitObjects.Add(new TestKilledHitObject { Duration = 20 });
|
||||
|
||||
createTest(beatmap, 10, () => new FramedClock(clock = new ManualClock()));
|
||||
|
||||
AddStep("subscribe to new result", () =>
|
||||
{
|
||||
anyJudged = false;
|
||||
drawableRuleset.NewResult += onNewResult;
|
||||
});
|
||||
AddStep("skip past object", () => clock.CurrentTime = beatmap.HitObjects[0].GetEndTime() + 1000);
|
||||
|
||||
AddAssert("object judged", () => anyJudged);
|
||||
|
||||
AddStep("clean up", () => drawableRuleset.NewResult -= onNewResult);
|
||||
}
|
||||
|
||||
private void createTest(IBeatmap beatmap, int poolSize, Func<IFrameBasedClock> createClock = null) => AddStep("create test", () =>
|
||||
{
|
||||
var ruleset = new TestPoolingRuleset();
|
||||
@ -192,6 +219,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
private void load()
|
||||
{
|
||||
RegisterPool<TestHitObject, DrawableTestHitObject>(poolSize);
|
||||
RegisterPool<TestKilledHitObject, DrawableTestKilledHitObject>(poolSize);
|
||||
}
|
||||
|
||||
protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new TestHitObjectLifetimeEntry(hitObject);
|
||||
@ -220,19 +248,30 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
protected override IEnumerable<TestHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap, CancellationToken cancellationToken)
|
||||
{
|
||||
yield return new TestHitObject
|
||||
switch (original)
|
||||
{
|
||||
StartTime = original.StartTime,
|
||||
Duration = 250
|
||||
};
|
||||
case TestKilledHitObject h:
|
||||
yield return h;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
yield return new TestHitObject
|
||||
{
|
||||
StartTime = original.StartTime,
|
||||
Duration = 250
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region HitObject
|
||||
#region HitObjects
|
||||
|
||||
private class TestHitObject : ConvertHitObject
|
||||
private class TestHitObject : ConvertHitObject, IHasDuration
|
||||
{
|
||||
public double EndTime => StartTime + Duration;
|
||||
|
||||
@ -287,6 +326,30 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
}
|
||||
|
||||
private class TestKilledHitObject : TestHitObject
|
||||
{
|
||||
}
|
||||
|
||||
private class DrawableTestKilledHitObject : DrawableHitObject<TestKilledHitObject>
|
||||
{
|
||||
public DrawableTestKilledHitObject()
|
||||
: base(null)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||
{
|
||||
base.UpdateHitStateTransforms(state);
|
||||
Expire();
|
||||
}
|
||||
|
||||
public override void OnKilled()
|
||||
{
|
||||
base.OnKilled();
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
var beatmap = Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo, Array.Empty<Mod>());
|
||||
|
||||
return new ScoreAccessibleReplayPlayer(ruleset.GetAutoplayMod()?.CreateReplayScore(beatmap));
|
||||
return new ScoreAccessibleReplayPlayer(ruleset.GetAutoplayMod()?.CreateReplayScore(beatmap, Array.Empty<Mod>()));
|
||||
}
|
||||
|
||||
protected override void AddCheckSteps()
|
||||
|
@ -19,6 +19,7 @@ using osu.Game.Replays;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Tests.Visual.UserInterface;
|
||||
using osuTK;
|
||||
@ -53,7 +54,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
|
||||
{
|
||||
Recorder = recorder = new TestReplayRecorder(replay)
|
||||
Recorder = recorder = new TestReplayRecorder(new Score { Replay = replay })
|
||||
{
|
||||
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos),
|
||||
},
|
||||
@ -117,8 +118,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public void TestBasic()
|
||||
{
|
||||
AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
|
||||
AddUntilStep("one frame recorded", () => replay.Frames.Count == 1);
|
||||
AddAssert("position matches", () => playbackManager.ChildrenOfType<Box>().First().Position == recordingManager.ChildrenOfType<Box>().First().Position);
|
||||
AddUntilStep("at least one frame recorded", () => replay.Frames.Count > 0);
|
||||
AddUntilStep("position matches", () => playbackManager.ChildrenOfType<Box>().First().Position == recordingManager.ChildrenOfType<Box>().First().Position);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -138,14 +139,16 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public void TestLimitedFrameRate()
|
||||
{
|
||||
ScheduledDelegate moveFunction = null;
|
||||
int initialFrameCount = 0;
|
||||
|
||||
AddStep("lower rate", () => recorder.RecordFrameRate = 2);
|
||||
AddStep("count frames", () => initialFrameCount = replay.Frames.Count);
|
||||
AddStep("move to center", () => InputManager.MoveMouseTo(recordingManager.ScreenSpaceDrawQuad.Centre));
|
||||
AddStep("much move", () => moveFunction = Scheduler.AddDelayed(() =>
|
||||
InputManager.MoveMouseTo(InputManager.CurrentState.Mouse.Position + new Vector2(-1, 0)), 10, true));
|
||||
AddWaitStep("move", 10);
|
||||
AddStep("stop move", () => moveFunction.Cancel());
|
||||
AddAssert("less than 10 frames recorded", () => replay.Frames.Count < 10);
|
||||
AddAssert("less than 10 frames recorded", () => replay.Frames.Count - initialFrameCount < 10);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -243,7 +246,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
|
||||
{
|
||||
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
|
||||
public override IEnumerable<IKeyBinding> DefaultKeyBindings => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.MouseLeft, TestAction.Down),
|
||||
};
|
||||
@ -271,7 +274,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
internal class TestReplayRecorder : ReplayRecorder<TestAction>
|
||||
{
|
||||
public TestReplayRecorder(Replay target)
|
||||
public TestReplayRecorder(Score target)
|
||||
: base(target)
|
||||
{
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ using osu.Game.Replays;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Tests.Visual.UserInterface;
|
||||
using osuTK;
|
||||
@ -44,7 +45,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
|
||||
{
|
||||
Recorder = new TestReplayRecorder(replay)
|
||||
Recorder = new TestReplayRecorder(new Score { Replay = replay })
|
||||
{
|
||||
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos)
|
||||
},
|
||||
@ -178,7 +179,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
internal class TestKeyBindingContainer : KeyBindingContainer<TestAction>
|
||||
{
|
||||
public override IEnumerable<KeyBinding> DefaultKeyBindings => new[]
|
||||
public override IEnumerable<IKeyBinding> DefaultKeyBindings => new[]
|
||||
{
|
||||
new KeyBinding(InputKey.MouseLeft, TestAction.Down),
|
||||
};
|
||||
@ -206,7 +207,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
internal class TestReplayRecorder : ReplayRecorder<TestAction>
|
||||
{
|
||||
public TestReplayRecorder(Replay target)
|
||||
public TestReplayRecorder(Score target)
|
||||
: base(target)
|
||||
{
|
||||
}
|
||||
|
45
osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs
Normal file
45
osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs
Normal file
@ -0,0 +1,45 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Skinning.Editor;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneSkinEditor : PlayerTestScene
|
||||
{
|
||||
private SkinEditor skinEditor;
|
||||
|
||||
[Resolved]
|
||||
private SkinManager skinManager { get; set; }
|
||||
|
||||
protected override bool Autoplay => true;
|
||||
|
||||
[SetUpSteps]
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("reload skin editor", () =>
|
||||
{
|
||||
skinEditor?.Expire();
|
||||
Player.ScaleTo(SkinEditorOverlay.VISIBLE_TARGET_SCALE);
|
||||
LoadComponentAsync(skinEditor = new SkinEditor(Player), Add);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToggleEditor()
|
||||
{
|
||||
AddToggleStep("toggle editor visibility", visible => skinEditor.ToggleVisibility());
|
||||
}
|
||||
|
||||
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Skinning.Editor;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneSkinEditorComponentsList : SkinnableTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestToggleEditor()
|
||||
{
|
||||
AddStep("show available components", () => SetContents(() => new SkinComponentToolbox(300)
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
}));
|
||||
}
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Skinning.Editor;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneSkinEditorMultipleSkins : SkinnableTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly ScoreProcessor scoreProcessor = new ScoreProcessor();
|
||||
|
||||
[Cached(typeof(HealthProcessor))]
|
||||
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("create editor overlay", () =>
|
||||
{
|
||||
SetContents(() =>
|
||||
{
|
||||
var ruleset = new OsuRuleset();
|
||||
var mods = new[] { ruleset.GetAutoplayMod() };
|
||||
var working = CreateWorkingBeatmap(ruleset.RulesetInfo);
|
||||
var beatmap = working.GetPlayableBeatmap(ruleset.RulesetInfo, mods);
|
||||
|
||||
var drawableRuleset = ruleset.CreateDrawableRulesetWith(beatmap, mods);
|
||||
|
||||
var hudOverlay = new HUDOverlay(drawableRuleset, mods)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
|
||||
// Add any key just to display the key counter visually.
|
||||
hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space));
|
||||
scoreProcessor.Combo.Value = 1;
|
||||
|
||||
return new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
drawableRuleset,
|
||||
hudOverlay,
|
||||
new SkinEditor(hudOverlay),
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
}
|
||||
}
|
@ -1,49 +1,36 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneSkinnableAccuracyCounter : SkinnableTestScene
|
||||
{
|
||||
private IEnumerable<SkinnableAccuracyCounter> accuracyCounters => CreatedDrawables.OfType<SkinnableAccuracyCounter>();
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
|
||||
[Cached]
|
||||
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("Create combo counters", () => SetContents(() =>
|
||||
{
|
||||
var accuracyCounter = new SkinnableAccuracyCounter();
|
||||
|
||||
accuracyCounter.Current.Value = 1;
|
||||
|
||||
return accuracyCounter;
|
||||
}));
|
||||
AddStep("Set initial accuracy", () => scoreProcessor.Accuracy.Value = 1);
|
||||
AddStep("Create accuracy counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.AccuracyCounter))));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangingAccuracy()
|
||||
{
|
||||
AddStep(@"Reset all", delegate
|
||||
{
|
||||
foreach (var s in accuracyCounters)
|
||||
s.Current.Value = 1;
|
||||
});
|
||||
AddStep(@"Reset all", () => scoreProcessor.Accuracy.Value = 1);
|
||||
|
||||
AddStep(@"Hit! :D", delegate
|
||||
{
|
||||
foreach (var s in accuracyCounters)
|
||||
s.Current.Value -= 0.023f;
|
||||
});
|
||||
AddStep(@"Miss :(", () => scoreProcessor.Accuracy.Value -= 0.023);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -298,7 +298,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => throw new NotImplementedException();
|
||||
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
}
|
||||
@ -309,7 +309,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => throw new NotImplementedException();
|
||||
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
}
|
||||
@ -321,7 +321,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => throw new NotImplementedException();
|
||||
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
|
||||
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
|
||||
|
||||
|
@ -14,6 +14,7 @@ using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -23,6 +24,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
private HUDOverlay hudOverlay;
|
||||
|
||||
[Cached]
|
||||
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
||||
|
||||
[Cached(typeof(HealthProcessor))]
|
||||
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
||||
|
||||
private IEnumerable<HUDOverlay> hudOverlays => CreatedDrawables.OfType<HUDOverlay>();
|
||||
|
||||
// best way to check without exposing.
|
||||
@ -37,17 +44,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
createNew();
|
||||
|
||||
AddRepeatStep("increase combo", () =>
|
||||
{
|
||||
foreach (var hud in hudOverlays)
|
||||
hud.ComboCounter.Current.Value++;
|
||||
}, 10);
|
||||
AddRepeatStep("increase combo", () => scoreProcessor.Combo.Value++, 10);
|
||||
|
||||
AddStep("reset combo", () =>
|
||||
{
|
||||
foreach (var hud in hudOverlays)
|
||||
hud.ComboCounter.Current.Value = 0;
|
||||
});
|
||||
AddStep("reset combo", () => scoreProcessor.Combo.Value = 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -80,13 +79,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
SetContents(() =>
|
||||
{
|
||||
hudOverlay = new HUDOverlay(null, null, null, Array.Empty<Mod>());
|
||||
hudOverlay = new HUDOverlay(null, Array.Empty<Mod>());
|
||||
|
||||
// Add any key just to display the key counter visually.
|
||||
hudOverlay.KeyCounter.Add(new KeyCounterKeyboard(Key.Space));
|
||||
|
||||
hudOverlay.ComboCounter.Current.Value = 1;
|
||||
|
||||
action?.Invoke(hudOverlay);
|
||||
|
||||
return hudOverlay;
|
||||
|
@ -1,35 +1,33 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Judgements;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneSkinnableHealthDisplay : SkinnableTestScene
|
||||
{
|
||||
private IEnumerable<SkinnableHealthDisplay> healthDisplays => CreatedDrawables.OfType<SkinnableHealthDisplay>();
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
|
||||
[Cached(typeof(HealthProcessor))]
|
||||
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("Create health displays", () =>
|
||||
{
|
||||
SetContents(() => new SkinnableHealthDisplay());
|
||||
});
|
||||
AddStep("Create health displays", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.HealthDisplay))));
|
||||
AddStep(@"Reset all", delegate
|
||||
{
|
||||
foreach (var s in healthDisplays)
|
||||
s.Current.Value = 1;
|
||||
healthProcessor.Health.Value = 1;
|
||||
});
|
||||
}
|
||||
|
||||
@ -38,23 +36,21 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
AddRepeatStep(@"decrease hp", delegate
|
||||
{
|
||||
foreach (var healthDisplay in healthDisplays)
|
||||
healthDisplay.Current.Value -= 0.08f;
|
||||
healthProcessor.Health.Value -= 0.08f;
|
||||
}, 10);
|
||||
|
||||
AddRepeatStep(@"increase hp without flash", delegate
|
||||
{
|
||||
foreach (var healthDisplay in healthDisplays)
|
||||
healthDisplay.Current.Value += 0.1f;
|
||||
healthProcessor.Health.Value += 0.1f;
|
||||
}, 3);
|
||||
|
||||
AddRepeatStep(@"increase hp with flash", delegate
|
||||
{
|
||||
foreach (var healthDisplay in healthDisplays)
|
||||
healthProcessor.Health.Value += 0.1f;
|
||||
healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement())
|
||||
{
|
||||
healthDisplay.Current.Value += 0.1f;
|
||||
healthDisplay.Flash(new JudgementResult(null, new OsuJudgement()));
|
||||
}
|
||||
Type = HitResult.Perfect
|
||||
});
|
||||
}, 3);
|
||||
}
|
||||
}
|
||||
|
@ -1,54 +1,41 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneSkinnableScoreCounter : SkinnableTestScene
|
||||
{
|
||||
private IEnumerable<SkinnableScoreCounter> scoreCounters => CreatedDrawables.OfType<SkinnableScoreCounter>();
|
||||
|
||||
protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset();
|
||||
|
||||
[Cached]
|
||||
private ScoreProcessor scoreProcessor = new ScoreProcessor();
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("Create combo counters", () => SetContents(() =>
|
||||
{
|
||||
var comboCounter = new SkinnableScoreCounter();
|
||||
comboCounter.Current.Value = 1;
|
||||
return comboCounter;
|
||||
}));
|
||||
AddStep("Create score counters", () => SetContents(() => new SkinnableDrawable(new HUDSkinComponent(HUDSkinComponents.ScoreCounter))));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestScoreCounterIncrementing()
|
||||
{
|
||||
AddStep(@"Reset all", delegate
|
||||
{
|
||||
foreach (var s in scoreCounters)
|
||||
s.Current.Value = 0;
|
||||
});
|
||||
AddStep(@"Reset all", () => scoreProcessor.TotalScore.Value = 0);
|
||||
|
||||
AddStep(@"Hit! :D", delegate
|
||||
{
|
||||
foreach (var s in scoreCounters)
|
||||
s.Current.Value += 300;
|
||||
});
|
||||
AddStep(@"Hit! :D", () => scoreProcessor.TotalScore.Value += 300);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestVeryLargeScore()
|
||||
{
|
||||
AddStep("set large score", () => scoreCounters.ForEach(counter => counter.Current.Value = 1_000_000_000));
|
||||
AddStep("set large score", () => scoreProcessor.TotalScore.Value = 1_000_000_000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,70 +43,60 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestStoppedSoundDoesntResumeAfterPause()
|
||||
{
|
||||
DrawableSample sample = null;
|
||||
AddStep("start sample with looping", () =>
|
||||
{
|
||||
sample = skinnableSound.ChildrenOfType<DrawableSample>().First();
|
||||
|
||||
skinnableSound.Looping = true;
|
||||
skinnableSound.Play();
|
||||
});
|
||||
|
||||
AddUntilStep("wait for sample to start playing", () => sample.Playing);
|
||||
AddUntilStep("wait for sample to start playing", () => skinnableSound.IsPlaying);
|
||||
|
||||
AddStep("stop sample", () => skinnableSound.Stop());
|
||||
|
||||
AddUntilStep("wait for sample to stop playing", () => !sample.Playing);
|
||||
AddUntilStep("wait for sample to stop playing", () => !skinnableSound.IsPlaying);
|
||||
|
||||
AddStep("disable sample playback", () => skinSource.SamplePlaybackDisabled.Value = true);
|
||||
|
||||
AddStep("enable sample playback", () => skinSource.SamplePlaybackDisabled.Value = false);
|
||||
|
||||
AddWaitStep("wait a bit", 5);
|
||||
AddAssert("sample not playing", () => !sample.Playing);
|
||||
AddAssert("sample not playing", () => !skinnableSound.IsPlaying);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLoopingSoundResumesAfterPause()
|
||||
{
|
||||
DrawableSample sample = null;
|
||||
AddStep("start sample with looping", () =>
|
||||
{
|
||||
skinnableSound.Looping = true;
|
||||
skinnableSound.Play();
|
||||
sample = skinnableSound.ChildrenOfType<DrawableSample>().First();
|
||||
});
|
||||
|
||||
AddUntilStep("wait for sample to start playing", () => sample.Playing);
|
||||
AddUntilStep("wait for sample to start playing", () => skinnableSound.IsPlaying);
|
||||
|
||||
AddStep("disable sample playback", () => skinSource.SamplePlaybackDisabled.Value = true);
|
||||
AddUntilStep("wait for sample to stop playing", () => !sample.Playing);
|
||||
AddUntilStep("wait for sample to stop playing", () => !skinnableSound.IsPlaying);
|
||||
|
||||
AddStep("enable sample playback", () => skinSource.SamplePlaybackDisabled.Value = false);
|
||||
AddUntilStep("wait for sample to start playing", () => sample.Playing);
|
||||
AddUntilStep("wait for sample to start playing", () => skinnableSound.IsPlaying);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNonLoopingStopsWithPause()
|
||||
{
|
||||
DrawableSample sample = null;
|
||||
AddStep("start sample", () =>
|
||||
{
|
||||
skinnableSound.Play();
|
||||
sample = skinnableSound.ChildrenOfType<DrawableSample>().First();
|
||||
});
|
||||
AddStep("start sample", () => skinnableSound.Play());
|
||||
|
||||
AddAssert("sample playing", () => sample.Playing);
|
||||
AddAssert("sample playing", () => skinnableSound.IsPlaying);
|
||||
|
||||
AddStep("disable sample playback", () => skinSource.SamplePlaybackDisabled.Value = true);
|
||||
|
||||
AddUntilStep("sample not playing", () => !sample.Playing);
|
||||
AddUntilStep("sample not playing", () => !skinnableSound.IsPlaying);
|
||||
|
||||
AddStep("enable sample playback", () => skinSource.SamplePlaybackDisabled.Value = false);
|
||||
|
||||
AddAssert("sample not playing", () => !sample.Playing);
|
||||
AddAssert("sample not playing", () => !sample.Playing);
|
||||
AddAssert("sample not playing", () => !sample.Playing);
|
||||
AddAssert("sample not playing", () => !skinnableSound.IsPlaying);
|
||||
AddAssert("sample not playing", () => !skinnableSound.IsPlaying);
|
||||
AddAssert("sample not playing", () => !skinnableSound.IsPlaying);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -119,10 +109,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
sample = skinnableSound.ChildrenOfType<DrawableSample>().Single();
|
||||
});
|
||||
|
||||
AddAssert("sample playing", () => sample.Playing);
|
||||
AddAssert("sample playing", () => skinnableSound.IsPlaying);
|
||||
|
||||
AddStep("disable sample playback", () => skinSource.SamplePlaybackDisabled.Value = true);
|
||||
AddUntilStep("wait for sample to stop playing", () => !sample.Playing);
|
||||
AddUntilStep("wait for sample to stop playing", () => !skinnableSound.IsPlaying);
|
||||
|
||||
AddStep("trigger skin change", () => skinSource.TriggerSourceChanged());
|
||||
|
||||
@ -133,11 +123,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
return sample != oldSample;
|
||||
});
|
||||
|
||||
AddAssert("new sample stopped", () => !sample.Playing);
|
||||
AddAssert("new sample stopped", () => !skinnableSound.IsPlaying);
|
||||
AddStep("enable sample playback", () => skinSource.SamplePlaybackDisabled.Value = false);
|
||||
|
||||
AddWaitStep("wait a bit", 5);
|
||||
AddAssert("new sample not played", () => !sample.Playing);
|
||||
AddAssert("new sample not played", () => !skinnableSound.IsPlaying);
|
||||
}
|
||||
|
||||
[Cached(typeof(ISkinSource))]
|
||||
@ -155,7 +145,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
public Drawable GetDrawableComponent(ISkinComponent component) => source?.GetDrawableComponent(component);
|
||||
public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => source?.GetTexture(componentName, wrapModeS, wrapModeT);
|
||||
public SampleChannel GetSample(ISampleInfo sampleInfo) => source?.GetSample(sampleInfo);
|
||||
public ISample GetSample(ISampleInfo sampleInfo) => source?.GetSample(sampleInfo);
|
||||
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => source?.GetConfig<TLookup, TValue>(lookup);
|
||||
|
||||
public void TriggerSourceChanged()
|
||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
var working = CreateWorkingBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo));
|
||||
working.LoadTrack();
|
||||
|
||||
Child = gameplayClockContainer = new GameplayClockContainer(working, 0)
|
||||
Child = gameplayClockContainer = new MasterGameplayClockContainer(working, 0)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("move mouse", () => InputManager.MoveMouseTo(skip.ScreenSpaceDrawQuad.Centre));
|
||||
AddStep("click", () =>
|
||||
{
|
||||
increment = skip_time - gameplayClock.CurrentTime - GameplayClockContainer.MINIMUM_SKIP_TIME / 2;
|
||||
increment = skip_time - gameplayClock.CurrentTime - MasterGameplayClockContainer.MINIMUM_SKIP_TIME / 2;
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddStep("click", () => InputManager.Click(MouseButton.Left));
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user