From 5e66b021085c1214da2c350ebe96688c5aabf0f2 Mon Sep 17 00:00:00 2001
From: smoogipoo <smoogipoo@smgi.me>
Date: Wed, 13 Jun 2018 18:38:27 +0900
Subject: [PATCH 1/5] Process beatmap before generating mappings

---
 .../Tests/Beatmaps/BeatmapConversionTest.cs   | 21 ++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
index 7470f6ebed..c5f74af770 100644
--- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
+++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
@@ -84,19 +84,26 @@ namespace osu.Game.Tests.Beatmaps
             beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo();
 
             var result = new ConvertResult();
-
             var converter = rulesetInstance.CreateBeatmapConverter(beatmap);
+
+            List<KeyValuePair<HitObject, IEnumerable<HitObject>>> conversions = new List<KeyValuePair<HitObject, IEnumerable<HitObject>>>();
+
             converter.ObjectConverted += (orig, converted) =>
             {
+                conversions.Add(new KeyValuePair<HitObject, IEnumerable<HitObject>>(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.AddRange(CreateConvertValue(obj));
-                result.Mappings.Add(mapping);
             };
 
-            converter.Convert();
+            IBeatmap convertedBeatmap = converter.Convert();
+            rulesetInstance.CreateBeatmapProcessor(convertedBeatmap).PostProcess();
+
+            foreach (var pair in conversions)
+            {
+                var mapping = new ConvertMapping { StartTime = pair.Key.StartTime };
+                foreach (var obj in pair.Value)
+                    mapping.Objects.AddRange(CreateConvertValue(obj));
+                result.Mappings.Add(mapping);
+            }
 
             return result;
         }

From 8d0e7abdd62c977ffc53d408430faf967604ced3 Mon Sep 17 00:00:00 2001
From: smoogipoo <smoogipoo@smgi.me>
Date: Wed, 13 Jun 2018 21:17:27 +0900
Subject: [PATCH 2/5] Some rulesets don't have a beatmap processor

---
 osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
index c5f74af770..4b0da3a14b 100644
--- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
+++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
@@ -86,7 +86,7 @@ namespace osu.Game.Tests.Beatmaps
             var result = new ConvertResult();
             var converter = rulesetInstance.CreateBeatmapConverter(beatmap);
 
-            List<KeyValuePair<HitObject, IEnumerable<HitObject>>> conversions = new List<KeyValuePair<HitObject, IEnumerable<HitObject>>>();
+            var conversions = new List<KeyValuePair<HitObject, IEnumerable<HitObject>>>();
 
             converter.ObjectConverted += (orig, converted) =>
             {
@@ -95,7 +95,7 @@ namespace osu.Game.Tests.Beatmaps
             };
 
             IBeatmap convertedBeatmap = converter.Convert();
-            rulesetInstance.CreateBeatmapProcessor(convertedBeatmap).PostProcess();
+            rulesetInstance.CreateBeatmapProcessor(convertedBeatmap)?.PostProcess();
 
             foreach (var pair in conversions)
             {

From 024d2abfe09ea3c1200a587ebcef17311d738238 Mon Sep 17 00:00:00 2001
From: smoogipoo <smoogipoo@smgi.me>
Date: Thu, 14 Jun 2018 20:26:55 +0900
Subject: [PATCH 3/5] Always generate mappings/convert values as soon as
 objects are converted

# Conflicts:
#	osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs
---
 osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 16 +++++-----------
 1 file changed, 5 insertions(+), 11 deletions(-)

diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
index 4b0da3a14b..424ebe0cf3 100644
--- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
+++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
@@ -86,25 +86,19 @@ namespace osu.Game.Tests.Beatmaps
             var result = new ConvertResult();
             var converter = rulesetInstance.CreateBeatmapConverter(beatmap);
 
-            var conversions = new List<KeyValuePair<HitObject, IEnumerable<HitObject>>>();
-
             converter.ObjectConverted += (orig, converted) =>
             {
-                conversions.Add(new KeyValuePair<HitObject, IEnumerable<HitObject>>(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.AddRange(CreateConvertValue(obj));
+                result.Mappings.Add(mapping);
             };
 
             IBeatmap convertedBeatmap = converter.Convert();
             rulesetInstance.CreateBeatmapProcessor(convertedBeatmap)?.PostProcess();
 
-            foreach (var pair in conversions)
-            {
-                var mapping = new ConvertMapping { StartTime = pair.Key.StartTime };
-                foreach (var obj in pair.Value)
-                    mapping.Objects.AddRange(CreateConvertValue(obj));
-                result.Mappings.Add(mapping);
-            }
-
             return result;
         }
 

From b99b520656dc8ae9cda7fc5ef1da13801e2ae858 Mon Sep 17 00:00:00 2001
From: smoogipoo <smoogipoo@smgi.me>
Date: Thu, 14 Jun 2018 20:28:29 +0900
Subject: [PATCH 4/5] Allow convertmapping to be extended

---
 .../Tests/Beatmaps/BeatmapConversionTest.cs   | 68 +++++++++++++++----
 1 file changed, 54 insertions(+), 14 deletions(-)

diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
index 424ebe0cf3..9f15949f7b 100644
--- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
+++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
@@ -16,7 +16,8 @@ using osu.Game.Rulesets.Objects;
 namespace osu.Game.Tests.Beatmaps
 {
     [TestFixture]
-    public abstract class BeatmapConversionTest<TConvertValue>
+    public abstract class BeatmapConversionTest<TConvertMapping, TConvertValue>
+        where TConvertMapping : ConvertMapping<TConvertValue>, IEquatable<TConvertMapping>, new()
         where TConvertValue : IEquatable<TConvertValue>
     {
         private const string resource_namespace = "Testing.Beatmaps";
@@ -59,9 +60,13 @@ namespace osu.Game.Tests.Beatmaps
                                 else if (objectCounter >= expectedMapping.Objects.Count)
                                     Assert.Fail($"The conversion generated a hitobject, but should not have, for hitobject at time: {ourMapping.StartTime}:\n"
                                                 + $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n");
-                                else if (!EqualityComparer<TConvertValue>.Default.Equals(expectedMapping.Objects[objectCounter], ourMapping.Objects[objectCounter]))
+                                else if (!expectedMapping.Equals(ourMapping))
+                                    Assert.Fail($"The conversion mapping differed for object at time {expectedMapping.StartTime}:\n"
+                                                + $"Expected {JsonConvert.SerializeObject(expectedMapping)}\n"
+                                                + $"Received: {JsonConvert.SerializeObject(ourMapping)}");
+                                else if (!expectedMapping.Objects[objectCounter].Equals(ourMapping.Objects[objectCounter]))
                                 {
-                                    Assert.Fail($"The conversion generated differing hitobjects for object at time: {expectedMapping.StartTime}\n"
+                                    Assert.Fail($"The conversion generated differing hitobjects for object at time: {expectedMapping.StartTime}:\n"
                                                 + $"Expected: {JsonConvert.SerializeObject(expectedMapping.Objects[objectCounter])}\n"
                                                 + $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n");
                                 }
@@ -90,7 +95,9 @@ namespace osu.Game.Tests.Beatmaps
             {
                 converted.ForEach(h => h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty));
 
-                var mapping = new ConvertMapping { StartTime = orig.StartTime };
+                var mapping = CreateConvertMapping();
+                mapping.StartTime = orig.StartTime;
+
                 foreach (var obj in converted)
                     mapping.Objects.AddRange(CreateConvertValue(obj));
                 result.Mappings.Add(mapping);
@@ -129,21 +136,54 @@ namespace osu.Game.Tests.Beatmaps
             return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}");
         }
 
-        protected abstract IEnumerable<TConvertValue> CreateConvertValue(HitObject hitObject);
-        protected abstract Ruleset CreateRuleset();
+        /// <summary>
+        /// Creates the conversion mapping for a <see cref="HitObject"/>. A conversion mapping stores important information about the conversion process.
+        /// This is generated _after_ the <see cref="HitObject"/> has been converted.
+        /// <para>
+        /// This should be used to validate the integrity of the conversion process after a conversion has occurred.
+        /// </para>
+        /// </summary>
+        protected virtual TConvertMapping CreateConvertMapping() => new TConvertMapping();
 
-        private class ConvertMapping
-        {
-            [JsonProperty]
-            public double StartTime;
-            [JsonProperty]
-            public List<TConvertValue> Objects = new List<TConvertValue>();
-        }
+        /// <summary>
+        /// Creates the conversion value for a <see cref="HitObject"/>. A conversion value stores information about the converted <see cref="HitObject"/>.
+        /// <para>
+        /// This should be used to validate the integrity of the converted <see cref="HitObject"/>.
+        /// </para>
+        /// </summary>
+        /// <param name="hitObject">The converted <see cref="HitObject"/>.</param>
+        protected abstract IEnumerable<TConvertValue> CreateConvertValue(HitObject hitObject);
+
+        /// <summary>
+        /// Creates the <see cref="Ruleset"/> applicable to this <see cref="BeatmapConversionTest{TConvertMapping,TConvertValue}"/>.
+        /// </summary>
+        /// <returns></returns>
+        protected abstract Ruleset CreateRuleset();
 
         private class ConvertResult
         {
             [JsonProperty]
-            public List<ConvertMapping> Mappings = new List<ConvertMapping>();
+            public List<TConvertMapping> Mappings = new List<TConvertMapping>();
         }
     }
+
+    public abstract class BeatmapConversionTest<TConvertValue> : BeatmapConversionTest<ConvertMapping<TConvertValue>, TConvertValue>
+        where TConvertValue : IEquatable<TConvertValue>
+    {
+    }
+
+    public class ConvertMapping<TConvertValue> : IEquatable<ConvertMapping<TConvertValue>>
+        where TConvertValue : IEquatable<TConvertValue>
+    {
+        [JsonProperty]
+        public double StartTime;
+
+        [JsonIgnore]
+        public List<TConvertValue> Objects = new List<TConvertValue>();
+
+        [JsonProperty("Objects")]
+        private List<TConvertValue> setObjects { set => Objects = value; }
+
+        public virtual bool Equals(ConvertMapping<TConvertValue> other) => StartTime.Equals(other?.StartTime);
+    }
 }

From a4d236408378ea34fd93af06e6c77d775072c59e Mon Sep 17 00:00:00 2001
From: smoogipoo <smoogipoo@smgi.me>
Date: Thu, 14 Jun 2018 21:29:08 +0900
Subject: [PATCH 5/5] Add one more newline

---
 osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
index 9f15949f7b..cf4dda52a8 100644
--- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
+++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
@@ -63,7 +63,7 @@ namespace osu.Game.Tests.Beatmaps
                                 else if (!expectedMapping.Equals(ourMapping))
                                     Assert.Fail($"The conversion mapping differed for object at time {expectedMapping.StartTime}:\n"
                                                 + $"Expected {JsonConvert.SerializeObject(expectedMapping)}\n"
-                                                + $"Received: {JsonConvert.SerializeObject(ourMapping)}");
+                                                + $"Received: {JsonConvert.SerializeObject(ourMapping)}\n");
                                 else if (!expectedMapping.Objects[objectCounter].Equals(ourMapping.Objects[objectCounter]))
                                 {
                                     Assert.Fail($"The conversion generated differing hitobjects for object at time: {expectedMapping.StartTime}:\n"