diff --git a/tests/unit/assets/preload/data/notestyles/funkin.json b/tests/unit/assets/preload/data/notestyles/funkin.json
index abb039150..a36dd8c1c 100644
--- a/tests/unit/assets/preload/data/notestyles/funkin.json
+++ b/tests/unit/assets/preload/data/notestyles/funkin.json
@@ -7,7 +7,7 @@
     "note": {
       "assetPath": "shared:arrows",
       "scale": 1.0,
-      "isPixel": true,
+      "isPixel": false,
       "data": {
         "left": { "prefix": "noteLeft" },
         "down": { "prefix": "noteDown" },
@@ -19,7 +19,7 @@
       "assetPath": "shared:arrows",
       "scale": 1.0,
       "offsets": [28, 32],
-      "isPixel": true,
+      "isPixel": false,
       "data": {
         "leftStatic": { "prefix": "staticLeft0" },
         "leftPress": { "prefix": "pressedLeft0" },
diff --git a/tests/unit/assets/preload/data/songs/bopeebo-swapped/bopeebo-swapped-chart.json b/tests/unit/assets/preload/data/songs/bopeebo-swapped/bopeebo-swapped-chart.json
new file mode 100644
index 000000000..c7f7629d6
--- /dev/null
+++ b/tests/unit/assets/preload/data/songs/bopeebo-swapped/bopeebo-swapped-chart.json
@@ -0,0 +1,15 @@
+{
+  "version": "2.0.0",
+  "songName": "Bopeebo",
+  "artist": "Kawai Sprite",
+  "timeFormat": "ms",
+  "timeChanges": [{ "t": 0, "bpm": 100, "n": 4, "d": 4, "bt": [4, 4, 4, 4] }],
+  "playData": {
+    "songVariations": [],
+    "difficulties": ["easy", "normal", "hard"],
+    "playableChars": { "bf": { "g": "gf", "o": "dad" } },
+    "stage": "mainStage",
+    "noteSkin": "Normal"
+  },
+  "generatedBy": "MasterEric (by hand)"
+}
diff --git a/tests/unit/assets/preload/data/songs/bopeebo-swapped/bopeebo-swapped-metadata.json b/tests/unit/assets/preload/data/songs/bopeebo-swapped/bopeebo-swapped-metadata.json
new file mode 100644
index 000000000..53817f96a
--- /dev/null
+++ b/tests/unit/assets/preload/data/songs/bopeebo-swapped/bopeebo-swapped-metadata.json
@@ -0,0 +1,1738 @@
+{
+  "version": "2.0.0",
+  "scrollSpeed": {
+    "default": 1.0,
+    "hard": 1.3
+  },
+  "events": [
+    { "t": 0, "e": "FocusCamera", "v": 1 },
+    { "t": 2400, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 4200,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 4800, "e": "FocusCamera", "v": 1 },
+    { "t": 7200, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 9000,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 9600, "e": "FocusCamera", "v": 1 },
+    { "t": 12000, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 13800,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 14400, "e": "FocusCamera", "v": 1 },
+    { "t": 16800, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 18600,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 19200, "e": "FocusCamera", "v": 1 },
+    { "t": 21600, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 23400,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 24000, "e": "FocusCamera", "v": 1 },
+    { "t": 26400, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 28200,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 28800, "e": "FocusCamera", "v": 1 },
+    { "t": 31200, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 33000,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 33600, "e": "FocusCamera", "v": 1 },
+    { "t": 36000, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 37800,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 38400, "e": "FocusCamera", "v": 1 },
+    { "t": 40800, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 42600,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 43200, "e": "FocusCamera", "v": 1 },
+    { "t": 45600, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 47400,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 48000, "e": "FocusCamera", "v": 1 },
+    { "t": 50400, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 52200,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 52800, "e": "FocusCamera", "v": 1 },
+    { "t": 55200, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 57000,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 57600, "e": "FocusCamera", "v": 1 },
+    { "t": 60000, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 61800,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 62400, "e": "FocusCamera", "v": 1 },
+    { "t": 64800, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 66600,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 67200, "e": "FocusCamera", "v": 1 },
+    { "t": 69600, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 71400,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 72000, "e": "FocusCamera", "v": 1 },
+    { "t": 74400, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 76200,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    }
+  ],
+  "notes": {
+    "easy": [
+      {
+        "t": 0,
+        "d": 6
+      },
+      {
+        "t": 600,
+        "d": 7,
+        "l": 450
+      },
+      {
+        "t": 1200,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 2400,
+        "d": 2
+      },
+      {
+        "t": 3000,
+        "d": 3,
+        "l": 450
+      },
+      {
+        "t": 3600,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 4800,
+        "d": 5,
+        "l": 300
+      },
+      {
+        "t": 5400,
+        "d": 4,
+        "l": 300
+      },
+      {
+        "t": 6000,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 7200,
+        "d": 1,
+        "l": 300
+      },
+      {
+        "t": 7800,
+        "d": 0,
+        "l": 300
+      },
+      {
+        "t": 8400,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 9600,
+        "d": 5
+      },
+      {
+        "t": 10200,
+        "d": 7
+      },
+      {
+        "t": 10500,
+        "d": 4
+      },
+      {
+        "t": 10800,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 12000,
+        "d": 1
+      },
+      {
+        "t": 12600,
+        "d": 3
+      },
+      {
+        "t": 12900,
+        "d": 0
+      },
+      {
+        "t": 13200,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 14400,
+        "d": 7
+      },
+      {
+        "t": 14700,
+        "d": 5
+      },
+      {
+        "t": 15300,
+        "d": 4
+      },
+      {
+        "t": 15600,
+        "d": 6,
+        "l": 600
+      },
+      {
+        "t": 16800,
+        "d": 3
+      },
+      {
+        "t": 17100,
+        "d": 1
+      },
+      {
+        "t": 17700,
+        "d": 0
+      },
+      {
+        "t": 18000,
+        "d": 2,
+        "l": 600
+      },
+      {
+        "t": 19200,
+        "d": 4
+      },
+      {
+        "t": 19500,
+        "d": 7
+      },
+      {
+        "t": 19800,
+        "d": 5,
+        "l": 900
+      },
+      {
+        "t": 21600,
+        "d": 0
+      },
+      {
+        "t": 21900,
+        "d": 3
+      },
+      {
+        "t": 22200,
+        "d": 1,
+        "l": 900
+      },
+      {
+        "t": 24000,
+        "d": 5
+      },
+      {
+        "t": 24300,
+        "d": 7
+      },
+      {
+        "t": 24600,
+        "d": 4,
+        "l": 900
+      },
+      {
+        "t": 26400,
+        "d": 1
+      },
+      {
+        "t": 26700,
+        "d": 3
+      },
+      {
+        "t": 27000,
+        "d": 0,
+        "l": 900
+      },
+      {
+        "t": 28800,
+        "d": 6
+      },
+      {
+        "t": 29100,
+        "d": 7
+      },
+      {
+        "t": 29400,
+        "d": 4,
+        "l": 1200
+      },
+      {
+        "t": 31200,
+        "d": 2
+      },
+      {
+        "t": 31500,
+        "d": 3
+      },
+      {
+        "t": 31800,
+        "d": 0,
+        "l": 1200
+      },
+      {
+        "t": 33600,
+        "d": 4
+      },
+      {
+        "t": 33900,
+        "d": 7
+      },
+      {
+        "t": 34500,
+        "d": 6
+      },
+      {
+        "t": 34800,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 36000,
+        "d": 0
+      },
+      {
+        "t": 36300,
+        "d": 3
+      },
+      {
+        "t": 36900,
+        "d": 2
+      },
+      {
+        "t": 37200,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 38400,
+        "d": 6,
+        "l": 450
+      },
+      {
+        "t": 39000,
+        "d": 7,
+        "l": 300
+      },
+      {
+        "t": 39600,
+        "d": 4,
+        "l": 600
+      },
+      {
+        "t": 40800,
+        "d": 2,
+        "l": 450
+      },
+      {
+        "t": 41400,
+        "d": 3,
+        "l": 300
+      },
+      {
+        "t": 42000,
+        "d": 0,
+        "l": 600
+      },
+      {
+        "t": 43200,
+        "d": 5
+      },
+      {
+        "t": 43800,
+        "d": 6
+      },
+      {
+        "t": 44400,
+        "d": 5
+      },
+      {
+        "t": 44700,
+        "d": 5
+      },
+      {
+        "t": 45000,
+        "d": 6
+      },
+      {
+        "t": 45600,
+        "d": 1
+      },
+      {
+        "t": 46200,
+        "d": 2
+      },
+      {
+        "t": 46800,
+        "d": 1
+      },
+      {
+        "t": 47100,
+        "d": 1
+      },
+      {
+        "t": 47400,
+        "d": 2
+      },
+      {
+        "t": 48000,
+        "d": 6,
+        "l": 450
+      },
+      {
+        "t": 48600,
+        "d": 7,
+        "l": 300
+      },
+      {
+        "t": 49200,
+        "d": 4,
+        "l": 450
+      },
+      {
+        "t": 50400,
+        "d": 2,
+        "l": 450
+      },
+      {
+        "t": 51000,
+        "d": 3,
+        "l": 300
+      },
+      {
+        "t": 51600,
+        "d": 0,
+        "l": 450
+      },
+      {
+        "t": 52800,
+        "d": 7,
+        "l": 1800
+      },
+      {
+        "t": 55200,
+        "d": 3,
+        "l": 1800
+      },
+      {
+        "t": 57600,
+        "d": 6
+      },
+      {
+        "t": 57900,
+        "d": 7
+      },
+      {
+        "t": 58200,
+        "d": 4,
+        "l": 1200
+      },
+      {
+        "t": 60000,
+        "d": 2
+      },
+      {
+        "t": 60300,
+        "d": 3
+      },
+      {
+        "t": 60600,
+        "d": 0,
+        "l": 1200
+      },
+      {
+        "t": 62400,
+        "d": 4
+      },
+      {
+        "t": 62700,
+        "d": 7
+      },
+      {
+        "t": 63300,
+        "d": 6
+      },
+      {
+        "t": 63600,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 64800,
+        "d": 0
+      },
+      {
+        "t": 65100,
+        "d": 3
+      },
+      {
+        "t": 65700,
+        "d": 2
+      },
+      {
+        "t": 66000,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 67200,
+        "d": 6
+      },
+      {
+        "t": 67500,
+        "d": 7
+      },
+      {
+        "t": 67800,
+        "d": 4
+      },
+      {
+        "t": 68100,
+        "d": 6
+      },
+      {
+        "t": 68400,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 69600,
+        "d": 2
+      },
+      {
+        "t": 69900,
+        "d": 3
+      },
+      {
+        "t": 70200,
+        "d": 0
+      },
+      {
+        "t": 70500,
+        "d": 2
+      },
+      {
+        "t": 70800,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 72000,
+        "d": 4
+      },
+      {
+        "t": 72300,
+        "d": 7
+      },
+      {
+        "t": 72900,
+        "d": 6
+      },
+      {
+        "t": 73200,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 74400,
+        "d": 0
+      },
+      {
+        "t": 74700,
+        "d": 3
+      },
+      {
+        "t": 75300,
+        "d": 2
+      },
+      {
+        "t": 75600,
+        "d": 1,
+        "l": 600
+      }
+    ],
+    "normal": [
+      {
+        "t": 0,
+        "d": 6
+      },
+      {
+        "t": 600,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 1200,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 2400,
+        "d": 2
+      },
+      {
+        "t": 3000,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 3600,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 4800,
+        "d": 5,
+        "l": 300
+      },
+      {
+        "t": 5400,
+        "d": 4,
+        "l": 300
+      },
+      {
+        "t": 6000,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 7200,
+        "d": 1,
+        "l": 300
+      },
+      {
+        "t": 7800,
+        "d": 0,
+        "l": 300
+      },
+      {
+        "t": 8400,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 9600,
+        "d": 5,
+        "l": 300
+      },
+      {
+        "t": 10200,
+        "d": 7
+      },
+      {
+        "t": 10500,
+        "d": 4
+      },
+      {
+        "t": 10800,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 12000,
+        "d": 1,
+        "l": 300
+      },
+      {
+        "t": 12600,
+        "d": 3
+      },
+      {
+        "t": 12900,
+        "d": 0
+      },
+      {
+        "t": 13200,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 14400,
+        "d": 7
+      },
+      {
+        "t": 14700,
+        "d": 5
+      },
+      {
+        "t": 15300,
+        "d": 4
+      },
+      {
+        "t": 15600,
+        "d": 6,
+        "l": 600
+      },
+      {
+        "t": 16800,
+        "d": 3
+      },
+      {
+        "t": 17100,
+        "d": 1
+      },
+      {
+        "t": 17700,
+        "d": 0
+      },
+      {
+        "t": 18000,
+        "d": 2,
+        "l": 600
+      },
+      {
+        "t": 19200,
+        "d": 4
+      },
+      {
+        "t": 19500,
+        "d": 7
+      },
+      {
+        "t": 19800,
+        "d": 5,
+        "l": 900
+      },
+      {
+        "t": 21600,
+        "d": 0
+      },
+      {
+        "t": 21900,
+        "d": 3
+      },
+      {
+        "t": 22200,
+        "d": 1,
+        "l": 900
+      },
+      {
+        "t": 24000,
+        "d": 5
+      },
+      {
+        "t": 24300,
+        "d": 7
+      },
+      {
+        "t": 24600,
+        "d": 4,
+        "l": 900
+      },
+      {
+        "t": 26400,
+        "d": 1
+      },
+      {
+        "t": 26700,
+        "d": 3
+      },
+      {
+        "t": 27000,
+        "d": 0,
+        "l": 900
+      },
+      {
+        "t": 28800,
+        "d": 6
+      },
+      {
+        "t": 29100,
+        "d": 7
+      },
+      {
+        "t": 29400,
+        "d": 4,
+        "l": 1200
+      },
+      {
+        "t": 31200,
+        "d": 2
+      },
+      {
+        "t": 31500,
+        "d": 3
+      },
+      {
+        "t": 31800,
+        "d": 0,
+        "l": 1200
+      },
+      {
+        "t": 33300,
+        "d": 6
+      },
+      {
+        "t": 33600,
+        "d": 4
+      },
+      {
+        "t": 33900,
+        "d": 7
+      },
+      {
+        "t": 34500,
+        "d": 6
+      },
+      {
+        "t": 34800,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 35700,
+        "d": 2
+      },
+      {
+        "t": 36000,
+        "d": 0
+      },
+      {
+        "t": 36300,
+        "d": 3
+      },
+      {
+        "t": 36900,
+        "d": 2
+      },
+      {
+        "t": 37200,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 38400,
+        "d": 6,
+        "l": 450
+      },
+      {
+        "t": 39000,
+        "d": 7,
+        "l": 300
+      },
+      {
+        "t": 39600,
+        "d": 4,
+        "l": 600
+      },
+      {
+        "t": 40800,
+        "d": 2,
+        "l": 450
+      },
+      {
+        "t": 41400,
+        "d": 3,
+        "l": 300
+      },
+      {
+        "t": 42000,
+        "d": 0,
+        "l": 600
+      },
+      {
+        "t": 43200,
+        "d": 5
+      },
+      {
+        "t": 43800,
+        "d": 6
+      },
+      {
+        "t": 44400,
+        "d": 5
+      },
+      {
+        "t": 44550,
+        "d": 5
+      },
+      {
+        "t": 44700,
+        "d": 5
+      },
+      {
+        "t": 45000,
+        "d": 6
+      },
+      {
+        "t": 45600,
+        "d": 1
+      },
+      {
+        "t": 46200,
+        "d": 2
+      },
+      {
+        "t": 46800,
+        "d": 1
+      },
+      {
+        "t": 46950,
+        "d": 1
+      },
+      {
+        "t": 47100,
+        "d": 1
+      },
+      {
+        "t": 47400,
+        "d": 2
+      },
+      {
+        "t": 48000,
+        "d": 6,
+        "l": 450
+      },
+      {
+        "t": 48600,
+        "d": 7,
+        "l": 300
+      },
+      {
+        "t": 49200,
+        "d": 4,
+        "l": 450
+      },
+      {
+        "t": 50400,
+        "d": 2,
+        "l": 450
+      },
+      {
+        "t": 51000,
+        "d": 3,
+        "l": 300
+      },
+      {
+        "t": 51600,
+        "d": 0,
+        "l": 450
+      },
+      {
+        "t": 52800,
+        "d": 7,
+        "l": 1800
+      },
+      {
+        "t": 55200,
+        "d": 3,
+        "l": 1800
+      },
+      {
+        "t": 57600,
+        "d": 6
+      },
+      {
+        "t": 57900,
+        "d": 7
+      },
+      {
+        "t": 58200,
+        "d": 4,
+        "l": 1200
+      },
+      {
+        "t": 60000,
+        "d": 2
+      },
+      {
+        "t": 60300,
+        "d": 3
+      },
+      {
+        "t": 60600,
+        "d": 0,
+        "l": 1200
+      },
+      {
+        "t": 62100,
+        "d": 6
+      },
+      {
+        "t": 62400,
+        "d": 4
+      },
+      {
+        "t": 62700,
+        "d": 7
+      },
+      {
+        "t": 63300,
+        "d": 6
+      },
+      {
+        "t": 63600,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 64500,
+        "d": 2
+      },
+      {
+        "t": 64800,
+        "d": 0
+      },
+      {
+        "t": 65100,
+        "d": 3
+      },
+      {
+        "t": 65700,
+        "d": 2
+      },
+      {
+        "t": 66000,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 67200,
+        "d": 6
+      },
+      {
+        "t": 67500,
+        "d": 7
+      },
+      {
+        "t": 67800,
+        "d": 4
+      },
+      {
+        "t": 68100,
+        "d": 6
+      },
+      {
+        "t": 68400,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 69600,
+        "d": 2
+      },
+      {
+        "t": 69900,
+        "d": 3
+      },
+      {
+        "t": 70200,
+        "d": 0
+      },
+      {
+        "t": 70500,
+        "d": 2
+      },
+      {
+        "t": 70800,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 71700,
+        "d": 6
+      },
+      {
+        "t": 72000,
+        "d": 4
+      },
+      {
+        "t": 72300,
+        "d": 7
+      },
+      {
+        "t": 72900,
+        "d": 6
+      },
+      {
+        "t": 73200,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 74100,
+        "d": 2
+      },
+      {
+        "t": 74400,
+        "d": 0
+      },
+      {
+        "t": 74700,
+        "d": 3
+      },
+      {
+        "t": 75300,
+        "d": 2
+      },
+      {
+        "t": 75600,
+        "d": 1,
+        "l": 600
+      }
+    ],
+    "hard": [
+      {
+        "t": 0,
+        "d": 6
+      },
+      {
+        "t": 600,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 1200,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 2400,
+        "d": 2
+      },
+      {
+        "t": 3000,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 3600,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 4800,
+        "d": 5,
+        "l": 300
+      },
+      {
+        "t": 5400,
+        "d": 4,
+        "l": 300
+      },
+      {
+        "t": 6000,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 7200,
+        "d": 1,
+        "l": 300
+      },
+      {
+        "t": 7800,
+        "d": 0,
+        "l": 300
+      },
+      {
+        "t": 8400,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 9600,
+        "d": 5,
+        "l": 300
+      },
+      {
+        "t": 10200,
+        "d": 7
+      },
+      {
+        "t": 10500,
+        "d": 4
+      },
+      {
+        "t": 10800,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 12000,
+        "d": 1,
+        "l": 300
+      },
+      {
+        "t": 12600,
+        "d": 3
+      },
+      {
+        "t": 12900,
+        "d": 0
+      },
+      {
+        "t": 13200,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 14400,
+        "d": 7
+      },
+      {
+        "t": 14700,
+        "d": 5
+      },
+      {
+        "t": 15300,
+        "d": 4
+      },
+      {
+        "t": 15600,
+        "d": 6,
+        "l": 600
+      },
+      {
+        "t": 16800,
+        "d": 3
+      },
+      {
+        "t": 17100,
+        "d": 1
+      },
+      {
+        "t": 17700,
+        "d": 0
+      },
+      {
+        "t": 18000,
+        "d": 2,
+        "l": 600
+      },
+      {
+        "t": 19200,
+        "d": 4
+      },
+      {
+        "t": 19500,
+        "d": 7
+      },
+      {
+        "t": 19800,
+        "d": 5,
+        "l": 900
+      },
+      {
+        "t": 21600,
+        "d": 0
+      },
+      {
+        "t": 21900,
+        "d": 3
+      },
+      {
+        "t": 22200,
+        "d": 1,
+        "l": 900
+      },
+      {
+        "t": 24000,
+        "d": 5
+      },
+      {
+        "t": 24300,
+        "d": 7
+      },
+      {
+        "t": 24600,
+        "d": 4,
+        "l": 900
+      },
+      {
+        "t": 26400,
+        "d": 1
+      },
+      {
+        "t": 26700,
+        "d": 3
+      },
+      {
+        "t": 27000,
+        "d": 0,
+        "l": 900
+      },
+      {
+        "t": 28800,
+        "d": 6
+      },
+      {
+        "t": 29100,
+        "d": 7
+      },
+      {
+        "t": 29400,
+        "d": 4,
+        "l": 1200
+      },
+      {
+        "t": 31200,
+        "d": 2
+      },
+      {
+        "t": 31500,
+        "d": 3
+      },
+      {
+        "t": 31800,
+        "d": 0,
+        "l": 1200
+      },
+      {
+        "t": 33300,
+        "d": 6
+      },
+      {
+        "t": 33600,
+        "d": 4
+      },
+      {
+        "t": 33900,
+        "d": 7
+      },
+      {
+        "t": 34500,
+        "d": 6
+      },
+      {
+        "t": 34575,
+        "d": 4
+      },
+      {
+        "t": 34800,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 35700,
+        "d": 2
+      },
+      {
+        "t": 36000,
+        "d": 0
+      },
+      {
+        "t": 36300,
+        "d": 3
+      },
+      {
+        "t": 36900,
+        "d": 2
+      },
+      {
+        "t": 36975,
+        "d": 0
+      },
+      {
+        "t": 37200,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 38400,
+        "d": 6,
+        "l": 450
+      },
+      {
+        "t": 39000,
+        "d": 7,
+        "l": 300
+      },
+      {
+        "t": 39600,
+        "d": 4,
+        "l": 600
+      },
+      {
+        "t": 40800,
+        "d": 2,
+        "l": 450
+      },
+      {
+        "t": 41400,
+        "d": 3,
+        "l": 300
+      },
+      {
+        "t": 42000,
+        "d": 0,
+        "l": 600
+      },
+      {
+        "t": 43200,
+        "d": 5
+      },
+      {
+        "t": 43800,
+        "d": 6
+      },
+      {
+        "t": 44400,
+        "d": 5
+      },
+      {
+        "t": 44550,
+        "d": 5
+      },
+      {
+        "t": 44700,
+        "d": 5
+      },
+      {
+        "t": 45000,
+        "d": 6
+      },
+      {
+        "t": 45600,
+        "d": 1
+      },
+      {
+        "t": 46200,
+        "d": 2
+      },
+      {
+        "t": 46800,
+        "d": 1
+      },
+      {
+        "t": 46950,
+        "d": 1
+      },
+      {
+        "t": 47100,
+        "d": 1
+      },
+      {
+        "t": 47400,
+        "d": 2
+      },
+      {
+        "t": 48000,
+        "d": 6,
+        "l": 450
+      },
+      {
+        "t": 48600,
+        "d": 7,
+        "l": 300
+      },
+      {
+        "t": 49200,
+        "d": 4,
+        "l": 450
+      },
+      {
+        "t": 50400,
+        "d": 2,
+        "l": 450
+      },
+      {
+        "t": 51000,
+        "d": 3,
+        "l": 300
+      },
+      {
+        "t": 51600,
+        "d": 0,
+        "l": 450
+      },
+      {
+        "t": 52800,
+        "d": 7,
+        "l": 1800
+      },
+      {
+        "t": 55200,
+        "d": 3,
+        "l": 1800
+      },
+      {
+        "t": 57600,
+        "d": 6
+      },
+      {
+        "t": 57900,
+        "d": 7
+      },
+      {
+        "t": 58200,
+        "d": 4,
+        "l": 1200
+      },
+      {
+        "t": 60000,
+        "d": 2
+      },
+      {
+        "t": 60300,
+        "d": 3
+      },
+      {
+        "t": 60600,
+        "d": 0,
+        "l": 1200
+      },
+      {
+        "t": 62100,
+        "d": 6
+      },
+      {
+        "t": 62400,
+        "d": 4
+      },
+      {
+        "t": 62700,
+        "d": 7
+      },
+      {
+        "t": 63300,
+        "d": 6
+      },
+      {
+        "t": 63375,
+        "d": 4
+      },
+      {
+        "t": 63600,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 64500,
+        "d": 2
+      },
+      {
+        "t": 64800,
+        "d": 0
+      },
+      {
+        "t": 65100,
+        "d": 3
+      },
+      {
+        "t": 65700,
+        "d": 2
+      },
+      {
+        "t": 65775,
+        "d": 0
+      },
+      {
+        "t": 66000,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 67200,
+        "d": 6
+      },
+      {
+        "t": 67500,
+        "d": 7
+      },
+      {
+        "t": 67800,
+        "d": 4
+      },
+      {
+        "t": 68100,
+        "d": 6
+      },
+      {
+        "t": 68400,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 69600,
+        "d": 2
+      },
+      {
+        "t": 69900,
+        "d": 3
+      },
+      {
+        "t": 70200,
+        "d": 0
+      },
+      {
+        "t": 70500,
+        "d": 2
+      },
+      {
+        "t": 70800,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 71700,
+        "d": 6
+      },
+      {
+        "t": 72000,
+        "d": 4
+      },
+      {
+        "t": 72300,
+        "d": 7
+      },
+      {
+        "t": 72900,
+        "d": 6
+      },
+      {
+        "t": 72975,
+        "d": 4
+      },
+      {
+        "t": 73200,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 74100,
+        "d": 2
+      },
+      {
+        "t": 74400,
+        "d": 0
+      },
+      {
+        "t": 74700,
+        "d": 3
+      },
+      {
+        "t": 75300,
+        "d": 2
+      },
+      {
+        "t": 75375,
+        "d": 0
+      },
+      {
+        "t": 75600,
+        "d": 1,
+        "l": 600
+      }
+    ]
+  },
+  "generatedBy": "MasterEric (by hand)"
+}
diff --git a/tests/unit/assets/preload/data/songs/bopeebo/bopeebo-chart.json b/tests/unit/assets/preload/data/songs/bopeebo/bopeebo-chart.json
new file mode 100644
index 000000000..53817f96a
--- /dev/null
+++ b/tests/unit/assets/preload/data/songs/bopeebo/bopeebo-chart.json
@@ -0,0 +1,1738 @@
+{
+  "version": "2.0.0",
+  "scrollSpeed": {
+    "default": 1.0,
+    "hard": 1.3
+  },
+  "events": [
+    { "t": 0, "e": "FocusCamera", "v": 1 },
+    { "t": 2400, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 4200,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 4800, "e": "FocusCamera", "v": 1 },
+    { "t": 7200, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 9000,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 9600, "e": "FocusCamera", "v": 1 },
+    { "t": 12000, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 13800,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 14400, "e": "FocusCamera", "v": 1 },
+    { "t": 16800, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 18600,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 19200, "e": "FocusCamera", "v": 1 },
+    { "t": 21600, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 23400,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 24000, "e": "FocusCamera", "v": 1 },
+    { "t": 26400, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 28200,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 28800, "e": "FocusCamera", "v": 1 },
+    { "t": 31200, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 33000,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 33600, "e": "FocusCamera", "v": 1 },
+    { "t": 36000, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 37800,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 38400, "e": "FocusCamera", "v": 1 },
+    { "t": 40800, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 42600,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 43200, "e": "FocusCamera", "v": 1 },
+    { "t": 45600, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 47400,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 48000, "e": "FocusCamera", "v": 1 },
+    { "t": 50400, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 52200,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 52800, "e": "FocusCamera", "v": 1 },
+    { "t": 55200, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 57000,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 57600, "e": "FocusCamera", "v": 1 },
+    { "t": 60000, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 61800,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 62400, "e": "FocusCamera", "v": 1 },
+    { "t": 64800, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 66600,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 67200, "e": "FocusCamera", "v": 1 },
+    { "t": 69600, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 71400,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    },
+    { "t": 72000, "e": "FocusCamera", "v": 1 },
+    { "t": 74400, "e": "FocusCamera", "v": 0 },
+    {
+      "t": 76200,
+      "e": "PlayAnimation",
+      "v": {
+        "target": "bf",
+        "anim": "hey",
+        "force": true
+      }
+    }
+  ],
+  "notes": {
+    "easy": [
+      {
+        "t": 0,
+        "d": 6
+      },
+      {
+        "t": 600,
+        "d": 7,
+        "l": 450
+      },
+      {
+        "t": 1200,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 2400,
+        "d": 2
+      },
+      {
+        "t": 3000,
+        "d": 3,
+        "l": 450
+      },
+      {
+        "t": 3600,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 4800,
+        "d": 5,
+        "l": 300
+      },
+      {
+        "t": 5400,
+        "d": 4,
+        "l": 300
+      },
+      {
+        "t": 6000,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 7200,
+        "d": 1,
+        "l": 300
+      },
+      {
+        "t": 7800,
+        "d": 0,
+        "l": 300
+      },
+      {
+        "t": 8400,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 9600,
+        "d": 5
+      },
+      {
+        "t": 10200,
+        "d": 7
+      },
+      {
+        "t": 10500,
+        "d": 4
+      },
+      {
+        "t": 10800,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 12000,
+        "d": 1
+      },
+      {
+        "t": 12600,
+        "d": 3
+      },
+      {
+        "t": 12900,
+        "d": 0
+      },
+      {
+        "t": 13200,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 14400,
+        "d": 7
+      },
+      {
+        "t": 14700,
+        "d": 5
+      },
+      {
+        "t": 15300,
+        "d": 4
+      },
+      {
+        "t": 15600,
+        "d": 6,
+        "l": 600
+      },
+      {
+        "t": 16800,
+        "d": 3
+      },
+      {
+        "t": 17100,
+        "d": 1
+      },
+      {
+        "t": 17700,
+        "d": 0
+      },
+      {
+        "t": 18000,
+        "d": 2,
+        "l": 600
+      },
+      {
+        "t": 19200,
+        "d": 4
+      },
+      {
+        "t": 19500,
+        "d": 7
+      },
+      {
+        "t": 19800,
+        "d": 5,
+        "l": 900
+      },
+      {
+        "t": 21600,
+        "d": 0
+      },
+      {
+        "t": 21900,
+        "d": 3
+      },
+      {
+        "t": 22200,
+        "d": 1,
+        "l": 900
+      },
+      {
+        "t": 24000,
+        "d": 5
+      },
+      {
+        "t": 24300,
+        "d": 7
+      },
+      {
+        "t": 24600,
+        "d": 4,
+        "l": 900
+      },
+      {
+        "t": 26400,
+        "d": 1
+      },
+      {
+        "t": 26700,
+        "d": 3
+      },
+      {
+        "t": 27000,
+        "d": 0,
+        "l": 900
+      },
+      {
+        "t": 28800,
+        "d": 6
+      },
+      {
+        "t": 29100,
+        "d": 7
+      },
+      {
+        "t": 29400,
+        "d": 4,
+        "l": 1200
+      },
+      {
+        "t": 31200,
+        "d": 2
+      },
+      {
+        "t": 31500,
+        "d": 3
+      },
+      {
+        "t": 31800,
+        "d": 0,
+        "l": 1200
+      },
+      {
+        "t": 33600,
+        "d": 4
+      },
+      {
+        "t": 33900,
+        "d": 7
+      },
+      {
+        "t": 34500,
+        "d": 6
+      },
+      {
+        "t": 34800,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 36000,
+        "d": 0
+      },
+      {
+        "t": 36300,
+        "d": 3
+      },
+      {
+        "t": 36900,
+        "d": 2
+      },
+      {
+        "t": 37200,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 38400,
+        "d": 6,
+        "l": 450
+      },
+      {
+        "t": 39000,
+        "d": 7,
+        "l": 300
+      },
+      {
+        "t": 39600,
+        "d": 4,
+        "l": 600
+      },
+      {
+        "t": 40800,
+        "d": 2,
+        "l": 450
+      },
+      {
+        "t": 41400,
+        "d": 3,
+        "l": 300
+      },
+      {
+        "t": 42000,
+        "d": 0,
+        "l": 600
+      },
+      {
+        "t": 43200,
+        "d": 5
+      },
+      {
+        "t": 43800,
+        "d": 6
+      },
+      {
+        "t": 44400,
+        "d": 5
+      },
+      {
+        "t": 44700,
+        "d": 5
+      },
+      {
+        "t": 45000,
+        "d": 6
+      },
+      {
+        "t": 45600,
+        "d": 1
+      },
+      {
+        "t": 46200,
+        "d": 2
+      },
+      {
+        "t": 46800,
+        "d": 1
+      },
+      {
+        "t": 47100,
+        "d": 1
+      },
+      {
+        "t": 47400,
+        "d": 2
+      },
+      {
+        "t": 48000,
+        "d": 6,
+        "l": 450
+      },
+      {
+        "t": 48600,
+        "d": 7,
+        "l": 300
+      },
+      {
+        "t": 49200,
+        "d": 4,
+        "l": 450
+      },
+      {
+        "t": 50400,
+        "d": 2,
+        "l": 450
+      },
+      {
+        "t": 51000,
+        "d": 3,
+        "l": 300
+      },
+      {
+        "t": 51600,
+        "d": 0,
+        "l": 450
+      },
+      {
+        "t": 52800,
+        "d": 7,
+        "l": 1800
+      },
+      {
+        "t": 55200,
+        "d": 3,
+        "l": 1800
+      },
+      {
+        "t": 57600,
+        "d": 6
+      },
+      {
+        "t": 57900,
+        "d": 7
+      },
+      {
+        "t": 58200,
+        "d": 4,
+        "l": 1200
+      },
+      {
+        "t": 60000,
+        "d": 2
+      },
+      {
+        "t": 60300,
+        "d": 3
+      },
+      {
+        "t": 60600,
+        "d": 0,
+        "l": 1200
+      },
+      {
+        "t": 62400,
+        "d": 4
+      },
+      {
+        "t": 62700,
+        "d": 7
+      },
+      {
+        "t": 63300,
+        "d": 6
+      },
+      {
+        "t": 63600,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 64800,
+        "d": 0
+      },
+      {
+        "t": 65100,
+        "d": 3
+      },
+      {
+        "t": 65700,
+        "d": 2
+      },
+      {
+        "t": 66000,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 67200,
+        "d": 6
+      },
+      {
+        "t": 67500,
+        "d": 7
+      },
+      {
+        "t": 67800,
+        "d": 4
+      },
+      {
+        "t": 68100,
+        "d": 6
+      },
+      {
+        "t": 68400,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 69600,
+        "d": 2
+      },
+      {
+        "t": 69900,
+        "d": 3
+      },
+      {
+        "t": 70200,
+        "d": 0
+      },
+      {
+        "t": 70500,
+        "d": 2
+      },
+      {
+        "t": 70800,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 72000,
+        "d": 4
+      },
+      {
+        "t": 72300,
+        "d": 7
+      },
+      {
+        "t": 72900,
+        "d": 6
+      },
+      {
+        "t": 73200,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 74400,
+        "d": 0
+      },
+      {
+        "t": 74700,
+        "d": 3
+      },
+      {
+        "t": 75300,
+        "d": 2
+      },
+      {
+        "t": 75600,
+        "d": 1,
+        "l": 600
+      }
+    ],
+    "normal": [
+      {
+        "t": 0,
+        "d": 6
+      },
+      {
+        "t": 600,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 1200,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 2400,
+        "d": 2
+      },
+      {
+        "t": 3000,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 3600,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 4800,
+        "d": 5,
+        "l": 300
+      },
+      {
+        "t": 5400,
+        "d": 4,
+        "l": 300
+      },
+      {
+        "t": 6000,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 7200,
+        "d": 1,
+        "l": 300
+      },
+      {
+        "t": 7800,
+        "d": 0,
+        "l": 300
+      },
+      {
+        "t": 8400,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 9600,
+        "d": 5,
+        "l": 300
+      },
+      {
+        "t": 10200,
+        "d": 7
+      },
+      {
+        "t": 10500,
+        "d": 4
+      },
+      {
+        "t": 10800,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 12000,
+        "d": 1,
+        "l": 300
+      },
+      {
+        "t": 12600,
+        "d": 3
+      },
+      {
+        "t": 12900,
+        "d": 0
+      },
+      {
+        "t": 13200,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 14400,
+        "d": 7
+      },
+      {
+        "t": 14700,
+        "d": 5
+      },
+      {
+        "t": 15300,
+        "d": 4
+      },
+      {
+        "t": 15600,
+        "d": 6,
+        "l": 600
+      },
+      {
+        "t": 16800,
+        "d": 3
+      },
+      {
+        "t": 17100,
+        "d": 1
+      },
+      {
+        "t": 17700,
+        "d": 0
+      },
+      {
+        "t": 18000,
+        "d": 2,
+        "l": 600
+      },
+      {
+        "t": 19200,
+        "d": 4
+      },
+      {
+        "t": 19500,
+        "d": 7
+      },
+      {
+        "t": 19800,
+        "d": 5,
+        "l": 900
+      },
+      {
+        "t": 21600,
+        "d": 0
+      },
+      {
+        "t": 21900,
+        "d": 3
+      },
+      {
+        "t": 22200,
+        "d": 1,
+        "l": 900
+      },
+      {
+        "t": 24000,
+        "d": 5
+      },
+      {
+        "t": 24300,
+        "d": 7
+      },
+      {
+        "t": 24600,
+        "d": 4,
+        "l": 900
+      },
+      {
+        "t": 26400,
+        "d": 1
+      },
+      {
+        "t": 26700,
+        "d": 3
+      },
+      {
+        "t": 27000,
+        "d": 0,
+        "l": 900
+      },
+      {
+        "t": 28800,
+        "d": 6
+      },
+      {
+        "t": 29100,
+        "d": 7
+      },
+      {
+        "t": 29400,
+        "d": 4,
+        "l": 1200
+      },
+      {
+        "t": 31200,
+        "d": 2
+      },
+      {
+        "t": 31500,
+        "d": 3
+      },
+      {
+        "t": 31800,
+        "d": 0,
+        "l": 1200
+      },
+      {
+        "t": 33300,
+        "d": 6
+      },
+      {
+        "t": 33600,
+        "d": 4
+      },
+      {
+        "t": 33900,
+        "d": 7
+      },
+      {
+        "t": 34500,
+        "d": 6
+      },
+      {
+        "t": 34800,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 35700,
+        "d": 2
+      },
+      {
+        "t": 36000,
+        "d": 0
+      },
+      {
+        "t": 36300,
+        "d": 3
+      },
+      {
+        "t": 36900,
+        "d": 2
+      },
+      {
+        "t": 37200,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 38400,
+        "d": 6,
+        "l": 450
+      },
+      {
+        "t": 39000,
+        "d": 7,
+        "l": 300
+      },
+      {
+        "t": 39600,
+        "d": 4,
+        "l": 600
+      },
+      {
+        "t": 40800,
+        "d": 2,
+        "l": 450
+      },
+      {
+        "t": 41400,
+        "d": 3,
+        "l": 300
+      },
+      {
+        "t": 42000,
+        "d": 0,
+        "l": 600
+      },
+      {
+        "t": 43200,
+        "d": 5
+      },
+      {
+        "t": 43800,
+        "d": 6
+      },
+      {
+        "t": 44400,
+        "d": 5
+      },
+      {
+        "t": 44550,
+        "d": 5
+      },
+      {
+        "t": 44700,
+        "d": 5
+      },
+      {
+        "t": 45000,
+        "d": 6
+      },
+      {
+        "t": 45600,
+        "d": 1
+      },
+      {
+        "t": 46200,
+        "d": 2
+      },
+      {
+        "t": 46800,
+        "d": 1
+      },
+      {
+        "t": 46950,
+        "d": 1
+      },
+      {
+        "t": 47100,
+        "d": 1
+      },
+      {
+        "t": 47400,
+        "d": 2
+      },
+      {
+        "t": 48000,
+        "d": 6,
+        "l": 450
+      },
+      {
+        "t": 48600,
+        "d": 7,
+        "l": 300
+      },
+      {
+        "t": 49200,
+        "d": 4,
+        "l": 450
+      },
+      {
+        "t": 50400,
+        "d": 2,
+        "l": 450
+      },
+      {
+        "t": 51000,
+        "d": 3,
+        "l": 300
+      },
+      {
+        "t": 51600,
+        "d": 0,
+        "l": 450
+      },
+      {
+        "t": 52800,
+        "d": 7,
+        "l": 1800
+      },
+      {
+        "t": 55200,
+        "d": 3,
+        "l": 1800
+      },
+      {
+        "t": 57600,
+        "d": 6
+      },
+      {
+        "t": 57900,
+        "d": 7
+      },
+      {
+        "t": 58200,
+        "d": 4,
+        "l": 1200
+      },
+      {
+        "t": 60000,
+        "d": 2
+      },
+      {
+        "t": 60300,
+        "d": 3
+      },
+      {
+        "t": 60600,
+        "d": 0,
+        "l": 1200
+      },
+      {
+        "t": 62100,
+        "d": 6
+      },
+      {
+        "t": 62400,
+        "d": 4
+      },
+      {
+        "t": 62700,
+        "d": 7
+      },
+      {
+        "t": 63300,
+        "d": 6
+      },
+      {
+        "t": 63600,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 64500,
+        "d": 2
+      },
+      {
+        "t": 64800,
+        "d": 0
+      },
+      {
+        "t": 65100,
+        "d": 3
+      },
+      {
+        "t": 65700,
+        "d": 2
+      },
+      {
+        "t": 66000,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 67200,
+        "d": 6
+      },
+      {
+        "t": 67500,
+        "d": 7
+      },
+      {
+        "t": 67800,
+        "d": 4
+      },
+      {
+        "t": 68100,
+        "d": 6
+      },
+      {
+        "t": 68400,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 69600,
+        "d": 2
+      },
+      {
+        "t": 69900,
+        "d": 3
+      },
+      {
+        "t": 70200,
+        "d": 0
+      },
+      {
+        "t": 70500,
+        "d": 2
+      },
+      {
+        "t": 70800,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 71700,
+        "d": 6
+      },
+      {
+        "t": 72000,
+        "d": 4
+      },
+      {
+        "t": 72300,
+        "d": 7
+      },
+      {
+        "t": 72900,
+        "d": 6
+      },
+      {
+        "t": 73200,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 74100,
+        "d": 2
+      },
+      {
+        "t": 74400,
+        "d": 0
+      },
+      {
+        "t": 74700,
+        "d": 3
+      },
+      {
+        "t": 75300,
+        "d": 2
+      },
+      {
+        "t": 75600,
+        "d": 1,
+        "l": 600
+      }
+    ],
+    "hard": [
+      {
+        "t": 0,
+        "d": 6
+      },
+      {
+        "t": 600,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 1200,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 2400,
+        "d": 2
+      },
+      {
+        "t": 3000,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 3600,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 4800,
+        "d": 5,
+        "l": 300
+      },
+      {
+        "t": 5400,
+        "d": 4,
+        "l": 300
+      },
+      {
+        "t": 6000,
+        "d": 7,
+        "l": 600
+      },
+      {
+        "t": 7200,
+        "d": 1,
+        "l": 300
+      },
+      {
+        "t": 7800,
+        "d": 0,
+        "l": 300
+      },
+      {
+        "t": 8400,
+        "d": 3,
+        "l": 600
+      },
+      {
+        "t": 9600,
+        "d": 5,
+        "l": 300
+      },
+      {
+        "t": 10200,
+        "d": 7
+      },
+      {
+        "t": 10500,
+        "d": 4
+      },
+      {
+        "t": 10800,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 12000,
+        "d": 1,
+        "l": 300
+      },
+      {
+        "t": 12600,
+        "d": 3
+      },
+      {
+        "t": 12900,
+        "d": 0
+      },
+      {
+        "t": 13200,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 14400,
+        "d": 7
+      },
+      {
+        "t": 14700,
+        "d": 5
+      },
+      {
+        "t": 15300,
+        "d": 4
+      },
+      {
+        "t": 15600,
+        "d": 6,
+        "l": 600
+      },
+      {
+        "t": 16800,
+        "d": 3
+      },
+      {
+        "t": 17100,
+        "d": 1
+      },
+      {
+        "t": 17700,
+        "d": 0
+      },
+      {
+        "t": 18000,
+        "d": 2,
+        "l": 600
+      },
+      {
+        "t": 19200,
+        "d": 4
+      },
+      {
+        "t": 19500,
+        "d": 7
+      },
+      {
+        "t": 19800,
+        "d": 5,
+        "l": 900
+      },
+      {
+        "t": 21600,
+        "d": 0
+      },
+      {
+        "t": 21900,
+        "d": 3
+      },
+      {
+        "t": 22200,
+        "d": 1,
+        "l": 900
+      },
+      {
+        "t": 24000,
+        "d": 5
+      },
+      {
+        "t": 24300,
+        "d": 7
+      },
+      {
+        "t": 24600,
+        "d": 4,
+        "l": 900
+      },
+      {
+        "t": 26400,
+        "d": 1
+      },
+      {
+        "t": 26700,
+        "d": 3
+      },
+      {
+        "t": 27000,
+        "d": 0,
+        "l": 900
+      },
+      {
+        "t": 28800,
+        "d": 6
+      },
+      {
+        "t": 29100,
+        "d": 7
+      },
+      {
+        "t": 29400,
+        "d": 4,
+        "l": 1200
+      },
+      {
+        "t": 31200,
+        "d": 2
+      },
+      {
+        "t": 31500,
+        "d": 3
+      },
+      {
+        "t": 31800,
+        "d": 0,
+        "l": 1200
+      },
+      {
+        "t": 33300,
+        "d": 6
+      },
+      {
+        "t": 33600,
+        "d": 4
+      },
+      {
+        "t": 33900,
+        "d": 7
+      },
+      {
+        "t": 34500,
+        "d": 6
+      },
+      {
+        "t": 34575,
+        "d": 4
+      },
+      {
+        "t": 34800,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 35700,
+        "d": 2
+      },
+      {
+        "t": 36000,
+        "d": 0
+      },
+      {
+        "t": 36300,
+        "d": 3
+      },
+      {
+        "t": 36900,
+        "d": 2
+      },
+      {
+        "t": 36975,
+        "d": 0
+      },
+      {
+        "t": 37200,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 38400,
+        "d": 6,
+        "l": 450
+      },
+      {
+        "t": 39000,
+        "d": 7,
+        "l": 300
+      },
+      {
+        "t": 39600,
+        "d": 4,
+        "l": 600
+      },
+      {
+        "t": 40800,
+        "d": 2,
+        "l": 450
+      },
+      {
+        "t": 41400,
+        "d": 3,
+        "l": 300
+      },
+      {
+        "t": 42000,
+        "d": 0,
+        "l": 600
+      },
+      {
+        "t": 43200,
+        "d": 5
+      },
+      {
+        "t": 43800,
+        "d": 6
+      },
+      {
+        "t": 44400,
+        "d": 5
+      },
+      {
+        "t": 44550,
+        "d": 5
+      },
+      {
+        "t": 44700,
+        "d": 5
+      },
+      {
+        "t": 45000,
+        "d": 6
+      },
+      {
+        "t": 45600,
+        "d": 1
+      },
+      {
+        "t": 46200,
+        "d": 2
+      },
+      {
+        "t": 46800,
+        "d": 1
+      },
+      {
+        "t": 46950,
+        "d": 1
+      },
+      {
+        "t": 47100,
+        "d": 1
+      },
+      {
+        "t": 47400,
+        "d": 2
+      },
+      {
+        "t": 48000,
+        "d": 6,
+        "l": 450
+      },
+      {
+        "t": 48600,
+        "d": 7,
+        "l": 300
+      },
+      {
+        "t": 49200,
+        "d": 4,
+        "l": 450
+      },
+      {
+        "t": 50400,
+        "d": 2,
+        "l": 450
+      },
+      {
+        "t": 51000,
+        "d": 3,
+        "l": 300
+      },
+      {
+        "t": 51600,
+        "d": 0,
+        "l": 450
+      },
+      {
+        "t": 52800,
+        "d": 7,
+        "l": 1800
+      },
+      {
+        "t": 55200,
+        "d": 3,
+        "l": 1800
+      },
+      {
+        "t": 57600,
+        "d": 6
+      },
+      {
+        "t": 57900,
+        "d": 7
+      },
+      {
+        "t": 58200,
+        "d": 4,
+        "l": 1200
+      },
+      {
+        "t": 60000,
+        "d": 2
+      },
+      {
+        "t": 60300,
+        "d": 3
+      },
+      {
+        "t": 60600,
+        "d": 0,
+        "l": 1200
+      },
+      {
+        "t": 62100,
+        "d": 6
+      },
+      {
+        "t": 62400,
+        "d": 4
+      },
+      {
+        "t": 62700,
+        "d": 7
+      },
+      {
+        "t": 63300,
+        "d": 6
+      },
+      {
+        "t": 63375,
+        "d": 4
+      },
+      {
+        "t": 63600,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 64500,
+        "d": 2
+      },
+      {
+        "t": 64800,
+        "d": 0
+      },
+      {
+        "t": 65100,
+        "d": 3
+      },
+      {
+        "t": 65700,
+        "d": 2
+      },
+      {
+        "t": 65775,
+        "d": 0
+      },
+      {
+        "t": 66000,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 67200,
+        "d": 6
+      },
+      {
+        "t": 67500,
+        "d": 7
+      },
+      {
+        "t": 67800,
+        "d": 4
+      },
+      {
+        "t": 68100,
+        "d": 6
+      },
+      {
+        "t": 68400,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 69600,
+        "d": 2
+      },
+      {
+        "t": 69900,
+        "d": 3
+      },
+      {
+        "t": 70200,
+        "d": 0
+      },
+      {
+        "t": 70500,
+        "d": 2
+      },
+      {
+        "t": 70800,
+        "d": 1,
+        "l": 600
+      },
+      {
+        "t": 71700,
+        "d": 6
+      },
+      {
+        "t": 72000,
+        "d": 4
+      },
+      {
+        "t": 72300,
+        "d": 7
+      },
+      {
+        "t": 72900,
+        "d": 6
+      },
+      {
+        "t": 72975,
+        "d": 4
+      },
+      {
+        "t": 73200,
+        "d": 5,
+        "l": 600
+      },
+      {
+        "t": 74100,
+        "d": 2
+      },
+      {
+        "t": 74400,
+        "d": 0
+      },
+      {
+        "t": 74700,
+        "d": 3
+      },
+      {
+        "t": 75300,
+        "d": 2
+      },
+      {
+        "t": 75375,
+        "d": 0
+      },
+      {
+        "t": 75600,
+        "d": 1,
+        "l": 600
+      }
+    ]
+  },
+  "generatedBy": "MasterEric (by hand)"
+}
diff --git a/tests/unit/assets/preload/data/songs/bopeebo/bopeebo-metadata.json b/tests/unit/assets/preload/data/songs/bopeebo/bopeebo-metadata.json
new file mode 100644
index 000000000..c7f7629d6
--- /dev/null
+++ b/tests/unit/assets/preload/data/songs/bopeebo/bopeebo-metadata.json
@@ -0,0 +1,15 @@
+{
+  "version": "2.0.0",
+  "songName": "Bopeebo",
+  "artist": "Kawai Sprite",
+  "timeFormat": "ms",
+  "timeChanges": [{ "t": 0, "bpm": 100, "n": 4, "d": 4, "bt": [4, 4, 4, 4] }],
+  "playData": {
+    "songVariations": [],
+    "difficulties": ["easy", "normal", "hard"],
+    "playableChars": { "bf": { "g": "gf", "o": "dad" } },
+    "stage": "mainStage",
+    "noteSkin": "Normal"
+  },
+  "generatedBy": "MasterEric (by hand)"
+}
diff --git a/tests/unit/project.xml b/tests/unit/project.xml
index 63f164607..2e505e015 100644
--- a/tests/unit/project.xml
+++ b/tests/unit/project.xml
@@ -44,11 +44,12 @@
 	<!-- Assets -->
 	<assets path="assets/preload" rename="assets" exclude="*.ogg" if="web" />
 	<assets path="assets/preload" rename="assets" exclude="*.mp3" unless="web" />
+	<assets path="assets/shared" library="shared" exclude="*.fla|*.ogg" if="web" />
+	<assets path="assets/shared" library="shared" exclude="*.fla|*.mp3" unless="web" />
+	<library name="shared" preload="true" />
 	<!--
 	<assets path="assets/songs" library="songs" exclude="*.fla|*.ogg" if="web" />
 	<assets path="assets/songs" library="songs" exclude="*.fla|*.mp3" unless="web" />
-	<assets path="assets/shared" library="shared" exclude="*.fla|*.ogg" if="web" />
-	<assets path="assets/shared" library="shared" exclude="*.fla|*.mp3" unless="web" />
 	<assets path="assets/tutorial" library="tutorial" exclude="*.fla|*.ogg" if="web" />
 	<assets path="assets/tutorial" library="tutorial" exclude="*.fla|*.mp3" unless="web" />
 	<assets path="assets/week1" library="week1" exclude="*.fla|*.ogg" if="web" />
@@ -68,7 +69,6 @@
 	<assets path="assets/weekend1" library="weekend1" exclude="*.fla|*.ogg" if="web" />
 	<assets path="assets/weekend1" library="weekend1" exclude="*.fla|*.mp3" unless="web" />
 	<library name="songs" preload="true" />
-	<library name="shared" preload="true" />
 	<library name="tutorial" preload="true" />
 	<library name="week1" preload="true" />
 	<library name="week2" preload="true" />
@@ -87,8 +87,8 @@
 	<haxedef name="FLX_RECORD" />
 
 	<!-- Clean up the output -->
-	<haxedef name="no-traces" />
 	<!--
+		<haxedef name="echo-traces" />
 	-->
 	<haxedef name="ignore-inline" />
 	<haxeflag name="-w" value="-WDeprecated" />
diff --git a/tests/unit/source/FunkinAssert.hx b/tests/unit/source/FunkinAssert.hx
index 00c3a9e00..6a0b0a9a4 100644
--- a/tests/unit/source/FunkinAssert.hx
+++ b/tests/unit/source/FunkinAssert.hx
@@ -127,4 +127,54 @@ class FunkinAssert
     };
     validateThrows(targetFunc, predicate, info);
   }
+
+  static var capturedTraces:Array<String> = [];
+
+  public static function initAssertTrace():Void
+  {
+    var oldTrace = haxe.Log.trace;
+    haxe.Log.trace = function(v:Dynamic, ?infos:haxe.PosInfos) {
+      onTrace(v, infos);
+      // oldTrace(v, infos);
+    };
+  }
+
+  public static function clearTraces():Void
+  {
+    capturedTraces = [];
+  }
+
+  @:nullSafety(Off) // Why isn't haxe.std null-safe?
+  static function onTrace(v:Dynamic, ?infos:haxe.PosInfos)
+  {
+    // var str:String = haxe.Log.formatOutput(v, infos);
+    var str:String = Std.string(v);
+    capturedTraces.push(str);
+
+    #if (sys && echo_traces)
+    Sys.println('[TESTLOG] $str');
+    #end
+  }
+
+  /**
+   * Check the first string that was traced and validate it.
+   * @param expected
+   */
+  public static inline function assertTrace(expected:String):Void
+  {
+    var actual:Null<String> = capturedTraces.shift();
+    Assert.isNotNull(actual);
+    Assert.areEqual(expected, actual);
+  }
+
+  /**
+   * Check the first string that was traced and validate it.
+   * @param expected
+   */
+  public static inline function assertLastTrace(expected:String):Void
+  {
+    var actual:Null<String> = capturedTraces.pop();
+    Assert.isNotNull(actual);
+    Assert.areEqual(expected, actual);
+  }
 }
diff --git a/tests/unit/source/MockTest.hx b/tests/unit/source/MockTest.hx
index 308dbb45a..e9345dc2d 100644
--- a/tests/unit/source/MockTest.hx
+++ b/tests/unit/source/MockTest.hx
@@ -30,8 +30,8 @@ class MockTest extends FunkinTest
   {
     // Test that mocking works.
 
-    var mockSprite = mockatoo.Mockatoo.mock(flixel.FlxSprite);
-    var mockAnim = mockatoo.Mockatoo.mock(flixel.animation.FlxAnimationController);
+    var mockSprite = Mockatoo.mock(flixel.FlxSprite);
+    var mockAnim = Mockatoo.mock(flixel.animation.FlxAnimationController);
     mockSprite.animation = mockAnim;
 
     var animData:funkin.data.animation.AnimationData =
@@ -44,12 +44,12 @@ class MockTest extends FunkinTest
 
     // Verify that the method was called once.
     // If not, a VerificationException will be thrown and the test will fail.
-    mockatoo.Mockatoo.verify(mockAnim.addByPrefix("testAnim", "blablabla", 24, false, false, false), times(1));
+    mockAnim.addByPrefix("testAnim", "blablabla", 24, false, false, false).verify(times(1));
 
     FunkinAssert.validateThrows(function() {
       // Attempt to verify the method was called.
       // This should FAIL, since we didn't call the method.
-      mockatoo.Mockatoo.verify(mockAnim.addByPrefix("testAnim", "blablabla", 24, false, false, false), times(1));
+      mockAnim.addByPrefix("testAnim", "blablabla", 24, false, false, false).verify(times(1));
     }, function(err) {
       return Std.isOfType(err, mockatoo.exception.VerificationException);
     });
diff --git a/tests/unit/source/funkin/ConductorTest.hx b/tests/unit/source/funkin/ConductorTest.hx
index 0cfbe3960..c65f3f297 100644
--- a/tests/unit/source/funkin/ConductorTest.hx
+++ b/tests/unit/source/funkin/ConductorTest.hx
@@ -3,7 +3,7 @@ package funkin;
 import flixel.FlxG;
 import flixel.FlxState;
 import funkin.Conductor;
-import funkin.play.song.SongData.SongTimeChange;
+import funkin.data.song.SongData.SongTimeChange;
 import funkin.util.Constants;
 import massive.munit.Assert;
 
@@ -16,6 +16,8 @@ class ConductorTest extends FunkinTest
   @Before
   function before()
   {
+    FunkinAssert.initAssertTrace();
+
     resetGame();
 
     // The ConductorState will advance the conductor when step() is called.
@@ -193,16 +195,7 @@ class ConductorTest extends FunkinTest
   function testSingleTimeChange():Void
   {
     // Start the song with a BPM of 120.
-    var songTimeChanges:Array<SongTimeChange> = [
-      {
-        t: 0,
-        b: 0,
-        bpm: 120,
-        n: 4,
-        d: 4,
-        bt: [4, 4, 4, 4]
-      }, // 120 bpm starting 0 sec/0 beats
-    ];
+    var songTimeChanges:Array<SongTimeChange> = [new SongTimeChange(0, 120)];
     Conductor.mapTimeChanges(songTimeChanges);
 
     // All should be at 0.
@@ -253,24 +246,7 @@ class ConductorTest extends FunkinTest
   function testDoubleTimeChange():Void
   {
     // Start the song with a BPM of 120.
-    var songTimeChanges:Array<SongTimeChange> = [
-      {
-        t: 0,
-        b: 0,
-        bpm: 120,
-        n: 4,
-        d: 4,
-        bt: [4, 4, 4, 4]
-      }, // 120 bpm starting 0 sec/0 beats
-      {
-        t: 3000,
-        b: 6,
-        bpm: 90,
-        n: 4,
-        d: 4,
-        bt: [4, 4, 4, 4]
-      } // 90 bpm starting 3 sec/6 beats
-    ];
+    var songTimeChanges:Array<SongTimeChange> = [new SongTimeChange(0, 120), new SongTimeChange(3000, 90)];
     Conductor.mapTimeChanges(songTimeChanges);
 
     // All should be at 0.
@@ -354,30 +330,9 @@ class ConductorTest extends FunkinTest
   {
     // Start the song with a BPM of 120, then move to 90, then move to 180.
     var songTimeChanges:Array<SongTimeChange> = [
-      {
-        t: 0,
-        b: null,
-        bpm: 120,
-        n: 4,
-        d: 4,
-        bt: [4, 4, 4, 4]
-      }, // 120 bpm starting 0 sec/0 beats
-      {
-        t: 3000,
-        b: null,
-        bpm: 90,
-        n: 4,
-        d: 4,
-        bt: [4, 4, 4, 4]
-      }, // 90 bpm starting 3 sec/6 beats
-      {
-        t: 6000,
-        b: null,
-        bpm: 180,
-        n: 4,
-        d: 4,
-        bt: [4, 4, 4, 4]
-      } // 90 bpm starting 3 sec/6 beats
+      new SongTimeChange(0, 120),
+      new SongTimeChange(3000, 90),
+      new SongTimeChange(6000, 180)
     ];
     Conductor.mapTimeChanges(songTimeChanges);
 
diff --git a/tests/unit/source/funkin/data/BaseRegistryTest.hx b/tests/unit/source/funkin/data/BaseRegistryTest.hx
index 31e44b6ed..0be932d35 100644
--- a/tests/unit/source/funkin/data/BaseRegistryTest.hx
+++ b/tests/unit/source/funkin/data/BaseRegistryTest.hx
@@ -17,7 +17,10 @@ class BaseRegistryTest extends FunkinTest
   }
 
   @BeforeClass
-  public function beforeClass() {}
+  public function beforeClass()
+  {
+    FunkinAssert.initAssertTrace();
+  }
 
   @AfterClass
   public function afterClass() {}
@@ -118,7 +121,7 @@ class MyType implements IRegistryEntry<MyTypeData>
     return 'MyType($id)';
   }
 
-  public function _fetchData(id:String):Null<MyTypeData>
+  static function _fetchData(id:String):Null<MyTypeData>
   {
     return MyTypeRegistry.instance.parseEntryDataWithMigration(id, MyTypeRegistry.instance.fetchEntryVersion(id));
   }
@@ -153,17 +156,18 @@ class MyTypeRegistry extends BaseRegistry<MyType, MyTypeData>
     // JsonParser does not take type parameters,
     // otherwise this function would be in BaseRegistry.
     var parser = new json2object.JsonParser<MyTypeData>();
-    var jsonStr:String = loadEntryFile(id);
 
-    parser.fromJson(jsonStr);
+    switch (loadEntryFile(id))
+    {
+      case {fileName: fileName, contents: contents}:
+        parser.fromJson(contents, fileName);
+      default:
+        return null;
+    }
 
     if (parser.errors.length > 0)
     {
-      trace('[${registryId}] Failed to parse entry data: ${id}');
-      for (error in parser.errors)
-      {
-        trace(error);
-      }
+      printErrors(parser.errors, id);
       return null;
     }
     return parser.value;
@@ -177,31 +181,33 @@ class MyTypeRegistry extends BaseRegistry<MyType, MyTypeData>
     // JsonParser does not take type parameters,
     // otherwise this function would be in BaseRegistry.
     var parser = new json2object.JsonParser<MyTypeData_v0_1_x>();
-    var jsonStr:String = loadEntryFile(id);
 
-    parser.fromJson(jsonStr);
+    switch (loadEntryFile(id))
+    {
+      case {fileName: fileName, contents: contents}:
+        parser.fromJson(contents, fileName);
+      default:
+        return null;
+    }
 
     if (parser.errors.length > 0)
     {
-      trace('[${registryId}] Failed to parse entry data: ${id}');
-      for (error in parser.errors)
-      {
-        trace(error);
-      }
+      printErrors(parser.errors, id);
       return null;
     }
 
     var oldData:MyTypeData_v0_1_x = parser.value;
+    return migrateData_v0_1_x(oldData);
+  }
 
-    var result:MyTypeData =
-      {
-        version: DATA_VERSION,
-        id: '${oldData.id}',
-        name: oldData.name,
-        data: []
-      };
-
-    return result;
+  function migrateData_v0_1_x(input:MyTypeData_v0_1_x):MyTypeData
+  {
+    return {
+      version: DATA_VERSION,
+      id: '${input.id}',
+      name: input.name,
+      data: []
+    };
   }
 
   public override function parseEntryDataWithMigration(id:String, version:thx.semver.Version):Null<MyTypeData>
diff --git a/tests/unit/source/funkin/data/level/LevelRegistryTest.hx b/tests/unit/source/funkin/data/level/LevelRegistryTest.hx
index 3d9cf5d29..691d8bedc 100644
--- a/tests/unit/source/funkin/data/level/LevelRegistryTest.hx
+++ b/tests/unit/source/funkin/data/level/LevelRegistryTest.hx
@@ -123,9 +123,12 @@ class LevelRegistryTest extends FunkinTest
   }
 
   @Test
-  @Ignore("Requires redoing validation.")
   public function testCreateEntryBlankPath():Void
   {
+    // Using @:jcustomparse, `titleAsset` has a validation function that ensures it is not blank.
+    // This test makes sure that the validation function is being called, and that the error
+    // results in the level failing to parse.
+
     FunkinAssert.validateThrows(function() {
       var result:Null<Level> = LevelRegistry.instance.createEntry("blankpathtest");
     }, function(err) {
@@ -134,7 +137,6 @@ class LevelRegistryTest extends FunkinTest
   }
 
   @Test
-  @Ignore("Requires redoing validation.")
   public function testFetchBadEntry():Void
   {
     var result:Null<Level> = LevelRegistry.instance.fetchEntry("blablabla");
diff --git a/tests/unit/source/funkin/data/notestyle/NoteStyleRegistryTest.hx b/tests/unit/source/funkin/data/notestyle/NoteStyleRegistryTest.hx
index 8ae9cb31f..447ee7831 100644
--- a/tests/unit/source/funkin/data/notestyle/NoteStyleRegistryTest.hx
+++ b/tests/unit/source/funkin/data/notestyle/NoteStyleRegistryTest.hx
@@ -86,7 +86,7 @@ class NoteStyleRegistryTest extends FunkinTest
   @Test
   public function testFetchDefault():Void
   {
-    var nsrMock:NoteStyleRegistry = mock(NoteStyleRegistry);
+    var nsrMock = Mockatoo.mock(NoteStyleRegistry);
 
     nsrMock.fetchDefault().callsRealMethod();
 
diff --git a/tests/unit/source/funkin/data/song/SongRegistryTest.hx b/tests/unit/source/funkin/data/song/SongRegistryTest.hx
new file mode 100644
index 000000000..c623306a6
--- /dev/null
+++ b/tests/unit/source/funkin/data/song/SongRegistryTest.hx
@@ -0,0 +1,133 @@
+package funkin.data.song;
+
+import funkin.data.song.SongData;
+import funkin.data.song.SongRegistry;
+import funkin.play.song.Song;
+import massive.munit.Assert;
+import massive.munit.async.AsyncFactory;
+import massive.munit.util.Timer;
+
+@:nullSafety
+@:access(funkin.play.song.Song)
+@:access(funkin.data.song.SongRegistry)
+class SongRegistryTest extends FunkinTest
+{
+  public function new()
+  {
+    super();
+  }
+
+  @BeforeClass
+  public function beforeClass():Void
+  {
+    FunkinAssert.initAssertTrace();
+    SongRegistry.instance.loadEntries();
+  }
+
+  @AfterClass
+  public function afterClass():Void {}
+
+  @Before
+  public function setup():Void {}
+
+  @After
+  public function tearDown():Void {}
+
+  @Test
+  public function testValid():Void
+  {
+    Assert.isNotNull(SongRegistry.instance);
+  }
+
+  @Test
+  public function testParseMetadata():Void
+  {
+    var result:Null<SongData.SongMetadata> = SongRegistry.instance.parseEntryMetadata("bopeebo");
+
+    Assert.isNotNull(result);
+
+    var expectedVersion:thx.semver.Version = "2.0.0";
+    Assert.areEqual(expectedVersion, result.version);
+    Assert.areEqual("Bopeebo", result.songName);
+    Assert.areEqual("Kawai Sprite", result.artist);
+    Assert.areEqual(SongData.SongTimeFormat.MILLISECONDS, result.timeFormat);
+    Assert.areEqual("MasterEric (by hand)", result.generatedBy);
+  }
+
+  @Test
+  public function testParseChartData():Void
+  {
+    var result:Null<SongData.SongChartData> = SongRegistry.instance.parseEntryChartData("bopeebo");
+
+    Assert.isNotNull(result);
+
+    var expectedVersion:thx.semver.Version = "2.0.0";
+    Assert.areEqual(expectedVersion, result.version);
+  }
+
+  /**
+   * A test validating an error is thrown when attempting to parse chart data as metadata.
+   */
+  @Test
+  public function testParseMetadataSwapped():Void
+  {
+    // Arrange
+    FunkinAssert.clearTraces();
+
+    // Act
+    var result:Null<SongData.SongMetadata> = SongRegistry.instance.parseEntryMetadata("bopeebo-swapped");
+
+    // Assert
+    Assert.isNull(result);
+    FunkinAssert.assertTrace("[SONG] Failed to parse entry data: bopeebo-swapped");
+    FunkinAssert.assertTrace("  Unknown variable \"scrollSpeed\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-metadata.json:3");
+    FunkinAssert.assertTrace("  Unknown variable \"events\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-metadata.json:7");
+    FunkinAssert.assertTrace("  Unknown variable \"notes\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-metadata.json:185");
+    FunkinAssert.assertTrace("  Uninitialized variable \"songName\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-metadata.json:1738");
+    FunkinAssert.assertTrace("  Uninitialized variable \"timeFormat\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-metadata.json:1738");
+    FunkinAssert.assertTrace("  Uninitialized variable \"timeChanges\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-metadata.json:1738");
+    FunkinAssert.assertTrace("  Uninitialized variable \"playData\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-metadata.json:1738");
+    FunkinAssert.assertTrace("  Uninitialized variable \"artist\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-metadata.json:1738");
+  }
+
+  /**
+   * A test validating an error is thrown when attempting to parse metadata as chart data.
+   */
+  @Test
+  public function testParseChartDataSwapped():Void
+  {
+    // Arrange
+    FunkinAssert.clearTraces();
+
+    // Act
+    var result:Null<SongData.SongChartData> = SongRegistry.instance.parseEntryChartData("bopeebo-swapped");
+
+    // Assert
+    Assert.isNull(result);
+    FunkinAssert.assertTrace("[SONG] Failed to parse entry data: bopeebo-swapped");
+    FunkinAssert.assertTrace("  Unknown variable \"songName\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-chart.json:3");
+    FunkinAssert.assertTrace("  Unknown variable \"artist\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-chart.json:4");
+    FunkinAssert.assertTrace("  Unknown variable \"timeFormat\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-chart.json:5");
+    FunkinAssert.assertTrace("  Unknown variable \"timeChanges\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-chart.json:6");
+    FunkinAssert.assertTrace("  Unknown variable \"playData\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-chart.json:7");
+    FunkinAssert.assertTrace("  Uninitialized variable \"notes\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-chart.json:15");
+    FunkinAssert.assertTrace("  Uninitialized variable \"events\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-chart.json:15");
+    FunkinAssert.assertTrace("  Uninitialized variable \"scrollSpeed\"");
+    FunkinAssert.assertTrace("    at assets/data/songs/bopeebo-swapped/bopeebo-swapped-chart.json:15");
+  }
+}
diff --git a/tests/unit/source/funkin/play/notes/notestyle/NoteStyleTest.hx b/tests/unit/source/funkin/play/notes/notestyle/NoteStyleTest.hx
index 6b4b46c10..f2d3e570c 100644
--- a/tests/unit/source/funkin/play/notes/notestyle/NoteStyleTest.hx
+++ b/tests/unit/source/funkin/play/notes/notestyle/NoteStyleTest.hx
@@ -1,11 +1,18 @@
 package funkin.play.notes.notestyle;
 
+import flixel.util.FlxSort;
+import funkin.util.SortUtil;
+import flixel.graphics.FlxGraphic;
+import flixel.graphics.frames.FlxFrame;
+import flixel.graphics.frames.FlxFramesCollection;
 import massive.munit.util.Timer;
 import massive.munit.Assert;
 import massive.munit.async.AsyncFactory;
 import funkin.data.notestyle.NoteStyleRegistry;
 import funkin.play.notes.notestyle.NoteStyle;
 import flixel.animation.FlxAnimationController;
+import openfl.utils.Assets;
+import flixel.math.FlxPoint;
 
 @:access(funkin.play.notes.notestyle.NoteStyle)
 class NoteStyleTest extends FunkinTest
@@ -31,20 +38,142 @@ class NoteStyleTest extends FunkinTest
   public function tearDown() {}
 
   @Test
-  @Ignore("This test doesn't work, crashes when the project has 2 mocks of the same class???")
   public function testBuildNoteSprite()
   {
     var target:Null<NoteStyle> = NoteStyleRegistry.instance.fetchEntry("funkin");
 
     Assert.isNotNull(target);
 
-    var mockNoteSprite:NoteSprite = mock(NoteSprite);
-    // var mockAnim = mock(FlxAnimationController);
-    // mockNoteSprite.animation = mockAnim;
+    // Arrange
+    var mockNoteSprite = Mockatoo.mock(NoteSprite);
+    var mockAnim = Mockatoo.mock(FlxAnimationController);
+    var scale = new FlxPoint(1, 1); // handle sprite.scale.x on the mock
 
+    mockNoteSprite.animation = mockAnim; // Tell the mock to forward calls to the animation controller mock.
+    mockNoteSprite.scale.returns(scale); // Redirect this final variable to a local variable.
+    mockNoteSprite.antialiasing.callsRealMethod(); // Tell the mock to treat this like a normal property.
+    mockNoteSprite.frames.callsRealMethod(); // Tell the mock to treat this like a normal property.
+
+    // Act
     target.buildNoteSprite(mockNoteSprite);
 
-    Assert.areEqual(mockNoteSprite.frames, []);
+    var expectedGraphic:FlxGraphic = FlxG.bitmap.add("shared:assets/shared/images/arrows.png");
+
+    // Assert
+    Assert.isNotNull(mockNoteSprite.frames);
+    mockNoteSprite.frames.frames.sort(SortUtil.byFrameName);
+    var frameCount:Int = mockNoteSprite.frames.frames.length;
+    Assert.areEqual(24, frameCount);
+
+    // Validate each frame.
+    for (i in 0...frameCount)
+    {
+      var currentFrame:FlxFrame = mockNoteSprite.frames.frames[i];
+      switch (i)
+      {
+        case 0:
+          Assert.areEqual("confirmDown0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 1:
+          Assert.areEqual("confirmDown0002", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 2:
+          Assert.areEqual("confirmLeft0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 3:
+          Assert.areEqual("confirmLeft0002", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 4:
+          Assert.areEqual("confirmRight0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 5:
+          Assert.areEqual("confirmRight0002", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 6:
+          Assert.areEqual("confirmUp0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 7:
+          Assert.areEqual("confirmUp0002", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 8:
+          Assert.areEqual("noteDown0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 9:
+          Assert.areEqual("noteLeft0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 10:
+          Assert.areEqual("noteRight0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 11:
+          Assert.areEqual("noteUp0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 12:
+          Assert.areEqual("pressedDown0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 13:
+          Assert.areEqual("pressedDown0002", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 14:
+          Assert.areEqual("pressedLeft0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 15:
+          Assert.areEqual("pressedLeft0002", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 16:
+          Assert.areEqual("pressedRight0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 17:
+          Assert.areEqual("pressedRight0002", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 18:
+          Assert.areEqual("pressedUp0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 19:
+          Assert.areEqual("pressedUp0002", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 20:
+          Assert.areEqual("staticDown0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 21:
+          Assert.areEqual("staticLeft0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 22:
+          Assert.areEqual("staticRight0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        case 23:
+          Assert.areEqual("staticUp0001", currentFrame.name);
+          Assert.areEqual(expectedGraphic, currentFrame.parent);
+        default:
+          Assert.fail('Got unexpected frame number ${i}');
+      }
+    }
+
+    // Verify animations were applied.
+    @:privateAccess {
+      mockAnim.addByPrefix('purpleScroll', 'noteLeft', 24, false, false, false).verify(times(1));
+      mockAnim.addByPrefix('blueScroll', 'noteDown', 24, false, false, false).verify(times(1));
+      mockAnim.addByPrefix('greenScroll', 'noteUp', 24, false, false, false).verify(times(1));
+      mockAnim.addByPrefix('redScroll', 'noteRight', 24, false, false, false).verify(times(1));
+      mockAnim.destroyAnimations().verify(times(1));
+      mockAnim.set_frameIndex(0).verify(times(1));
+      // Verify there were no other functions called.
+      mockAnim.verifyZeroInteractions();
+    }
+
+    // Verify sprite was initialized.
+    @:privateAccess {
+      mockNoteSprite.set_graphic(expectedGraphic).verify(times(1));
+      mockNoteSprite.graphicLoaded().verify(times(1));
+      mockNoteSprite.set_antialiasing(true).verify(times(1));
+      mockNoteSprite.set_frames(mockNoteSprite.frames).verify(times(1));
+      mockNoteSprite.set_frame(mockNoteSprite.frames.frames[21]).verify(times(1));
+      mockNoteSprite.resetHelpers().verify(times(1));
+
+      Assert.areEqual(1, mockNoteSprite.scale.x);
+      Assert.areEqual(1, mockNoteSprite.scale.y);
+      // Verify there were no other functions called.
+      mockNoteSprite.verifyZeroInteractions();
+    }
   }
 
   @Test
diff --git a/tests/unit/source/funkin/util/SortUtilTest.hx b/tests/unit/source/funkin/util/SortUtilTest.hx
index 1a39bf655..5b868b278 100644
--- a/tests/unit/source/funkin/util/SortUtilTest.hx
+++ b/tests/unit/source/funkin/util/SortUtilTest.hx
@@ -3,7 +3,7 @@ package funkin.util;
 import flixel.FlxObject;
 import flixel.FlxSprite;
 import flixel.util.FlxSort;
-import funkin.play.song.SongData.SongNoteData;
+import funkin.data.song.SongData.SongNoteData;
 import massive.munit.util.Timer;
 import massive.munit.Assert;
 import massive.munit.async.AsyncFactory;
diff --git a/tests/unit/source/funkin/util/assets/FlxAnimationUtilTest.hx b/tests/unit/source/funkin/util/assets/FlxAnimationUtilTest.hx
index 02b03055d..8adfd3565 100644
--- a/tests/unit/source/funkin/util/assets/FlxAnimationUtilTest.hx
+++ b/tests/unit/source/funkin/util/assets/FlxAnimationUtilTest.hx
@@ -34,8 +34,8 @@ class FlxAnimationUtilTest extends FunkinTest
   public function testAddAtlasAnimation()
   {
     // Build a mock child class of FlxSprite
-    var mockSprite = mock(FlxSprite);
-    var mockAnim = mock(FlxAnimationController);
+    var mockSprite = Mockatoo.mock(FlxSprite);
+    var mockAnim = Mockatoo.mock(FlxAnimationController);
     mockSprite.animation = mockAnim;
 
     var animData:AnimationData =
@@ -85,8 +85,8 @@ class FlxAnimationUtilTest extends FunkinTest
   public function testAddAtlasAnimations()
   {
     // Build a mock child class of FlxSprite
-    var mockSprite = mock(FlxSprite);
-    var mockAnim = mock(FlxAnimationController);
+    var mockSprite = Mockatoo.mock(FlxSprite);
+    var mockAnim = Mockatoo.mock(FlxAnimationController);
     mockSprite.animation = mockAnim;
 
     var animData:Array<AnimationData> = [