mirror of
https://github.com/ppy/osu.git
synced 2025-03-15 17:47:18 +08:00
Implement basic structure for beatmap conversion testing
This commit is contained in:
parent
99e5eb03f6
commit
797d03a65f
File diff suppressed because one or more lines are too long
1824
osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/875945.osu
Normal file
1824
osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/875945.osu
Normal file
File diff suppressed because it is too large
Load Diff
70
osu.Game.Rulesets.Osu/Tests/OsuBeatmapConversionTest.cs
Normal file
70
osu.Game.Rulesets.Osu/Tests/OsuBeatmapConversionTest.cs
Normal file
@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public class OsuBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||
|
||||
[TestCase(875945)]
|
||||
public new void Test(int beatmapId)
|
||||
{
|
||||
base.Test(beatmapId);
|
||||
}
|
||||
|
||||
protected override ConvertValue CreateConvertValue(HitObject hitObject)
|
||||
{
|
||||
var startPosition = (hitObject as IHasPosition)?.Position ?? new Vector2(256, 192);
|
||||
var endPosition = (hitObject as Slider)?.EndPosition ?? startPosition;
|
||||
|
||||
return new ConvertValue
|
||||
{
|
||||
StartTime = hitObject.StartTime,
|
||||
EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime,
|
||||
StartX = startPosition.X,
|
||||
StartY = startPosition.Y,
|
||||
EndX = endPosition.X,
|
||||
EndY = endPosition.Y
|
||||
};
|
||||
}
|
||||
|
||||
protected override ITestableBeatmapConverter CreateConverter() => new OsuBeatmapConverter();
|
||||
}
|
||||
|
||||
public struct ConvertValue : IEquatable<ConvertValue>
|
||||
{
|
||||
[JsonProperty]
|
||||
public double StartTime;
|
||||
[JsonProperty]
|
||||
public double EndTime;
|
||||
[JsonProperty]
|
||||
public float StartX;
|
||||
[JsonProperty]
|
||||
public float StartY;
|
||||
[JsonProperty]
|
||||
public float EndX;
|
||||
[JsonProperty]
|
||||
public float EndY;
|
||||
|
||||
public bool Equals(ConvertValue other)
|
||||
=> Precision.AlmostEquals(StartTime, other.StartTime, 1)
|
||||
&& Precision.AlmostEquals(EndTime, other.EndTime, 1)
|
||||
&& Precision.AlmostEquals(StartX, other.StartX, 1)
|
||||
&& Precision.AlmostEquals(StartY, other.StartY, 1)
|
||||
&& Precision.AlmostEquals(EndX, other.EndX, 1)
|
||||
&& Precision.AlmostEquals(EndY, other.EndY, 1);
|
||||
}
|
||||
}
|
@ -37,6 +37,9 @@
|
||||
<HintPath>$(SolutionDir)\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed">
|
||||
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.8.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
@ -127,6 +130,7 @@
|
||||
<Compile Include="OsuDifficulty\Utils\History.cs" />
|
||||
<Compile Include="OsuInputManager.cs" />
|
||||
<Compile Include="Replays\OsuReplayInputHandler.cs" />
|
||||
<Compile Include="Tests\OsuBeatmapConversionTest.cs" />
|
||||
<Compile Include="Tests\TestCaseHitCircle.cs" />
|
||||
<Compile Include="Tests\TestCaseHitCircleHidden.cs" />
|
||||
<Compile Include="Tests\TestCasePerformancePoints.cs" />
|
||||
@ -172,6 +176,10 @@
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\Testing\Beatmaps\875945-expected-conversion.json" />
|
||||
<EmbeddedResource Include="Resources\Testing\Beatmaps\875945.osu" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
|
@ -8,12 +8,36 @@ using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
public interface ITestableBeatmapConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked when a <see cref="HitObject"/> has been converted.
|
||||
/// The first argument contains the <see cref="HitObject"/> that was converted.
|
||||
/// The second argument contains the <see cref="HitObject"/>s that were output from the conversion process.
|
||||
/// </summary>
|
||||
event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Beatmap using this Beatmap Converter.
|
||||
/// </summary>
|
||||
/// <param name="original">The un-converted Beatmap.</param>
|
||||
void Convert(Beatmap beatmap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Beatmap for another mode.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of HitObject stored in the Beatmap.</typeparam>
|
||||
public abstract class BeatmapConverter<T> where T : HitObject
|
||||
public abstract class BeatmapConverter<T> : ITestableBeatmapConverter
|
||||
where T : HitObject
|
||||
{
|
||||
private event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
|
||||
event Action<HitObject, IEnumerable<HitObject>> ITestableBeatmapConverter.ObjectConverted
|
||||
{
|
||||
add => ObjectConverted += value;
|
||||
remove => ObjectConverted -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a Beatmap can be converted using this Beatmap Converter.
|
||||
/// </summary>
|
||||
@ -32,6 +56,8 @@ namespace osu.Game.Beatmaps
|
||||
return ConvertBeatmap(new Beatmap(original));
|
||||
}
|
||||
|
||||
void ITestableBeatmapConverter.Convert(Beatmap original) => Convert(original);
|
||||
|
||||
/// <summary>
|
||||
/// Performs the conversion of a Beatmap using this Beatmap Converter.
|
||||
/// </summary>
|
||||
@ -63,8 +89,11 @@ namespace osu.Game.Beatmaps
|
||||
yield break;
|
||||
}
|
||||
|
||||
var converted = ConvertHitObject(original, beatmap).ToList();
|
||||
ObjectConverted?.Invoke(original, converted);
|
||||
|
||||
// Convert the hit object
|
||||
foreach (var obj in ConvertHitObject(original, beatmap))
|
||||
foreach (var obj in converted)
|
||||
{
|
||||
if (obj == null)
|
||||
continue;
|
||||
|
@ -2,7 +2,9 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using osu.Framework.Testing;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
@ -16,6 +18,9 @@ using System.Runtime.InteropServices;
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
[assembly: InternalsVisibleTo("osu.Game.Tests")]
|
||||
[assembly: InternalsVisibleTo(DynamicClassCompiler.DYNAMIC_ASSEMBLY_NAME)]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
|
139
osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
Normal file
139
osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
Normal file
@ -0,0 +1,139 @@
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps
|
||||
{
|
||||
[TestFixture]
|
||||
public abstract class BeatmapConversionTest<TConvertValue>
|
||||
where TConvertValue : IEquatable<TConvertValue>
|
||||
{
|
||||
private const string resource_namespace = "Testing.Beatmaps";
|
||||
private const string expected_conversion_suffix = "-expected-conversion";
|
||||
|
||||
protected abstract string ResourceAssembly { get; }
|
||||
|
||||
protected void Test(int beatmapId)
|
||||
{
|
||||
var ourResult = convert(beatmapId);
|
||||
var expectedResult = read(beatmapId);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
int mappingCounter = 0;
|
||||
while (true)
|
||||
{
|
||||
if (mappingCounter >= ourResult.Mappings.Count && mappingCounter >= expectedResult.Mappings.Count)
|
||||
break;
|
||||
if (mappingCounter >= ourResult.Mappings.Count)
|
||||
Assert.Fail($"Missing conversion for object at time: {expectedResult.Mappings[mappingCounter].StartTime}");
|
||||
else if (mappingCounter >= expectedResult.Mappings.Count)
|
||||
Assert.Fail($"Extra conversion for object at time: {ourResult.Mappings[mappingCounter].StartTime}");
|
||||
else
|
||||
{
|
||||
var counter = mappingCounter;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
var ourMapping = ourResult.Mappings[counter];
|
||||
var expectedMapping = expectedResult.Mappings[counter];
|
||||
|
||||
int objectCounter = 0;
|
||||
while (true)
|
||||
{
|
||||
if (objectCounter >= ourMapping.Objects.Count && objectCounter >= expectedMapping.Objects.Count)
|
||||
break;
|
||||
if (objectCounter >= ourMapping.Objects.Count)
|
||||
Assert.Fail($"Expected conversion for object at time: {expectedMapping.StartTime}:\n{JsonConvert.SerializeObject(expectedMapping.Objects[objectCounter])}");
|
||||
else if (objectCounter >= expectedMapping.Objects.Count)
|
||||
Assert.Fail($"Unexpected conversion for object at time: {ourMapping.StartTime}:\n{JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}");
|
||||
else if (!EqualityComparer<TConvertValue>.Default.Equals(expectedMapping.Objects[objectCounter], ourMapping.Objects[objectCounter]))
|
||||
{
|
||||
Assert.Fail($"Converted hitobjects differ for object at time: {expectedMapping.StartTime}\n"
|
||||
+ $"Expected: {JsonConvert.SerializeObject(expectedMapping.Objects[objectCounter])}\n"
|
||||
+ $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n");
|
||||
}
|
||||
|
||||
objectCounter++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
mappingCounter++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ConvertResult convert(int beatmapId)
|
||||
{
|
||||
var beatmap = getBeatmap(beatmapId);
|
||||
|
||||
var result = new ConvertResult();
|
||||
|
||||
var converter = CreateConverter();
|
||||
converter.ObjectConverted += (orig, converted) =>
|
||||
{
|
||||
converted.ForEach(h => h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty));
|
||||
|
||||
var mapping = new ConvertMapping { StartTime = orig.StartTime };
|
||||
foreach (var obj in converted)
|
||||
mapping.Objects.Add(CreateConvertValue(obj));
|
||||
result.Mappings.Add(mapping);
|
||||
};
|
||||
|
||||
converter.Convert(beatmap);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private ConvertResult read(int beatmapId)
|
||||
{
|
||||
using (var resStream = openResource($"{resource_namespace}.{beatmapId}{expected_conversion_suffix}.json"))
|
||||
using (var reader = new StreamReader(resStream))
|
||||
{
|
||||
var contents = reader.ReadToEnd();
|
||||
return JsonConvert.DeserializeObject<ConvertResult>(contents);
|
||||
}
|
||||
}
|
||||
|
||||
private Beatmap getBeatmap(int beatmapId)
|
||||
{
|
||||
var decoder = new LegacyBeatmapDecoder();
|
||||
using (var resStream = openResource($"{resource_namespace}.{beatmapId}.osu"))
|
||||
using (var stream = new StreamReader(resStream))
|
||||
return decoder.DecodeBeatmap(stream);
|
||||
}
|
||||
|
||||
private Stream openResource(string name)
|
||||
{
|
||||
var localPath = Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(Assembly.GetExecutingAssembly().CodeBase).Path));
|
||||
return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}");
|
||||
}
|
||||
|
||||
protected abstract TConvertValue CreateConvertValue(HitObject hitObject);
|
||||
protected abstract ITestableBeatmapConverter CreateConverter();
|
||||
|
||||
private class ConvertMapping
|
||||
{
|
||||
[JsonProperty]
|
||||
public double StartTime;
|
||||
[JsonProperty]
|
||||
public List<TConvertValue> Objects = new List<TConvertValue>();
|
||||
}
|
||||
|
||||
private class ConvertResult
|
||||
{
|
||||
[JsonProperty]
|
||||
public List<ConvertMapping> Mappings = new List<ConvertMapping>();
|
||||
}
|
||||
}
|
||||
}
|
@ -883,6 +883,7 @@
|
||||
<Compile Include="Storyboards\StoryboardLayer.cs" />
|
||||
<Compile Include="Storyboards\StoryboardSample.cs" />
|
||||
<Compile Include="Storyboards\StoryboardSprite.cs" />
|
||||
<Compile Include="Tests\Beatmaps\BeatmapConversionTest.cs" />
|
||||
<Compile Include="Tests\Beatmaps\TestWorkingBeatmap.cs" />
|
||||
<Compile Include="Tests\CleanRunHeadlessGameHost.cs" />
|
||||
<Compile Include="Tests\Platform\TestStorage.cs" />
|
||||
@ -939,4 +940,4 @@
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
|
||||
<Import Project="$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('$(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.8\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" />
|
||||
</Project>
|
||||
</Project>
|
Loading…
x
Reference in New Issue
Block a user