Merge remote-tracking branch 'refs/remotes/upstream/master' into add-access-to-skin-legacy-version

This commit is contained in:
iiSaLMaN
2019-11-20 01:10:32 +03:00
472 changed files with 13059 additions and 5964 deletions

View File

@ -167,9 +167,9 @@ namespace osu.Game.Tests.Beatmaps.Formats
var controlPoints = beatmap.ControlPointInfo;
Assert.AreEqual(4, controlPoints.TimingPoints.Count);
Assert.AreEqual(42, controlPoints.DifficultyPoints.Count);
Assert.AreEqual(42, controlPoints.SamplePoints.Count);
Assert.AreEqual(42, controlPoints.EffectPoints.Count);
Assert.AreEqual(5, controlPoints.DifficultyPoints.Count);
Assert.AreEqual(34, controlPoints.SamplePoints.Count);
Assert.AreEqual(8, controlPoints.EffectPoints.Count);
var timingPoint = controlPoints.TimingPointAt(0);
Assert.AreEqual(956, timingPoint.Time);
@ -191,7 +191,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(1.0, difficultyPoint.SpeedMultiplier);
difficultyPoint = controlPoints.DifficultyPointAt(48428);
Assert.AreEqual(48428, difficultyPoint.Time);
Assert.AreEqual(0, difficultyPoint.Time);
Assert.AreEqual(1.0, difficultyPoint.SpeedMultiplier);
difficultyPoint = controlPoints.DifficultyPointAt(116999);
@ -224,7 +224,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsFalse(effectPoint.OmitFirstBarLine);
effectPoint = controlPoints.EffectPointAt(119637);
Assert.AreEqual(119637, effectPoint.Time);
Assert.AreEqual(95901, effectPoint.Time);
Assert.IsFalse(effectPoint.KiaiMode);
Assert.IsFalse(effectPoint.OmitFirstBarLine);
}
@ -262,6 +262,21 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
}
[Test]
public void TestTimingPointResetsSpeedMultiplier()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = TestResources.OpenResource("timingpoint-speedmultiplier-reset.osu"))
using (var stream = new LineBufferedReader(resStream))
{
var controlPoints = decoder.Decode(stream).ControlPointInfo;
Assert.That(controlPoints.DifficultyPointAt(0).SpeedMultiplier, Is.EqualTo(0.5).Within(0.1));
Assert.That(controlPoints.DifficultyPointAt(2000).SpeedMultiplier, Is.EqualTo(1).Within(0.1));
}
}
[Test]
public void TestDecodeBeatmapColours()
{
@ -362,6 +377,23 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
}
[Test]
public void TestDecodeControlPointDifficultyChange()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = TestResources.OpenResource("controlpoint-difficulty-multiplier.osu"))
using (var stream = new LineBufferedReader(resStream))
{
var controlPointInfo = decoder.Decode(stream).ControlPointInfo;
Assert.That(controlPointInfo.DifficultyPointAt(5).SpeedMultiplier, Is.EqualTo(1));
Assert.That(controlPointInfo.DifficultyPointAt(1000).SpeedMultiplier, Is.EqualTo(10));
Assert.That(controlPointInfo.DifficultyPointAt(2000).SpeedMultiplier, Is.EqualTo(1.8518518518518519d));
Assert.That(controlPointInfo.DifficultyPointAt(3000).SpeedMultiplier, Is.EqualTo(0.5));
}
}
[Test]
public void TestDecodeControlPointCustomSampleBank()
{

View File

@ -29,7 +29,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportWhenClosed()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenClosed"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWhenClosed)))
{
try
{
@ -46,7 +46,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportThenDelete()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDelete"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenDelete)))
{
try
{
@ -67,7 +67,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportThenImport()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImport"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImport)))
{
try
{
@ -94,7 +94,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportCorruptThenImport()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImport"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportCorruptThenImport)))
{
try
{
@ -136,7 +136,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestRollbackOnFailure()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestRollbackOnFailure"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestRollbackOnFailure)))
{
try
{
@ -213,7 +213,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportThenImportDifferentHash()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImportDifferentHash"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportDifferentHash)))
{
try
{
@ -244,7 +244,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportThenDeleteThenImport()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDeleteThenImport"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenDeleteThenImport)))
{
try
{
@ -272,7 +272,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportThenDeleteThenImportWithOnlineIDMismatch(bool set)
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"TestImportThenDeleteThenImport-{set}"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"{nameof(TestImportThenDeleteThenImportWithOnlineIDMismatch)}-{set}"))
{
try
{
@ -306,7 +306,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportWithDuplicateBeatmapIDs()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWithDuplicateBeatmapID"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWithDuplicateBeatmapIDs)))
{
try
{
@ -392,7 +392,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public async Task TestImportWhenFileOpen()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenFileOpen"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWhenFileOpen)))
{
try
{
@ -411,10 +411,52 @@ namespace osu.Game.Tests.Beatmaps.IO
}
}
[Test]
public async Task TestImportWithDuplicateHashes()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportNestedStructure)))
{
try
{
var osu = loadOsu(host);
var temp = TestResources.GetTestBeatmapForImport();
string extractedFolder = $"{temp}_extracted";
Directory.CreateDirectory(extractedFolder);
try
{
using (var zip = ZipArchive.Open(temp))
zip.WriteToDirectory(extractedFolder);
using (var zip = ZipArchive.Create())
{
zip.AddAllFromDirectory(extractedFolder);
zip.AddEntry("duplicate.osu", Directory.GetFiles(extractedFolder, "*.osu").First());
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
}
await osu.Dependencies.Get<BeatmapManager>().Import(temp);
ensureLoaded(osu);
}
finally
{
Directory.Delete(extractedFolder, true);
}
}
finally
{
host.Exit();
}
}
}
[Test]
public async Task TestImportNestedStructure()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportNestedStructure"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportNestedStructure)))
{
try
{
@ -456,9 +498,63 @@ namespace osu.Game.Tests.Beatmaps.IO
}
}
public static async Task<BeatmapSetInfo> LoadOszIntoOsu(OsuGameBase osu, string path = null)
[Test]
public async Task TestImportWithIgnoredDirectoryInArchive()
{
var temp = path ?? TestResources.GetTestBeatmapForImport();
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWithIgnoredDirectoryInArchive)))
{
try
{
var osu = loadOsu(host);
var temp = TestResources.GetTestBeatmapForImport();
string extractedFolder = $"{temp}_extracted";
string dataFolder = Path.Combine(extractedFolder, "actual_data");
string resourceForkFolder = Path.Combine(extractedFolder, "__MACOSX");
string resourceForkFilePath = Path.Combine(resourceForkFolder, ".extracted");
Directory.CreateDirectory(dataFolder);
Directory.CreateDirectory(resourceForkFolder);
using (var resourceForkFile = File.CreateText(resourceForkFilePath))
{
resourceForkFile.WriteLine("adding content so that it's not empty");
}
try
{
using (var zip = ZipArchive.Open(temp))
zip.WriteToDirectory(dataFolder);
using (var zip = ZipArchive.Create())
{
zip.AddAllFromDirectory(extractedFolder);
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
}
var imported = await osu.Dependencies.Get<BeatmapManager>().Import(temp);
ensureLoaded(osu);
Assert.IsFalse(imported.Files.Any(f => f.Filename.Contains("__MACOSX")), "Files contain resource fork folder, which should be ignored");
Assert.IsFalse(imported.Files.Any(f => f.Filename.Contains("actual_data")), "Files contain common subfolder");
}
finally
{
Directory.Delete(extractedFolder, true);
}
}
finally
{
host.Exit();
}
}
}
public static async Task<BeatmapSetInfo> LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false)
{
var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);
var manager = osu.Dependencies.Get<BeatmapManager>();

View File

@ -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;
using System.IO;
using System.Text;
using NUnit.Framework;
@ -14,9 +15,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public void TestReadLineByLine()
{
const string contents = @"line 1
line 2
line 3";
const string contents = "line 1\rline 2\nline 3";
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
using (var bufferedReader = new LineBufferedReader(stream))
@ -31,9 +30,7 @@ line 3";
[Test]
public void TestPeekLineOnce()
{
const string contents = @"line 1
peek this
line 3";
const string contents = "line 1\r\npeek this\nline 3";
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
using (var bufferedReader = new LineBufferedReader(stream))
@ -49,9 +46,7 @@ line 3";
[Test]
public void TestPeekLineMultipleTimes()
{
const string contents = @"peek this once
line 2
peek this a lot";
const string contents = "peek this once\nline 2\rpeek this a lot";
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
using (var bufferedReader = new LineBufferedReader(stream))
@ -70,8 +65,7 @@ peek this a lot";
[Test]
public void TestPeekLineAtEndOfStream()
{
const string contents = @"first line
second line";
const string contents = "first line\r\nsecond line";
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
using (var bufferedReader = new LineBufferedReader(stream))
@ -100,8 +94,7 @@ second line";
[Test]
public void TestReadToEndNoPeeks()
{
const string contents = @"first line
second line";
const string contents = "first line\r\nsecond line";
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
using (var bufferedReader = new LineBufferedReader(stream))
@ -113,20 +106,19 @@ second line";
[Test]
public void TestReadToEndAfterReadsAndPeeks()
{
const string contents = @"this line is gone
this one shouldn't be
these ones
definitely not";
const string contents = "this line is gone\rthis one shouldn't be\r\nthese ones\ndefinitely not";
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
using (var bufferedReader = new LineBufferedReader(stream))
{
Assert.AreEqual("this line is gone", bufferedReader.ReadLine());
Assert.AreEqual("this one shouldn't be", bufferedReader.PeekLine());
const string ending = @"this one shouldn't be
these ones
definitely not";
Assert.AreEqual(ending, bufferedReader.ReadToEnd());
var endingLines = bufferedReader.ReadToEnd().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
Assert.AreEqual(3, endingLines.Length);
Assert.AreEqual("this one shouldn't be", endingLines[0]);
Assert.AreEqual("these ones", endingLines[1]);
Assert.AreEqual("definitely not", endingLines[2]);
}
}
}

View File

@ -273,6 +273,96 @@ namespace osu.Game.Tests.Chat
Assert.AreEqual(21, result.Links[0].Length);
}
[Test]
public void TestMarkdownFormatLinkWithInlineTitle()
{
Message result = MessageFormatter.FormatMessage(new Message { Content = "I haven't seen [this link format](https://osu.ppy.sh \"osu!\") before..." });
Assert.AreEqual("I haven't seen this link format before...", result.DisplayContent);
Assert.AreEqual(1, result.Links.Count);
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
Assert.AreEqual(15, result.Links[0].Index);
Assert.AreEqual(16, result.Links[0].Length);
}
[Test]
public void TestMarkdownFormatLinkWithInlineTitleAndEscapedQuotes()
{
Message result = MessageFormatter.FormatMessage(new Message { Content = "I haven't seen [this link format](https://osu.ppy.sh \"inner quote \\\" just to confuse \") before..." });
Assert.AreEqual("I haven't seen this link format before...", result.DisplayContent);
Assert.AreEqual(1, result.Links.Count);
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
Assert.AreEqual(15, result.Links[0].Index);
Assert.AreEqual(16, result.Links[0].Length);
}
[Test]
public void TestMarkdownFormatLinkWithUrlInTextAndInlineTitle()
{
Message result = MessageFormatter.FormatMessage(new Message { Content = "I haven't seen [https://osu.ppy.sh](https://osu.ppy.sh \"https://osu.ppy.sh\") before..." });
Assert.AreEqual("I haven't seen https://osu.ppy.sh before...", result.DisplayContent);
Assert.AreEqual(1, result.Links.Count);
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
Assert.AreEqual(15, result.Links[0].Index);
Assert.AreEqual(18, result.Links[0].Length);
}
[Test]
public void TestMarkdownFormatLinkWithUrlAndTextInTitle()
{
Message result = MessageFormatter.FormatMessage(new Message { Content = "I haven't seen [oh no, text here! https://osu.ppy.sh](https://osu.ppy.sh) before..." });
Assert.AreEqual("I haven't seen oh no, text here! https://osu.ppy.sh before...", result.DisplayContent);
Assert.AreEqual(1, result.Links.Count);
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
Assert.AreEqual(15, result.Links[0].Index);
Assert.AreEqual(36, result.Links[0].Length);
}
[Test]
public void TestMarkdownFormatLinkWithMisleadingUrlInText()
{
Message result = MessageFormatter.FormatMessage(new Message { Content = "I haven't seen [https://google.com](https://osu.ppy.sh) before..." });
Assert.AreEqual("I haven't seen https://google.com before...", result.DisplayContent);
Assert.AreEqual(1, result.Links.Count);
Assert.AreEqual("https://osu.ppy.sh", result.Links[0].Url);
Assert.AreEqual(15, result.Links[0].Index);
Assert.AreEqual(18, result.Links[0].Length);
}
[Test]
public void TestMarkdownFormatLinkThatContractsIntoLargerLink()
{
Message result = MessageFormatter.FormatMessage(new Message { Content = "super broken https://[osu.ppy](https://reddit.com).sh/" });
Assert.AreEqual("super broken https://osu.ppy.sh/", result.DisplayContent);
Assert.AreEqual(1, result.Links.Count);
Assert.AreEqual("https://reddit.com", result.Links[0].Url);
Assert.AreEqual(21, result.Links[0].Index);
Assert.AreEqual(7, result.Links[0].Length);
}
[Test]
public void TestMarkdownFormatLinkDirectlyNextToRawLink()
{
// the raw link has a port at the end of it, so that the raw link regex terminates at the port and doesn't consume display text from the formatted one
Message result = MessageFormatter.FormatMessage(new Message { Content = "https://localhost:8080[https://osu.ppy.sh](https://osu.ppy.sh) should be two links" });
Assert.AreEqual("https://localhost:8080https://osu.ppy.sh should be two links", result.DisplayContent);
Assert.AreEqual(2, result.Links.Count);
Assert.AreEqual("https://localhost:8080", result.Links[0].Url);
Assert.AreEqual(0, result.Links[0].Index);
Assert.AreEqual(22, result.Links[0].Length);
Assert.AreEqual("https://osu.ppy.sh", result.Links[1].Url);
Assert.AreEqual(22, result.Links[1].Index);
Assert.AreEqual(18, result.Links[1].Length);
}
[Test]
public void TestChannelLink()
{

View 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 NUnit.Framework;
using osu.Framework.Testing;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Edit;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit;
using osu.Game.Tests.Visual;
namespace osu.Game.Tests.Editor
{
[HeadlessTest]
public class TestSceneHitObjectComposerDistanceSnapping : EditorClockTestScene
{
private TestHitObjectComposer composer;
[SetUp]
public void Setup() => Schedule(() =>
{
Child = composer = new TestHitObjectComposer();
BeatDivisor.Value = 1;
composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 1;
composer.EditorBeatmap.ControlPointInfo.Clear();
composer.EditorBeatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 1 });
composer.EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 });
});
[TestCase(1)]
[TestCase(2)]
public void TestSliderMultiplier(float multiplier)
{
AddStep($"set multiplier = {multiplier}", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = multiplier);
assertSnapDistance(100 * multiplier);
}
[TestCase(1)]
[TestCase(2)]
public void TestSpeedMultiplier(float multiplier)
{
AddStep($"set multiplier = {multiplier}", () =>
{
composer.EditorBeatmap.ControlPointInfo.Clear();
composer.EditorBeatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = multiplier });
});
assertSnapDistance(100 * multiplier);
}
[TestCase(1)]
[TestCase(2)]
public void TestBeatDivisor(int divisor)
{
AddStep($"set divisor = {divisor}", () => BeatDivisor.Value = divisor);
assertSnapDistance(100f / divisor);
}
[Test]
public void TestConvertDurationToDistance()
{
assertDurationToDistance(500, 50);
assertDurationToDistance(1000, 100);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
assertDurationToDistance(500, 100);
assertDurationToDistance(1000, 200);
AddStep("set beat length = 500", () =>
{
composer.EditorBeatmap.ControlPointInfo.Clear();
composer.EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 500 });
});
assertDurationToDistance(500, 200);
assertDurationToDistance(1000, 400);
}
[Test]
public void TestConvertDistanceToDuration()
{
assertDistanceToDuration(50, 500);
assertDistanceToDuration(100, 1000);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
assertDistanceToDuration(100, 500);
assertDistanceToDuration(200, 1000);
AddStep("set beat length = 500", () =>
{
composer.EditorBeatmap.ControlPointInfo.Clear();
composer.EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 500 });
});
assertDistanceToDuration(200, 500);
assertDistanceToDuration(400, 1000);
}
[Test]
public void TestGetSnappedDurationFromDistance()
{
assertSnappedDuration(50, 0);
assertSnappedDuration(100, 1000);
assertSnappedDuration(150, 1000);
assertSnappedDuration(200, 2000);
assertSnappedDuration(250, 2000);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
assertSnappedDuration(50, 0);
assertSnappedDuration(100, 0);
assertSnappedDuration(150, 0);
assertSnappedDuration(200, 1000);
assertSnappedDuration(250, 1000);
AddStep("set beat length = 500", () =>
{
composer.EditorBeatmap.ControlPointInfo.Clear();
composer.EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 500 });
});
assertSnappedDuration(50, 0);
assertSnappedDuration(100, 0);
assertSnappedDuration(150, 0);
assertSnappedDuration(200, 500);
assertSnappedDuration(250, 500);
assertSnappedDuration(400, 1000);
}
[Test]
public void GetSnappedDistanceFromDistance()
{
assertSnappedDistance(50, 0);
assertSnappedDistance(100, 100);
assertSnappedDistance(150, 100);
assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200);
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
assertSnappedDistance(50, 0);
assertSnappedDistance(100, 0);
assertSnappedDistance(150, 0);
assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200);
AddStep("set beat length = 500", () =>
{
composer.EditorBeatmap.ControlPointInfo.Clear();
composer.EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 500 });
});
assertSnappedDistance(50, 0);
assertSnappedDistance(100, 0);
assertSnappedDistance(150, 0);
assertSnappedDistance(200, 200);
assertSnappedDistance(250, 200);
assertSnappedDistance(400, 400);
}
private void assertSnapDistance(float expectedDistance)
=> AddAssert($"distance is {expectedDistance}", () => composer.GetBeatSnapDistanceAt(0) == expectedDistance);
private void assertDurationToDistance(double duration, float expectedDistance)
=> AddAssert($"duration = {duration} -> distance = {expectedDistance}", () => composer.DurationToDistance(0, duration) == expectedDistance);
private void assertDistanceToDuration(float distance, double expectedDuration)
=> AddAssert($"distance = {distance} -> duration = {expectedDuration}", () => composer.DistanceToDuration(0, distance) == expectedDuration);
private void assertSnappedDuration(float distance, double expectedDuration)
=> AddAssert($"distance = {distance} -> duration = {expectedDuration} (snapped)", () => composer.GetSnappedDurationFromDistance(0, distance) == expectedDuration);
private void assertSnappedDistance(float distance, float expectedDistance)
=> AddAssert($"distance = {distance} -> distance = {expectedDistance} (snapped)", () => composer.GetSnappedDistanceFromDistance(0, distance) == expectedDistance);
private class TestHitObjectComposer : OsuHitObjectComposer
{
public new EditorBeatmap<OsuHitObject> EditorBeatmap => base.EditorBeatmap;
public TestHitObjectComposer()
: base(new OsuRuleset())
{
}
}
}
}

View 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 System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Textures;
using osu.Framework.Testing;
using osu.Game.Audio;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Skinning;
using osu.Game.Tests.Visual;
using osuTK.Graphics;
namespace osu.Game.Tests.Gameplay
{
[HeadlessTest]
public class TestSceneHitObjectAccentColour : OsuTestScene
{
private Container skinContainer;
[SetUp]
public void Setup() => Schedule(() => Child = skinContainer = new SkinProvidingContainer(new TestSkin()));
[Test]
public void TestChangeComboIndexBeforeLoad()
{
TestDrawableHitObject hitObject = null;
AddStep("set combo and add hitobject", () =>
{
hitObject = new TestDrawableHitObject();
hitObject.HitObject.ComboIndex = 1;
skinContainer.Add(hitObject);
});
AddAssert("combo colour is green", () => hitObject.AccentColour.Value == Color4.Green);
}
[Test]
public void TestChangeComboIndexDuringLoad()
{
TestDrawableHitObject hitObject = null;
AddStep("add hitobject and set combo", () =>
{
skinContainer.Add(hitObject = new TestDrawableHitObject());
hitObject.HitObject.ComboIndex = 1;
});
AddAssert("combo colour is green", () => hitObject.AccentColour.Value == Color4.Green);
}
[Test]
public void TestChangeComboIndexAfterLoad()
{
TestDrawableHitObject hitObject = null;
AddStep("add hitobject", () => skinContainer.Add(hitObject = new TestDrawableHitObject()));
AddAssert("combo colour is red", () => hitObject.AccentColour.Value == Color4.Red);
AddStep("change combo", () => hitObject.HitObject.ComboIndex = 1);
AddAssert("combo colour is green", () => hitObject.AccentColour.Value == Color4.Green);
}
private class TestDrawableHitObject : DrawableHitObject<TestHitObjectWithCombo>
{
public TestDrawableHitObject()
: base(new TestHitObjectWithCombo())
{
}
}
private class TestHitObjectWithCombo : HitObject, IHasComboInformation
{
public bool NewCombo { get; } = false;
public int ComboOffset { get; } = 0;
public Bindable<int> IndexInCurrentComboBindable { get; } = new Bindable<int>();
public int IndexInCurrentCombo
{
get => IndexInCurrentComboBindable.Value;
set => IndexInCurrentComboBindable.Value = value;
}
public Bindable<int> ComboIndexBindable { get; } = new Bindable<int>();
public int ComboIndex
{
get => ComboIndexBindable.Value;
set => ComboIndexBindable.Value = value;
}
public Bindable<bool> LastInComboBindable { get; } = new Bindable<bool>();
public bool LastInCombo
{
get => LastInComboBindable.Value;
set => LastInComboBindable.Value = value;
}
}
private class TestSkin : ISkin
{
public readonly List<Color4> ComboColours = new List<Color4>
{
Color4.Red,
Color4.Green
};
public Drawable GetDrawableComponent(ISkinComponent component) => throw new NotImplementedException();
public Texture GetTexture(string componentName) => throw new NotImplementedException();
public SampleChannel GetSample(ISampleInfo sampleInfo) => throw new NotImplementedException();
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
{
switch (lookup)
{
case GlobalSkinConfiguration global:
switch (global)
{
case GlobalSkinConfiguration.ComboColours:
return SkinUtils.As<TValue>(new Bindable<List<Color4>>(ComboColours));
}
break;
}
throw new NotImplementedException();
}
}
}
}

View File

@ -0,0 +1,48 @@
// 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.Beatmaps;
namespace osu.Game.Tests.NonVisual
{
[TestFixture]
public class BeatmapSetInfoEqualityTest
{
[Test]
public void TestOnlineWithOnline()
{
var ourInfo = new BeatmapSetInfo { OnlineBeatmapSetID = 123 };
var otherInfo = new BeatmapSetInfo { OnlineBeatmapSetID = 123 };
Assert.AreEqual(ourInfo, otherInfo);
}
[Test]
public void TestDatabasedWithDatabased()
{
var ourInfo = new BeatmapSetInfo { ID = 123 };
var otherInfo = new BeatmapSetInfo { ID = 123 };
Assert.AreEqual(ourInfo, otherInfo);
}
[Test]
public void TestDatabasedWithOnline()
{
var ourInfo = new BeatmapSetInfo { ID = 123, OnlineBeatmapSetID = 12 };
var otherInfo = new BeatmapSetInfo { OnlineBeatmapSetID = 12 };
Assert.AreEqual(ourInfo, otherInfo);
}
[Test]
public void TestCheckNullID()
{
var ourInfo = new BeatmapSetInfo { Status = BeatmapSetOnlineStatus.Loved };
var otherInfo = new BeatmapSetInfo { Status = BeatmapSetOnlineStatus.Approved };
Assert.AreNotEqual(ourInfo, otherInfo);
}
}
}

View File

@ -0,0 +1,227 @@
// 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.Game.Beatmaps.ControlPoints;
namespace osu.Game.Tests.NonVisual
{
[TestFixture]
public class ControlPointInfoTest
{
[Test]
public void TestAdd()
{
var cpi = new ControlPointInfo();
cpi.Add(0, new TimingControlPoint());
cpi.Add(1000, new TimingControlPoint { BeatLength = 500 });
Assert.That(cpi.Groups.Count, Is.EqualTo(2));
Assert.That(cpi.TimingPoints.Count, Is.EqualTo(2));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(2));
}
[Test]
public void TestAddRedundantTiming()
{
var cpi = new ControlPointInfo();
cpi.Add(0, new TimingControlPoint()); // is *not* redundant, special exception for first timing point.
cpi.Add(1000, new TimingControlPoint()); // is redundant
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
Assert.That(cpi.TimingPoints.Count, Is.EqualTo(1));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(1));
}
[Test]
public void TestAddRedundantDifficulty()
{
var cpi = new ControlPointInfo();
cpi.Add(0, new DifficultyControlPoint()); // is redundant
cpi.Add(1000, new DifficultyControlPoint()); // is redundant
Assert.That(cpi.Groups.Count, Is.EqualTo(0));
Assert.That(cpi.TimingPoints.Count, Is.EqualTo(0));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(0));
cpi.Add(1000, new DifficultyControlPoint { SpeedMultiplier = 2 }); // is not redundant
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(1));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(1));
}
[Test]
public void TestAddRedundantSample()
{
var cpi = new ControlPointInfo();
cpi.Add(0, new SampleControlPoint()); // is redundant
cpi.Add(1000, new SampleControlPoint()); // is redundant
Assert.That(cpi.Groups.Count, Is.EqualTo(0));
Assert.That(cpi.TimingPoints.Count, Is.EqualTo(0));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(0));
cpi.Add(1000, new SampleControlPoint { SampleVolume = 50 }); // is not redundant
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
Assert.That(cpi.SamplePoints.Count, Is.EqualTo(1));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(1));
}
[Test]
public void TestAddRedundantEffect()
{
var cpi = new ControlPointInfo();
cpi.Add(0, new EffectControlPoint()); // is redundant
cpi.Add(1000, new EffectControlPoint()); // is redundant
Assert.That(cpi.Groups.Count, Is.EqualTo(0));
Assert.That(cpi.TimingPoints.Count, Is.EqualTo(0));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(0));
cpi.Add(1000, new EffectControlPoint { KiaiMode = true }); // is not redundant
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
Assert.That(cpi.EffectPoints.Count, Is.EqualTo(1));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(1));
}
[Test]
public void TestAddGroup()
{
var cpi = new ControlPointInfo();
var group = cpi.GroupAt(1000, true);
var group2 = cpi.GroupAt(1000, true);
Assert.That(group, Is.EqualTo(group2));
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
}
[Test]
public void TestGroupAtLookupOnly()
{
var cpi = new ControlPointInfo();
var group = cpi.GroupAt(5000, true);
Assert.That(group, Is.Not.Null);
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
Assert.That(cpi.GroupAt(1000), Is.Null);
Assert.That(cpi.GroupAt(5000), Is.Not.Null);
}
[Test]
public void TestAddRemoveGroup()
{
var cpi = new ControlPointInfo();
var group = cpi.GroupAt(1000, true);
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
cpi.RemoveGroup(group);
Assert.That(cpi.Groups.Count, Is.EqualTo(0));
}
[Test]
public void TestAddControlPointToGroup()
{
var cpi = new ControlPointInfo();
var group = cpi.GroupAt(1000, true);
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
// usually redundant, but adding to group forces it to be added
group.Add(new DifficultyControlPoint());
Assert.That(group.ControlPoints.Count, Is.EqualTo(1));
Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(1));
}
[Test]
public void TestAddDuplicateControlPointToGroup()
{
var cpi = new ControlPointInfo();
var group = cpi.GroupAt(1000, true);
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
group.Add(new DifficultyControlPoint());
group.Add(new DifficultyControlPoint { SpeedMultiplier = 2 });
Assert.That(group.ControlPoints.Count, Is.EqualTo(1));
Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(1));
Assert.That(cpi.DifficultyPoints.First().SpeedMultiplier, Is.EqualTo(2));
}
[Test]
public void TestRemoveControlPointFromGroup()
{
var cpi = new ControlPointInfo();
var group = cpi.GroupAt(1000, true);
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
var difficultyPoint = new DifficultyControlPoint();
group.Add(difficultyPoint);
group.Remove(difficultyPoint);
Assert.That(group.ControlPoints.Count, Is.EqualTo(0));
Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(0));
Assert.That(cpi.AllControlPoints.Count, Is.EqualTo(0));
}
[Test]
public void TestOrdering()
{
var cpi = new ControlPointInfo();
cpi.Add(0, new TimingControlPoint());
cpi.Add(1000, new TimingControlPoint { BeatLength = 500 });
cpi.Add(10000, new TimingControlPoint { BeatLength = 200 });
cpi.Add(5000, new TimingControlPoint { BeatLength = 100 });
cpi.Add(3000, new DifficultyControlPoint { SpeedMultiplier = 2 });
cpi.GroupAt(7000, true).Add(new DifficultyControlPoint { SpeedMultiplier = 4 });
cpi.GroupAt(1000).Add(new SampleControlPoint { SampleVolume = 0 });
cpi.GroupAt(8000, true).Add(new EffectControlPoint { KiaiMode = true });
Assert.That(cpi.AllControlPoints.Count, Is.EqualTo(8));
Assert.That(cpi.Groups, Is.Ordered.Ascending.By(nameof(ControlPointGroup.Time)));
Assert.That(cpi.AllControlPoints, Is.Ordered.Ascending.By(nameof(ControlPoint.Time)));
Assert.That(cpi.TimingPoints, Is.Ordered.Ascending.By(nameof(ControlPoint.Time)));
}
[Test]
public void TestClear()
{
var cpi = new ControlPointInfo();
cpi.Add(0, new TimingControlPoint());
cpi.Add(1000, new TimingControlPoint { BeatLength = 500 });
cpi.Add(10000, new TimingControlPoint { BeatLength = 200 });
cpi.Add(5000, new TimingControlPoint { BeatLength = 100 });
cpi.Add(3000, new DifficultyControlPoint { SpeedMultiplier = 2 });
cpi.GroupAt(7000, true).Add(new DifficultyControlPoint { SpeedMultiplier = 4 });
cpi.GroupAt(1000).Add(new SampleControlPoint { SampleVolume = 0 });
cpi.GroupAt(8000, true).Add(new EffectControlPoint { KiaiMode = true });
cpi.Clear();
Assert.That(cpi.Groups.Count, Is.EqualTo(0));
Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(0));
Assert.That(cpi.AllControlPoints.Count, Is.EqualTo(0));
}
}
}

View File

@ -225,8 +225,10 @@ namespace osu.Game.Tests.NonVisual
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");
}

View File

@ -11,13 +11,13 @@ namespace osu.Game.Tests.Resources
{
public static Stream OpenResource(string name) => new DllResourceStore("osu.Game.Tests.dll").GetStream($"Resources/{name}");
public static Stream GetTestBeatmapStream() => new DllResourceStore("osu.Game.Resources.dll").GetStream("Beatmaps/241526 Soleily - Renatus.osz");
public static Stream GetTestBeatmapStream(bool virtualTrack = false) => new DllResourceStore("osu.Game.Resources.dll").GetStream($"Beatmaps/241526 Soleily - Renatus{(virtualTrack ? "_virtual" : "")}.osz");
public static string GetTestBeatmapForImport()
public static string GetTestBeatmapForImport(bool virtualTrack = false)
{
var temp = Path.GetTempFileName() + ".osz";
using (var stream = GetTestBeatmapStream())
using (var stream = GetTestBeatmapStream(virtualTrack))
using (var newFile = File.Create(temp))
stream.CopyTo(newFile);

View File

@ -0,0 +1,8 @@
osu file format v7
[TimingPoints]
0,100,4,2,0,100,1,0
12,500,4,2,0,100,1,0
1000,-10,4,2,0,100,0,0
2000,-54,4,2,0,100,0,0
3000,-200,4,2,0,100,0,0

View File

@ -0,0 +1,5 @@
osu file format v14
[TimingPoints]
0,-200,4,1,0,100,0,0
2000,100,1,1,0,100,1,0

View File

@ -25,7 +25,9 @@ namespace osu.Game.Tests.Skins
var comboColors = decoder.Decode(stream).ComboColours;
List<Color4> expectedColors;
if (hasColours)
{
expectedColors = new List<Color4>
{
new Color4(142, 199, 255, 255),
@ -33,6 +35,7 @@ namespace osu.Game.Tests.Skins
new Color4(128, 255, 255, 255),
new Color4(100, 100, 100, 100),
};
}
else
expectedColors = new DefaultSkin().Configuration.ComboColours;

View File

@ -285,6 +285,12 @@ namespace osu.Game.Tests.Visual.Background
});
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
rulesets?.Dispose();
}
private class DummySongSelect : PlaySongSelect
{
protected override BackgroundScreen CreateBackground()

View File

@ -7,6 +7,7 @@ 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
{
@ -34,6 +35,7 @@ namespace osu.Game.Tests.Visual.Components
PreviewTrack track = null;
AddStep("get track", () => track = getOwnedTrack());
AddUntilStep("wait loaded", () => track.IsLoaded);
AddStep("start", () => track.Start());
AddAssert("started", () => track.IsRunning);
AddStep("stop", () => track.Stop());
@ -52,10 +54,15 @@ namespace osu.Game.Tests.Visual.Components
track2 = getOwnedTrack();
});
AddUntilStep("wait loaded", () => track1.IsLoaded && track2.IsLoaded);
AddStep("start track 1", () => track1.Start());
AddStep("start track 2", () => track2.Start());
AddAssert("track 1 stopped", () => !track1.IsRunning);
AddAssert("track 2 started", () => track2.IsRunning);
AddStep("start track 1", () => track1.Start());
AddAssert("track 2 stopped", () => !track2.IsRunning);
AddAssert("track 1 started", () => track1.IsRunning);
}
[Test]
@ -64,6 +71,7 @@ namespace osu.Game.Tests.Visual.Components
PreviewTrack track = null;
AddStep("get track", () => track = getOwnedTrack());
AddUntilStep("wait loaded", () => track.IsLoaded);
AddStep("start", () => track.Start());
AddStep("stop by owner", () => trackManager.StopAnyPlaying(this));
AddAssert("stopped", () => !track.IsRunning);
@ -76,6 +84,7 @@ namespace osu.Game.Tests.Visual.Components
PreviewTrack track = null;
AddStep("get track", () => Add(owner = new TestTrackOwner(track = getTrack())));
AddUntilStep("wait loaded", () => track.IsLoaded);
AddStep("start", () => track.Start());
AddStep("attempt stop", () => trackManager.StopAnyPlaying(this));
AddAssert("not stopped", () => track.IsRunning);
@ -83,22 +92,46 @@ namespace osu.Game.Tests.Visual.Components
AddAssert("stopped", () => !track.IsRunning);
}
private PreviewTrack getTrack() => trackManager.Get(null);
[Test]
public void TestNonPresentTrack()
{
TestPreviewTrack track = null;
private PreviewTrack getOwnedTrack()
AddStep("get non-present track", () =>
{
Add(new TestTrackOwner(track = getTrack()));
track.Alpha = 0;
});
AddUntilStep("wait loaded", () => track.IsLoaded);
AddStep("start", () => track.Start());
AddStep("seek to end", () => track.Track.Seek(track.Track.Length));
AddAssert("track stopped", () => !track.IsRunning);
}
private TestPreviewTrack getTrack() => (TestPreviewTrack)trackManager.Get(null);
private TestPreviewTrack getOwnedTrack()
{
var track = getTrack();
Add(track);
LoadComponentAsync(track, Add);
return track;
}
private class TestTrackOwner : CompositeDrawable, IPreviewTrackOwner
{
private readonly PreviewTrack track;
public TestTrackOwner(PreviewTrack track)
{
AddInternal(track);
this.track = track;
}
[BackgroundDependencyLoader]
private void load()
{
LoadComponentAsync(track, AddInternal);
}
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
@ -109,14 +142,16 @@ namespace osu.Game.Tests.Visual.Components
}
}
private class TestPreviewTrackManager : PreviewTrackManager
public class TestPreviewTrackManager : PreviewTrackManager
{
protected override TrackManagerPreviewTrack CreatePreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TestPreviewTrack(beatmapSetInfo, trackStore);
protected class TestPreviewTrack : TrackManagerPreviewTrack
public class TestPreviewTrack : TrackManagerPreviewTrack
{
private readonly ITrackStore trackManager;
public new Track Track => base.Track;
public TestPreviewTrack(BeatmapSetInfo beatmapSetInfo, ITrackStore trackManager)
: base(beatmapSetInfo, trackManager)
{

View File

@ -1,8 +1,6 @@
// 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.Framework.Allocation;
using osu.Game.Rulesets.Osu;
@ -11,10 +9,8 @@ using osu.Game.Screens.Edit.Compose;
namespace osu.Game.Tests.Visual.Editor
{
[TestFixture]
public class TestSceneEditorCompose : EditorClockTestScene
public class TestSceneComposeScreen : EditorClockTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(ComposeScreen) };
[BackgroundDependencyLoader]
private void load()
{

View File

@ -0,0 +1,171 @@
// 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.Shapes;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor
{
public class TestSceneDistanceSnapGrid : EditorClockTestScene
{
private const double beat_length = 100;
private static readonly Vector2 grid_position = new Vector2(512, 384);
[Cached(typeof(IEditorBeatmap))]
private readonly EditorBeatmap<OsuHitObject> editorBeatmap;
[Cached(typeof(IDistanceSnapProvider))]
private readonly SnapProvider snapProvider = new SnapProvider();
public TestSceneDistanceSnapGrid()
{
editorBeatmap = new EditorBeatmap<OsuHitObject>(new OsuBeatmap());
editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length });
}
[SetUp]
public void Setup() => Schedule(() =>
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.SlateGray
},
new TestDistanceSnapGrid(new HitObject(), grid_position)
};
});
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
[TestCase(4)]
[TestCase(6)]
[TestCase(8)]
[TestCase(12)]
[TestCase(16)]
public void TestBeatDivisor(int divisor)
{
AddStep($"set beat divisor = {divisor}", () => BeatDivisor.Value = divisor);
}
[Test]
public void TestLimitedDistance()
{
AddStep("create limited grid", () =>
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.SlateGray
},
new TestDistanceSnapGrid(new HitObject(), grid_position, new HitObject { StartTime = 100 })
};
});
}
private class TestDistanceSnapGrid : DistanceSnapGrid
{
public new float DistanceSpacing => base.DistanceSpacing;
public TestDistanceSnapGrid(HitObject hitObject, Vector2 centrePosition, HitObject nextHitObject = null)
: base(hitObject, nextHitObject, centrePosition)
{
}
protected override void CreateContent(Vector2 centrePosition)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(5),
Position = centrePosition
});
int beatIndex = 0;
for (float s = centrePosition.X + DistanceSpacing; s <= DrawWidth && beatIndex < MaxIntervals; s += DistanceSpacing, beatIndex++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(5, 10),
Position = new Vector2(s, centrePosition.Y),
Colour = GetColourForBeatIndex(beatIndex)
});
}
beatIndex = 0;
for (float s = centrePosition.X - DistanceSpacing; s >= 0 && beatIndex < MaxIntervals; s -= DistanceSpacing, beatIndex++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(5, 10),
Position = new Vector2(s, centrePosition.Y),
Colour = GetColourForBeatIndex(beatIndex)
});
}
beatIndex = 0;
for (float s = centrePosition.Y + DistanceSpacing; s <= DrawHeight && beatIndex < MaxIntervals; s += DistanceSpacing, beatIndex++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(10, 5),
Position = new Vector2(centrePosition.X, s),
Colour = GetColourForBeatIndex(beatIndex)
});
}
beatIndex = 0;
for (float s = centrePosition.Y - DistanceSpacing; s >= 0 && beatIndex < MaxIntervals; s -= DistanceSpacing, beatIndex++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(10, 5),
Position = new Vector2(centrePosition.X, s),
Colour = GetColourForBeatIndex(beatIndex)
});
}
}
public override (Vector2 position, double time) GetSnappedPosition(Vector2 screenSpacePosition)
=> (Vector2.Zero, 0);
}
private class SnapProvider : IDistanceSnapProvider
{
public (Vector2 position, double time) GetSnappedPosition(Vector2 position, double time) => (position, time);
public float GetBeatSnapDistanceAt(double referenceTime) => 10;
public float DurationToDistance(double referenceTime, double duration) => (float)duration;
public double DistanceToDuration(double referenceTime, float distance) => distance;
public double GetSnappedDurationFromDistance(double referenceTime, float distance) => 0;
public float GetSnappedDistanceFromDistance(double referenceTime, float distance) => 0;
}
}
}

View File

@ -10,9 +10,9 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osuTK;
using osuTK.Graphics;
@ -101,7 +101,7 @@ namespace osu.Game.Tests.Visual.Editor
}
}
private class StartStopButton : Button
private class StartStopButton : OsuButton
{
private IAdjustableClock adjustableClock;
private bool started;

View File

@ -28,18 +28,7 @@ namespace osu.Game.Tests.Visual.Editor
{
var testBeatmap = new Beatmap
{
ControlPointInfo = new ControlPointInfo
{
TimingPoints =
{
new TimingControlPoint { Time = 0, BeatLength = 200 },
new TimingControlPoint { Time = 100, BeatLength = 400 },
new TimingControlPoint { Time = 175, BeatLength = 800 },
new TimingControlPoint { Time = 350, BeatLength = 200 },
new TimingControlPoint { Time = 450, BeatLength = 100 },
new TimingControlPoint { Time = 500, BeatLength = 307.69230769230802 }
}
},
ControlPointInfo = new ControlPointInfo(),
HitObjects =
{
new HitCircle { StartTime = 0 },
@ -47,6 +36,13 @@ namespace osu.Game.Tests.Visual.Editor
}
};
testBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 200 });
testBeatmap.ControlPointInfo.Add(100, new TimingControlPoint { BeatLength = 400 });
testBeatmap.ControlPointInfo.Add(175, new TimingControlPoint { BeatLength = 800 });
testBeatmap.ControlPointInfo.Add(350, new TimingControlPoint { BeatLength = 200 });
testBeatmap.ControlPointInfo.Add(450, new TimingControlPoint { BeatLength = 100 });
testBeatmap.ControlPointInfo.Add(500, new TimingControlPoint { BeatLength = 307.69230769230802 });
Beatmap.Value = CreateWorkingBeatmap(testBeatmap);
Child = new TimingPointVisualiser(testBeatmap, 5000) { Clock = Clock };

View File

@ -22,7 +22,7 @@ using osuTK;
namespace osu.Game.Tests.Visual.Editor
{
[TestFixture]
public class TestSceneHitObjectComposer : OsuTestScene
public class TestSceneHitObjectComposer : EditorClockTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{

View 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 System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit.Timing;
namespace osu.Game.Tests.Visual.Editor
{
[TestFixture]
public class TestSceneTimingScreen : EditorClockTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ControlPointTable),
typeof(ControlPointSettings),
typeof(Section<>),
typeof(TimingSection),
typeof(EffectSection),
typeof(SampleSection),
typeof(DifficultySection),
typeof(RowAttribute)
};
[BackgroundDependencyLoader]
private void load()
{
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
Child = new TimingScreen();
}
}
}

View File

@ -47,7 +47,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestRelativeBeatLengthScaleSingleTimingPoint()
{
var beatmap = createBeatmap(new TimingControlPoint { BeatLength = time_range / 2 });
var beatmap = createBeatmap();
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range / 2 });
createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true);
@ -61,10 +62,10 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestRelativeBeatLengthScaleTimingPointBeyondEndDoesNotBecomeDominant()
{
var beatmap = createBeatmap(
new TimingControlPoint { BeatLength = time_range / 2 },
new TimingControlPoint { Time = 12000, BeatLength = time_range },
new TimingControlPoint { Time = 100000, BeatLength = time_range });
var beatmap = createBeatmap();
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range / 2 });
beatmap.ControlPointInfo.Add(12000, new TimingControlPoint { BeatLength = time_range });
beatmap.ControlPointInfo.Add(100000, new TimingControlPoint { BeatLength = time_range });
createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true);
@ -75,9 +76,9 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestRelativeBeatLengthScaleFromSecondTimingPoint()
{
var beatmap = createBeatmap(
new TimingControlPoint { BeatLength = time_range },
new TimingControlPoint { Time = 3 * time_range, BeatLength = time_range / 2 });
var beatmap = createBeatmap();
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range });
beatmap.ControlPointInfo.Add(3 * time_range, new TimingControlPoint { BeatLength = time_range / 2 });
createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true);
@ -97,9 +98,9 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestNonRelativeScale()
{
var beatmap = createBeatmap(
new TimingControlPoint { BeatLength = time_range },
new TimingControlPoint { Time = 3 * time_range, BeatLength = time_range / 2 });
var beatmap = createBeatmap();
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range });
beatmap.ControlPointInfo.Add(3 * time_range, new TimingControlPoint { BeatLength = time_range / 2 });
createTest(beatmap);
@ -119,7 +120,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestSliderMultiplierDoesNotAffectRelativeBeatLength()
{
var beatmap = createBeatmap(new TimingControlPoint { BeatLength = time_range });
var beatmap = createBeatmap();
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range });
beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2;
createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true);
@ -132,7 +134,8 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test]
public void TestSliderMultiplierAffectsNonRelativeBeatLength()
{
var beatmap = createBeatmap(new TimingControlPoint { BeatLength = time_range });
var beatmap = createBeatmap();
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range });
beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2;
createTest(beatmap);
@ -154,14 +157,11 @@ namespace osu.Game.Tests.Visual.Gameplay
/// Creates an <see cref="IBeatmap"/>, containing 10 hitobjects and user-provided timing points.
/// The hitobjects are spaced <see cref="time_range"/> milliseconds apart.
/// </summary>
/// <param name="timingControlPoints">The timing points to add to the beatmap.</param>
/// <returns>The <see cref="IBeatmap"/>.</returns>
private IBeatmap createBeatmap(params TimingControlPoint[] timingControlPoints)
private IBeatmap createBeatmap()
{
var beatmap = new Beatmap<HitObject> { BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo } };
beatmap.ControlPointInfo.TimingPoints.AddRange(timingControlPoints);
for (int i = 0; i < 10; i++)
beatmap.HitObjects.Add(new HitObject { StartTime = i * time_range });

View File

@ -69,6 +69,24 @@ namespace osu.Game.Tests.Visual.Gameplay
confirmClockRunning(true);
}
[Test]
public void TestPauseWithResumeOverlay()
{
AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre));
AddUntilStep("wait for hitobjects", () => Player.ScoreProcessor.Health.Value < 1);
pauseAndConfirm();
resume();
confirmClockRunning(false);
confirmPauseOverlayShown(false);
pauseAndConfirm();
AddUntilStep("resume overlay is not active", () => Player.DrawableRuleset.ResumeOverlay.State.Value == Visibility.Hidden);
confirmPaused();
}
[Test]
public void TestResumeWithResumeOverlaySkipped()
{
@ -219,6 +237,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("player not exited", () => Player.IsCurrentScreen());
AddStep("exit", () => Player.Exit());
confirmExited();
confirmNoTrackAdjustments();
}
private void confirmPaused()
@ -240,6 +259,11 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("player 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 resume() => AddStep("resume", () => Player.Resume());

View File

@ -7,11 +7,10 @@ using osu.Game.Online;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Rulesets.Osu;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Users;
using osuTK;
using System;
using System.Collections.Generic;
using osu.Game.Screens.Ranking.Pages;
namespace osu.Game.Tests.Visual.Gameplay
{
@ -42,7 +41,6 @@ namespace osu.Game.Tests.Visual.Gameplay
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(80, 40),
};
});
}

View File

@ -3,6 +3,7 @@
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Play.PlayerSettings;
@ -20,6 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
State = { Value = Visibility.Visible }
});
Add(container = new ExampleContainer());

View File

@ -3,11 +3,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Ranking.Pages;
@ -22,11 +27,13 @@ namespace osu.Game.Tests.Visual.Gameplay
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ScoreInfo),
typeof(Results),
typeof(ResultsPage),
typeof(ScoreResultsPage),
typeof(LocalLeaderboardPage)
typeof(RetryButton),
typeof(ReplayDownloadButton),
typeof(LocalLeaderboardPage),
typeof(TestPlayer)
};
[BackgroundDependencyLoader]
@ -42,26 +49,82 @@ namespace osu.Game.Tests.Visual.Gameplay
var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetID == 0);
if (beatmapInfo != null)
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
}
LoadScreen(new SoloResults(new ScoreInfo
private TestSoloResults createResultsScreen() => new TestSoloResults(new ScoreInfo
{
TotalScore = 2845370,
Accuracy = 0.98,
MaxCombo = 123,
Rank = ScoreRank.A,
Date = DateTimeOffset.Now,
Statistics = new Dictionary<HitResult, int>
{
TotalScore = 2845370,
Accuracy = 0.98,
MaxCombo = 123,
Rank = ScoreRank.A,
Date = DateTimeOffset.Now,
Statistics = new Dictionary<HitResult, int>
{ HitResult.Great, 50 },
{ HitResult.Good, 20 },
{ HitResult.Meh, 50 },
{ HitResult.Miss, 1 }
},
User = new User
{
Username = "peppy",
}
});
[Test]
public void ResultsWithoutPlayer()
{
TestSoloResults screen = null;
AddStep("load results", () => Child = new OsuScreenStack(screen = createResultsScreen())
{
RelativeSizeAxes = Axes.Both
});
AddUntilStep("wait for loaded", () => screen.IsLoaded);
AddAssert("retry overlay not present", () => screen.RetryOverlay == null);
}
[Test]
public void ResultsWithPlayer()
{
TestSoloResults screen = null;
AddStep("load results", () => Child = new TestResultsContainer(screen = createResultsScreen()));
AddUntilStep("wait for loaded", () => screen.IsLoaded);
AddAssert("retry overlay present", () => screen.RetryOverlay != null);
}
private class TestResultsContainer : Container
{
[Cached(typeof(Player))]
private readonly Player player = new TestPlayer();
public TestResultsContainer(IScreen screen)
{
RelativeSizeAxes = Axes.Both;
InternalChild = new OsuScreenStack(screen)
{
{ HitResult.Great, 50 },
{ HitResult.Good, 20 },
{ HitResult.Meh, 50 },
{ HitResult.Miss, 1 }
},
User = new User
{
Username = "peppy",
}
}));
RelativeSizeAxes = Axes.Both,
};
}
}
private class TestSoloResults : SoloResults
{
public HotkeyRetryOverlay RetryOverlay;
public TestSoloResults(ScoreInfo score)
: base(score)
{
}
protected override void LoadComplete()
{
base.LoadComplete();
RetryOverlay = InternalChildren.OfType<HotkeyRetryOverlay>().SingleOrDefault();
}
}
}
}

View File

@ -326,7 +326,11 @@ namespace osu.Game.Tests.Visual.Gameplay
public IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup) => throw new NotImplementedException();
public event Action SourceChanged;
public event Action SourceChanged
{
add { }
remove { }
}
}
private class TestSkinComponent : ISkinComponent

View File

@ -33,23 +33,15 @@ namespace osu.Game.Tests.Visual.Menus
[Test]
public void TestInstantLoad()
{
bool logoVisible = false;
// visual only, very impossible to test this using asserts.
AddStep("begin loading", () =>
AddStep("load immediately", () =>
{
loader = new TestLoader();
loader.AllowLoad.Set();
LoadScreen(loader);
});
AddUntilStep("loaded", () =>
{
logoVisible = loader.Logo?.Alpha > 0;
return loader.Logo != null && loader.ScreenLoaded;
});
AddAssert("logo was not visible", () => !logoVisible);
}
[Test]
@ -58,7 +50,7 @@ namespace osu.Game.Tests.Visual.Menus
AddStep("begin loading", () => LoadScreen(loader = new TestLoader()));
AddUntilStep("wait for logo visible", () => loader.Logo?.Alpha > 0);
AddStep("finish loading", () => loader.AllowLoad.Set());
AddAssert("loaded", () => loader.Logo != null && loader.ScreenLoaded);
AddUntilStep("loaded", () => loader.Logo != null && loader.ScreenLoaded);
AddUntilStep("logo gone", () => loader.Logo?.Alpha == 0);
}

View File

@ -5,12 +5,15 @@ using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
@ -18,7 +21,9 @@ using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select;
using osu.Game.Tests.Beatmaps.IO;
using osuTK;
using osuTK.Graphics;
using osuTK.Input;
@ -31,11 +36,11 @@ namespace osu.Game.Tests.Visual.Menus
private const float click_padding = 25;
private GameHost host;
private TestOsuGame osuGame;
private TestOsuGame game;
private Vector2 backButtonPosition => osuGame.ToScreenSpace(new Vector2(click_padding, osuGame.LayoutRectangle.Bottom - click_padding));
private Vector2 backButtonPosition => game.ToScreenSpace(new Vector2(click_padding, game.LayoutRectangle.Bottom - click_padding));
private Vector2 optionsButtonPosition => osuGame.ToScreenSpace(new Vector2(click_padding, click_padding));
private Vector2 optionsButtonPosition => game.ToScreenSpace(new Vector2(click_padding, click_padding));
[BackgroundDependencyLoader]
private void load(GameHost host)
@ -54,23 +59,23 @@ namespace osu.Game.Tests.Visual.Menus
{
AddStep("Create new game instance", () =>
{
if (osuGame != null)
if (game != null)
{
Remove(osuGame);
osuGame.Dispose();
Remove(game);
game.Dispose();
}
osuGame = new TestOsuGame(LocalStorage, API);
osuGame.SetHost(host);
game = new TestOsuGame(LocalStorage, API);
game.SetHost(host);
// todo: this can be removed once we can run audio trakcs without a device present
// see https://github.com/ppy/osu/issues/1302
osuGame.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles);
game.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles);
Add(osuGame);
Add(game);
});
AddUntilStep("Wait for load", () => osuGame.IsLoaded);
AddUntilStep("Wait for intro", () => osuGame.ScreenStack.CurrentScreen is IntroScreen);
AddUntilStep("Wait for load", () => game.IsLoaded);
AddUntilStep("Wait for intro", () => game.ScreenStack.CurrentScreen is IntroScreen);
confirmAtMainMenu();
}
@ -82,11 +87,43 @@ namespace osu.Game.Tests.Visual.Menus
pushAndConfirm(() => songSelect = new TestSongSelect());
AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show());
AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
AddStep("Press escape", () => pressAndRelease(Key.Escape));
pushEscape();
AddAssert("Overlay was hidden", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden);
exitViaEscapeAndConfirm();
}
[TestCase(true)]
[TestCase(false)]
public void TestSongContinuesAfterExitPlayer(bool withUserPause)
{
Player player = null;
WorkingBeatmap beatmap() => game.Beatmap.Value;
Track track() => beatmap().Track;
pushAndConfirm(() => new TestSongSelect());
AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Wait());
AddUntilStep("wait for selected", () => !game.Beatmap.IsDefault);
if (withUserPause)
AddStep("pause", () => game.Dependencies.Get<MusicController>().Stop());
AddStep("press enter", () => pressAndRelease(Key.Enter));
AddUntilStep("wait for player", () => (player = game.ScreenStack.CurrentScreen as Player) != null);
AddUntilStep("wait for fail", () => player.HasFailed);
AddUntilStep("wait for track stop", () => !track().IsRunning);
AddAssert("Ensure time before preview point", () => track().CurrentTime < beatmap().Metadata.PreviewTime);
pushEscape();
AddUntilStep("wait for track playing", () => track().IsRunning);
AddAssert("Ensure time wasn't reset to preview point", () => track().CurrentTime < beatmap().Metadata.PreviewTime);
}
[Test]
public void TestExitSongSelectWithClick()
{
@ -98,7 +135,7 @@ namespace osu.Game.Tests.Visual.Menus
AddStep("Move mouse to backButton", () => InputManager.MoveMouseTo(backButtonPosition));
// BackButton handles hover using its child button, so this checks whether or not any of BackButton's children are hovered.
AddUntilStep("Back button is hovered", () => InputManager.HoveredDrawables.Any(d => d.Parent == osuGame.BackButton));
AddUntilStep("Back button is hovered", () => InputManager.HoveredDrawables.Any(d => d.Parent == game.BackButton));
AddStep("Click back button", () => InputManager.Click(MouseButton.Left));
AddUntilStep("Overlay was hidden", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden);
@ -122,25 +159,28 @@ namespace osu.Game.Tests.Visual.Menus
[Test]
public void TestOpenOptionsAndExitWithEscape()
{
AddUntilStep("Wait for options to load", () => osuGame.Settings.IsLoaded);
AddUntilStep("Wait for options to load", () => game.Settings.IsLoaded);
AddStep("Enter menu", () => pressAndRelease(Key.Enter));
AddStep("Move mouse to options overlay", () => InputManager.MoveMouseTo(optionsButtonPosition));
AddStep("Click options overlay", () => InputManager.Click(MouseButton.Left));
AddAssert("Options overlay was opened", () => osuGame.Settings.State.Value == Visibility.Visible);
AddAssert("Options overlay was opened", () => game.Settings.State.Value == Visibility.Visible);
AddStep("Hide options overlay using escape", () => pressAndRelease(Key.Escape));
AddAssert("Options overlay was closed", () => osuGame.Settings.State.Value == Visibility.Hidden);
AddAssert("Options overlay was closed", () => game.Settings.State.Value == Visibility.Hidden);
}
private void pushAndConfirm(Func<Screen> newScreen)
{
Screen screen = null;
AddStep("Push new screen", () => osuGame.ScreenStack.Push(screen = newScreen()));
AddUntilStep("Wait for new screen", () => osuGame.ScreenStack.CurrentScreen == screen && screen.IsLoaded);
AddStep("Push new screen", () => game.ScreenStack.Push(screen = newScreen()));
AddUntilStep("Wait for new screen", () => game.ScreenStack.CurrentScreen == screen && screen.IsLoaded);
}
private void pushEscape() =>
AddStep("Press escape", () => pressAndRelease(Key.Escape));
private void exitViaEscapeAndConfirm()
{
AddStep("Press escape", () => pressAndRelease(Key.Escape));
pushEscape();
confirmAtMainMenu();
}
@ -151,7 +191,7 @@ namespace osu.Game.Tests.Visual.Menus
confirmAtMainMenu();
}
private void confirmAtMainMenu() => AddUntilStep("Wait for main menu", () => osuGame.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded);
private void confirmAtMainMenu() => AddUntilStep("Wait for main menu", () => game.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded);
private void pressAndRelease(Key key)
{
@ -169,6 +209,8 @@ namespace osu.Game.Tests.Visual.Menus
public new OsuConfigManager LocalConfig => base.LocalConfig;
public new Bindable<WorkingBeatmap> Beatmap => base.Beatmap;
protected override Loader CreateLoader() => new TestLoader();
public TestOsuGame(Storage storage, IAPIProvider api)

View File

@ -0,0 +1,102 @@
// 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.UserInterface;
using osu.Game.Beatmaps;
using osu.Game.Overlays.BeatmapSet;
using osu.Game.Rulesets;
using System;
using System.Collections.Generic;
using System.Linq;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneBeatmapRulesetSelector : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(BeatmapRulesetSelector),
typeof(BeatmapRulesetTabItem),
};
private readonly TestRulesetSelector selector;
public TestSceneBeatmapRulesetSelector()
{
Add(selector = new TestRulesetSelector());
}
[Resolved]
private RulesetStore rulesets { get; set; }
[Test]
public void TestMultipleRulesetsBeatmapSet()
{
var enabledRulesets = rulesets.AvailableRulesets.Skip(1).Take(2);
AddStep("load multiple rulesets beatmapset", () =>
{
selector.BeatmapSet = new BeatmapSetInfo
{
Beatmaps = enabledRulesets.Select(r => new BeatmapInfo { Ruleset = r }).ToList()
};
});
var tabItems = selector.TabContainer.TabItems;
AddAssert("other rulesets disabled", () => tabItems.Except(tabItems.Where(t => enabledRulesets.Any(r => r.Equals(t.Value)))).All(t => !t.Enabled.Value));
AddAssert("left-most ruleset selected", () => tabItems.First(t => t.Enabled.Value).Active.Value);
}
[Test]
public void TestSingleRulesetBeatmapSet()
{
var enabledRuleset = rulesets.AvailableRulesets.Last();
AddStep("load single ruleset beatmapset", () =>
{
selector.BeatmapSet = new BeatmapSetInfo
{
Beatmaps = new List<BeatmapInfo>
{
new BeatmapInfo
{
Ruleset = enabledRuleset
}
}
};
});
AddAssert("single ruleset selected", () => selector.SelectedTab.Value.Equals(enabledRuleset));
}
[Test]
public void TestEmptyBeatmapSet()
{
AddStep("load empty beatmapset", () => selector.BeatmapSet = new BeatmapSetInfo
{
Beatmaps = new List<BeatmapInfo>()
});
AddAssert("no ruleset selected", () => selector.SelectedTab == null);
AddAssert("all rulesets disabled", () => selector.TabContainer.TabItems.All(t => !t.Enabled.Value));
}
[Test]
public void TestNullBeatmapSet()
{
AddStep("load null beatmapset", () => selector.BeatmapSet = null);
AddAssert("no ruleset selected", () => selector.SelectedTab == null);
AddAssert("all rulesets disabled", () => selector.TabContainer.TabItems.All(t => !t.Enabled.Value));
}
private class TestRulesetSelector : BeatmapRulesetSelector
{
public new TabItem<RulesetInfo> SelectedTab => base.SelectedTab;
public new TabFillFlowContainer TabContainer => base.TabContainer;
}
}
}

View File

@ -40,24 +40,19 @@ namespace osu.Game.Tests.Visual.Online
typeof(PreviewButton),
typeof(SuccessRate),
typeof(BeatmapAvailability),
typeof(BeatmapRulesetSelector),
typeof(BeatmapRulesetTabItem),
};
protected override bool UseOnlineAPI => true;
private RulesetInfo taikoRuleset;
private RulesetInfo maniaRuleset;
public TestSceneBeatmapSetOverlay()
{
Add(overlay = new TestBeatmapSetOverlay());
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
taikoRuleset = rulesets.GetRuleset(1);
maniaRuleset = rulesets.GetRuleset(3);
}
[Resolved]
private RulesetStore rulesets { get; set; }
[Test]
public void TestLoading()
@ -111,7 +106,7 @@ namespace osu.Game.Tests.Visual.Online
StarDifficulty = 9.99,
Version = @"TEST",
Length = 456000,
Ruleset = maniaRuleset,
Ruleset = rulesets.GetRuleset(3),
BaseDifficulty = new BeatmapDifficulty
{
CircleSize = 1,
@ -189,7 +184,7 @@ namespace osu.Game.Tests.Visual.Online
StarDifficulty = 5.67,
Version = @"ANOTHER TEST",
Length = 123000,
Ruleset = taikoRuleset,
Ruleset = rulesets.GetRuleset(1),
BaseDifficulty = new BeatmapDifficulty
{
CircleSize = 9,
@ -217,6 +212,54 @@ namespace osu.Game.Tests.Visual.Online
downloadAssert(false);
}
[Test]
public void TestMultipleRulesets()
{
AddStep("show multiple rulesets beatmap", () =>
{
var beatmaps = new List<BeatmapInfo>();
foreach (var ruleset in rulesets.AvailableRulesets.Skip(1))
{
beatmaps.Add(new BeatmapInfo
{
Version = ruleset.Name,
Ruleset = ruleset,
BaseDifficulty = new BeatmapDifficulty(),
OnlineInfo = new BeatmapOnlineInfo(),
Metrics = new BeatmapMetrics
{
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
},
});
}
overlay.ShowBeatmapSet(new BeatmapSetInfo
{
Metadata = new BeatmapMetadata
{
Title = @"multiple rulesets beatmap",
Artist = @"none",
Author = new User
{
Username = "BanchoBot",
Id = 3,
}
},
OnlineInfo = new BeatmapSetOnlineInfo
{
Covers = new BeatmapSetOnlineCovers(),
},
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() },
Beatmaps = beatmaps
});
});
AddAssert("shown beatmaps of current ruleset", () => overlay.Header.Picker.Difficulties.All(b => b.Beatmap.Ruleset.Equals(overlay.Header.RulesetSelector.Current.Value)));
AddAssert("left-most beatmap selected", () => overlay.Header.Picker.Difficulties.First().State == BeatmapPicker.DifficultySelectorState.Selected);
}
[Test]
public void TestHide()
{
@ -281,12 +324,12 @@ namespace osu.Game.Tests.Visual.Online
private void downloadAssert(bool shown)
{
AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.DownloadButtonsVisible == shown);
AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.Header.DownloadButtonsVisible == shown);
}
private class TestBeatmapSetOverlay : BeatmapSetOverlay
{
public bool DownloadButtonsVisible => Header.DownloadButtonsVisible;
public new Header Header => base.Header;
}
}
}

View File

@ -0,0 +1,59 @@
// 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.Online.API.Requests;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Game.Overlays.Comments;
namespace osu.Game.Tests.Visual.Online
{
[TestFixture]
public class TestSceneCommentsContainer : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(CommentsContainer),
typeof(CommentsHeader),
typeof(DrawableComment),
typeof(HeaderButton),
typeof(SortTabControl),
typeof(ShowChildrenButton),
typeof(DeletedChildrenPlaceholder),
typeof(VotePill)
};
protected override bool UseOnlineAPI => true;
public TestSceneCommentsContainer()
{
BasicScrollContainer scrollFlow;
Add(scrollFlow = new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
});
AddStep("Big Black comments", () =>
{
scrollFlow.Clear();
scrollFlow.Add(new CommentsContainer(CommentableType.Beatmapset, 41823));
});
AddStep("Airman comments", () =>
{
scrollFlow.Clear();
scrollFlow.Add(new CommentsContainer(CommentableType.Beatmapset, 24313));
});
AddStep("lazer build comments", () =>
{
scrollFlow.Clear();
scrollFlow.Add(new CommentsContainer(CommentableType.Build, 4772));
});
}
}
}

View File

@ -0,0 +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 NUnit.Framework;
using osu.Framework.Bindables;
using osu.Game.Overlays.Comments;
namespace osu.Game.Tests.Visual.Online
{
[TestFixture]
public class TestSceneCommentsHeader : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(CommentsHeader),
typeof(HeaderButton),
typeof(SortTabControl),
};
private readonly Bindable<CommentsSortCriteria> sort = new Bindable<CommentsSortCriteria>();
private readonly BindableBool showDeleted = new BindableBool();
public TestSceneCommentsHeader()
{
Add(new CommentsHeader
{
Sort = { BindTarget = sort },
ShowDeleted = { BindTarget = showDeleted }
});
AddStep("Trigger ShowDeleted", () => showDeleted.Value = !showDeleted.Value);
AddStep("Select old", () => sort.Value = CommentsSortCriteria.Old);
AddStep("Select new", () => sort.Value = CommentsSortCriteria.New);
AddStep("Select top", () => sort.Value = CommentsSortCriteria.Top);
}
}
}

View File

@ -0,0 +1,54 @@
// 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.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Overlays.BeatmapSet.Buttons;
using osuTK;
namespace osu.Game.Tests.Visual.Online
{
public class TestSceneFavouriteButton : OsuTestScene
{
private FavouriteButton favourite;
[SetUpSteps]
public void SetUpSteps()
{
AddStep("create button", () => Child = favourite = new FavouriteButton
{
RelativeSizeAxes = Axes.None,
Size = new Vector2(50),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
}
[Test]
public void TestLoggedOutIn()
{
AddStep("set valid beatmap", () => favourite.BeatmapSet.Value = new BeatmapSetInfo { OnlineBeatmapSetID = 88 });
AddStep("log out", () => API.Logout());
checkEnabled(false);
AddStep("log in", () => API.Login("test", "test"));
checkEnabled(true);
}
[Test]
public void TestBeatmapChange()
{
AddStep("log in", () => API.Login("test", "test"));
AddStep("set valid beatmap", () => favourite.BeatmapSet.Value = new BeatmapSetInfo { OnlineBeatmapSetID = 88 });
checkEnabled(true);
AddStep("set invalid beatmap", () => favourite.BeatmapSet.Value = new BeatmapSetInfo());
checkEnabled(false);
}
private void checkEnabled(bool expected)
{
AddAssert("is " + (expected ? "enabled" : "disabled"), () => favourite.Enabled.Value == expected);
}
}
}

View File

@ -1,10 +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 osu.Game.Overlays.Profile.Sections;
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Allocation;
using osu.Game.Graphics;
namespace osu.Game.Tests.Visual.Online
{
@ -17,11 +19,11 @@ namespace osu.Game.Tests.Visual.Online
public TestSceneShowMoreButton()
{
ShowMoreButton button = null;
TestButton button = null;
int fireCount = 0;
Add(button = new ShowMoreButton
Add(button = new TestButton
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@ -51,5 +53,16 @@ namespace osu.Game.Tests.Visual.Online
AddAssert("action fired twice", () => fireCount == 2);
AddAssert("is in loading state", () => button.IsLoading);
}
private class TestButton : ShowMoreButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colors)
{
IdleColour = colors.YellowDark;
HoverColour = colors.Yellow;
ChevronIconColour = colors.Red;
}
}
}
}

View File

@ -6,6 +6,11 @@ using osu.Framework.Graphics;
using osu.Game.Online.Chat;
using osu.Game.Users;
using osuTK;
using System;
using System.Linq;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Chat;
namespace osu.Game.Tests.Visual.Online
{
@ -41,14 +46,14 @@ namespace osu.Game.Tests.Visual.Online
[Cached]
private ChannelManager channelManager = new ChannelManager();
private readonly StandAloneChatDisplay chatDisplay;
private readonly StandAloneChatDisplay chatDisplay2;
private readonly TestStandAloneChatDisplay chatDisplay;
private readonly TestStandAloneChatDisplay chatDisplay2;
public TestSceneStandAloneChatDisplay()
{
Add(channelManager);
Add(chatDisplay = new StandAloneChatDisplay
Add(chatDisplay = new TestStandAloneChatDisplay
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
@ -56,7 +61,7 @@ namespace osu.Game.Tests.Visual.Online
Size = new Vector2(400, 80)
});
Add(chatDisplay2 = new StandAloneChatDisplay(true)
Add(chatDisplay2 = new TestStandAloneChatDisplay(true)
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
@ -111,6 +116,60 @@ namespace osu.Game.Tests.Visual.Online
Sender = longUsernameUser,
Content = "Hi guys, my new username is lit!"
}));
AddStep("message with new date", () => testChannel.AddNewMessages(new Message(sequence++)
{
Sender = longUsernameUser,
Content = "Message from the future!",
Timestamp = DateTimeOffset.Now
}));
AddUntilStep("ensure still scrolled to bottom", () => chatDisplay.ScrolledToBottom);
const int messages_per_call = 10;
AddRepeatStep("add many messages", () =>
{
for (int i = 0; i < messages_per_call; i++)
{
testChannel.AddNewMessages(new Message(sequence++)
{
Sender = longUsernameUser,
Content = "Many messages! " + Guid.NewGuid(),
Timestamp = DateTimeOffset.Now
});
}
}, Channel.MAX_HISTORY / messages_per_call + 5);
AddAssert("Ensure no adjacent day separators", () =>
{
var indices = chatDisplay.FillFlow.OfType<DrawableChannel.DaySeparator>().Select(ds => chatDisplay.FillFlow.IndexOf(ds));
foreach (var i in indices)
{
if (i < chatDisplay.FillFlow.Count && chatDisplay.FillFlow[i + 1] is DrawableChannel.DaySeparator)
return false;
}
return true;
});
AddUntilStep("ensure still scrolled to bottom", () => chatDisplay.ScrolledToBottom);
}
private class TestStandAloneChatDisplay : StandAloneChatDisplay
{
public TestStandAloneChatDisplay(bool textbox = false)
: base(textbox)
{
}
protected DrawableChannel DrawableChannel => InternalChildren.OfType<DrawableChannel>().First();
protected OsuScrollContainer ScrollContainer => (OsuScrollContainer)((Container)DrawableChannel.Child).Child;
public FillFlowContainer FillFlow => (FillFlowContainer)ScrollContainer.Child;
public bool ScrolledToBottom => ScrollContainer.IsScrolledToEnd(1);
}
}
}

View File

@ -0,0 +1,76 @@
// 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.Framework.Graphics;
using osu.Game.Overlays.Comments;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Tests.Visual.Online
{
[TestFixture]
public class TestSceneVotePill : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(VotePill)
};
private VotePill votePill;
[Test]
public void TestUserCommentPill()
{
AddStep("Log in", logIn);
AddStep("User comment", () => addVotePill(getUserComment()));
AddStep("Click", () => votePill.Click());
AddAssert("Not loading", () => !votePill.IsLoading);
}
[Test]
public void TestRandomCommentPill()
{
AddStep("Log in", logIn);
AddStep("Random comment", () => addVotePill(getRandomComment()));
AddStep("Click", () => votePill.Click());
AddAssert("Loading", () => votePill.IsLoading);
}
[Test]
public void TestOfflineRandomCommentPill()
{
AddStep("Log out", API.Logout);
AddStep("Random comment", () => addVotePill(getRandomComment()));
AddStep("Click", () => votePill.Click());
AddAssert("Not loading", () => !votePill.IsLoading);
}
private void logIn() => API.Login("localUser", "password");
private Comment getUserComment() => new Comment
{
IsVoted = false,
UserId = API.LocalUser.Value.Id,
VotesCount = 10,
};
private Comment getRandomComment() => new Comment
{
IsVoted = false,
UserId = 4444,
VotesCount = 2,
};
private void addVotePill(Comment comment)
{
Clear();
Add(votePill = new VotePill(comment)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
});
}
}
}

View File

@ -10,6 +10,7 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets;
@ -51,11 +52,6 @@ namespace osu.Game.Tests.Visual.SongSelect
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
Add(carousel = new TestBeatmapCarousel
{
RelativeSizeAxes = Axes.Both,
});
}
/// <summary>
@ -245,6 +241,28 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert($"Check #{set_count} is at bottom", () => carousel.BeatmapSets.Last().Metadata.Title.EndsWith($"#{set_count}!"));
}
[Test]
public void TestSortingStability()
{
var sets = new List<BeatmapSetInfo>();
for (int i = 0; i < 20; i++)
{
var set = createTestBeatmapSet(i);
set.Metadata.Artist = "same artist";
set.Metadata.Title = "same title";
sets.Add(set);
}
loadBeatmaps(sets);
AddStep("Sort by artist", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Artist }, false));
AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.ID == index).All(b => b));
AddStep("Sort by title", () => carousel.Filter(new FilterCriteria { Sort = SortMode.Title }, false));
AddAssert("Items remain in original order", () => carousel.BeatmapSets.Select((set, index) => set.ID == index).All(b => b));
}
[Test]
public void TestSortingWithFiltered()
{
@ -316,10 +334,19 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestHiding()
{
BeatmapSetInfo hidingSet = createTestBeatmapSet(1);
hidingSet.Beatmaps[1].Hidden = true;
BeatmapSetInfo hidingSet = null;
List<BeatmapSetInfo> hiddenList = new List<BeatmapSetInfo>();
loadBeatmaps(new List<BeatmapSetInfo> { hidingSet });
AddStep("create hidden set", () =>
{
hidingSet = createTestBeatmapSet(1);
hidingSet.Beatmaps[1].Hidden = true;
hiddenList.Clear();
hiddenList.Add(hidingSet);
});
loadBeatmaps(hiddenList);
setSelected(1, 1);
@ -353,9 +380,14 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test]
public void TestSelectingFilteredRuleset()
{
var testMixed = createTestBeatmapSet(set_count + 1);
BeatmapSetInfo testMixed = null;
createCarousel();
AddStep("add mixed ruleset beatmapset", () =>
{
testMixed = createTestBeatmapSet(set_count + 1);
for (int i = 0; i <= 2; i++)
{
testMixed.Beatmaps[i].Ruleset = rulesets.AvailableRulesets.ElementAt(i);
@ -407,6 +439,8 @@ namespace osu.Game.Tests.Visual.SongSelect
private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null)
{
createCarousel();
if (beatmapSets == null)
{
beatmapSets = new List<BeatmapSetInfo>();
@ -426,6 +460,20 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("Wait for load", () => changed);
}
private void createCarousel(Container target = null)
{
AddStep("Create carousel", () =>
{
selectedSets.Clear();
eagerSelectedIDs.Clear();
(target ?? this).Child = carousel = new TestBeatmapCarousel
{
RelativeSizeAxes = Axes.Both,
};
});
}
private void ensureRandomFetchSuccess() =>
AddAssert("ensure prev random fetch worked", () => selectedSets.Peek() == carousel.SelectedBeatmapSet);
@ -445,8 +493,10 @@ namespace osu.Game.Tests.Visual.SongSelect
private void advanceSelection(bool diff, int direction = 1, int count = 1)
{
if (count == 1)
{
AddStep($"select {(direction > 0 ? "next" : "prev")} {(diff ? "diff" : "set")}", () =>
carousel.SelectNext(direction, !diff));
}
else
{
AddRepeatStep($"select {(direction > 0 ? "next" : "prev")} {(diff ? "diff" : "set")}", () =>

View File

@ -16,6 +16,7 @@ using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
@ -34,6 +35,8 @@ namespace osu.Game.Tests.Visual.SongSelect
private RulesetStore rulesets;
private MusicController music;
private WorkingBeatmap defaultBeatmap;
public override IReadOnlyList<Type> RequiredTypes => new[]
@ -79,6 +82,11 @@ namespace osu.Game.Tests.Visual.SongSelect
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default));
Dependencies.Cache(music = new MusicController());
// required to get bindables attached
Add(music);
Beatmap.SetDefault();
Dependencies.Cache(config = new OsuConfigManager(LocalStorage));
@ -93,6 +101,59 @@ namespace osu.Game.Tests.Visual.SongSelect
manager?.Delete(manager.GetAllUsableBeatmapSets());
});
[Test]
public void TestAudioResuming()
{
createSongSelect();
addRulesetImportStep(0);
addRulesetImportStep(0);
checkMusicPlaying(true);
AddStep("select first", () => songSelect.Carousel.SelectBeatmap(songSelect.Carousel.BeatmapSets.First().Beatmaps.First()));
checkMusicPlaying(true);
AddStep("manual pause", () => music.TogglePause());
checkMusicPlaying(false);
AddStep("select next difficulty", () => songSelect.Carousel.SelectNext(skipDifficulties: false));
checkMusicPlaying(false);
AddStep("select next set", () => songSelect.Carousel.SelectNext());
checkMusicPlaying(true);
}
[TestCase(false)]
[TestCase(true)]
public void TestAudioRemainsCorrectOnRulesetChange(bool rulesetsInSameBeatmap)
{
createSongSelect();
// start with non-osu! to avoid convert confusion
changeRuleset(1);
if (rulesetsInSameBeatmap)
{
AddStep("import multi-ruleset map", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
manager.Import(createTestBeatmapSet(0, usableRulesets)).Wait();
});
}
else
{
addRulesetImportStep(1);
addRulesetImportStep(0);
}
checkMusicPlaying(true);
AddStep("manual pause", () => music.TogglePause());
checkMusicPlaying(false);
changeRuleset(0);
checkMusicPlaying(!rulesetsInSameBeatmap);
}
[Test]
public void TestDummy()
{
@ -128,12 +189,10 @@ namespace osu.Game.Tests.Visual.SongSelect
}
[Test]
[Ignore("needs fixing")]
public void TestImportUnderDifferentRuleset()
{
createSongSelect();
changeRuleset(2);
addRulesetImportStep(0);
addRulesetImportStep(2);
AddUntilStep("no selection", () => songSelect.Carousel.SelectedBeatmap == null);
}
@ -183,6 +242,22 @@ namespace osu.Game.Tests.Visual.SongSelect
void onRulesetChange(ValueChangedEvent<RulesetInfo> e) => rulesetChangeIndex = actionIndex++;
}
[Test]
public void TestModsRetainedBetweenSongSelect()
{
AddAssert("empty mods", () => !Mods.Value.Any());
createSongSelect();
addRulesetImportStep(0);
changeMods(new OsuModHardRock());
createSongSelect();
AddAssert("mods retained", () => Mods.Value.Any());
}
[Test]
public void TestStartAfterUnMatchingFilterDoesNotStart()
{
@ -224,6 +299,9 @@ namespace osu.Game.Tests.Visual.SongSelect
private static int importId;
private int getImportId() => ++importId;
private void checkMusicPlaying(bool playing) =>
AddUntilStep($"music {(playing ? "" : "not ")}playing", () => music.IsPlaying == playing);
private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => Mods.Value = mods);
private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id));
@ -289,5 +367,11 @@ namespace osu.Game.Tests.Visual.SongSelect
DateAdded = DateTimeOffset.UtcNow,
};
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
rulesets?.Dispose();
}
}
}

View File

@ -109,16 +109,20 @@ namespace osu.Game.Tests.Visual
AddAssert("check OsuGame DI members", () =>
{
foreach (var type in requiredGameDependencies)
{
if (game.Dependencies.Get(type) == null)
throw new Exception($"{type} has not been cached");
}
return true;
});
AddAssert("check OsuGameBase DI members", () =>
{
foreach (var type in requiredGameBaseDependencies)
{
if (gameBase.Dependencies.Get(type) == null)
throw new Exception($"{type} has not been cached");
}
return true;
});

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
@ -10,7 +11,6 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Lists;
using osu.Framework.Timing;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
@ -153,7 +153,7 @@ namespace osu.Game.Tests.Visual.UserInterface
};
}
private SortedList<TimingControlPoint> timingPoints => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints;
private List<TimingControlPoint> timingPoints => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.ToList();
private TimingControlPoint getNextTimingPoint(TimingControlPoint current)
{

View File

@ -11,7 +11,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneLabelledComponent : OsuTestScene
public class TestSceneLabelledDrawable : OsuTestScene
{
[TestCase(false)]
[TestCase(true)]
@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{
AddStep("create component", () =>
{
LabelledComponent<Drawable> component;
LabelledDrawable<Drawable> component;
Child = new Container
{
@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.UserInterface
Origin = Anchor.Centre,
Width = 500,
AutoSizeAxes = Axes.Y,
Child = component = padded ? (LabelledComponent<Drawable>)new PaddedLabelledComponent() : new NonPaddedLabelledComponent(),
Child = component = padded ? (LabelledDrawable<Drawable>)new PaddedLabelledDrawable() : new NonPaddedLabelledDrawable(),
};
component.Label = "a sample component";
@ -41,9 +41,9 @@ namespace osu.Game.Tests.Visual.UserInterface
});
}
private class PaddedLabelledComponent : LabelledComponent<Drawable>
private class PaddedLabelledDrawable : LabelledDrawable<Drawable>
{
public PaddedLabelledComponent()
public PaddedLabelledDrawable()
: base(true)
{
}
@ -57,9 +57,9 @@ namespace osu.Game.Tests.Visual.UserInterface
};
}
private class NonPaddedLabelledComponent : LabelledComponent<Drawable>
private class NonPaddedLabelledDrawable : LabelledDrawable<Drawable>
{
public NonPaddedLabelledComponent()
public NonPaddedLabelledDrawable()
: base(false)
{
}

View File

@ -7,7 +7,6 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2;
namespace osu.Game.Tests.Visual.UserInterface
@ -28,7 +27,7 @@ namespace osu.Game.Tests.Visual.UserInterface
{
AddStep("create component", () =>
{
LabelledComponent<OsuTextBox> component;
LabelledTextBox component;
Child = new Container
{

View File

@ -0,0 +1,145 @@
// 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.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneStatefulMenuItem : ManualInputManagerTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(OsuMenu),
typeof(StatefulMenuItem),
typeof(TernaryStateMenuItem),
typeof(DrawableStatefulMenuItem),
};
[Test]
public void TestTernaryMenuItem()
{
OsuMenu menu = null;
Bindable<TernaryState> state = new Bindable<TernaryState>(TernaryState.Indeterminate);
AddStep("create menu", () =>
{
state.Value = TernaryState.Indeterminate;
Child = menu = new OsuMenu(Direction.Vertical, true)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Items = new[]
{
new TernaryStateMenuItem("First"),
new TernaryStateMenuItem("Second") { State = { BindTarget = state } },
new TernaryStateMenuItem("Third") { State = { Value = TernaryState.True } },
}
};
});
checkState(TernaryState.Indeterminate);
click();
checkState(TernaryState.True);
click();
checkState(TernaryState.False);
click();
checkState(TernaryState.True);
click();
checkState(TernaryState.False);
AddStep("change state via bindable", () => state.Value = TernaryState.True);
void click() =>
AddStep("click", () =>
{
InputManager.MoveMouseTo(menu.ScreenSpaceDrawQuad.Centre);
InputManager.Click(MouseButton.Left);
});
void checkState(TernaryState expected)
=> AddAssert($"state is {expected}", () => state.Value == expected);
}
[Test]
public void TestCustomState()
{
AddStep("create menu", () =>
{
Child = new OsuMenu(Direction.Vertical, true)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Items = new[]
{
new TestMenuItem("First", MenuItemType.Standard, getNextState),
new TestMenuItem("Second", MenuItemType.Standard, getNextState) { State = { Value = TestStates.State2 } },
new TestMenuItem("Third", MenuItemType.Standard, getNextState) { State = { Value = TestStates.State3 } },
}
};
});
}
private TestStates getNextState(TestStates state)
{
switch (state)
{
case TestStates.State1:
return TestStates.State2;
case TestStates.State2:
return TestStates.State3;
case TestStates.State3:
return TestStates.State1;
}
return TestStates.State1;
}
private class TestMenuItem : StatefulMenuItem<TestStates>
{
public TestMenuItem(string text, MenuItemType type, Func<TestStates, TestStates> changeStateFunc)
: base(text, changeStateFunc, type)
{
}
public override IconUsage? GetIconForState(TestStates state)
{
switch (state)
{
case TestStates.State1:
return FontAwesome.Solid.DiceOne;
case TestStates.State2:
return FontAwesome.Solid.DiceTwo;
case TestStates.State3:
return FontAwesome.Solid.DiceThree;
default:
throw new ArgumentOutOfRangeException(nameof(state), state, null);
}
}
}
private enum TestStates
{
State1,
State2,
State3
}
}
}

View File

@ -0,0 +1,34 @@
// 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 osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneToggleMenuItem : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(OsuMenu),
typeof(ToggleMenuItem),
typeof(DrawableStatefulMenuItem)
};
public TestSceneToggleMenuItem()
{
Add(new OsuMenu(Direction.Vertical, true)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Items = new[]
{
new ToggleMenuItem("First"),
new ToggleMenuItem("Second") { State = { Value = true } }
}
});
}
}
}

View File

@ -12,7 +12,7 @@ using osu.Game.Graphics.Sprites;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
public class TestSceneWaveContainer : OsuTestScene

View File

@ -3,14 +3,14 @@
<ItemGroup Label="Package References">
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup>
<PropertyGroup Label="Project">
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />