Updated and revised unit tests (including new SongRegistry)

This commit is contained in:
EliteMasterEric 2023-09-08 17:46:10 -04:00
parent 7e1c11bb25
commit dfedaa8838
16 changed files with 3882 additions and 101 deletions

View file

@ -7,7 +7,7 @@
"note": { "note": {
"assetPath": "shared:arrows", "assetPath": "shared:arrows",
"scale": 1.0, "scale": 1.0,
"isPixel": true, "isPixel": false,
"data": { "data": {
"left": { "prefix": "noteLeft" }, "left": { "prefix": "noteLeft" },
"down": { "prefix": "noteDown" }, "down": { "prefix": "noteDown" },
@ -19,7 +19,7 @@
"assetPath": "shared:arrows", "assetPath": "shared:arrows",
"scale": 1.0, "scale": 1.0,
"offsets": [28, 32], "offsets": [28, 32],
"isPixel": true, "isPixel": false,
"data": { "data": {
"leftStatic": { "prefix": "staticLeft0" }, "leftStatic": { "prefix": "staticLeft0" },
"leftPress": { "prefix": "pressedLeft0" }, "leftPress": { "prefix": "pressedLeft0" },

View file

@ -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)"
}

File diff suppressed because it is too large Load diff

View file

@ -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)"
}

View file

@ -44,11 +44,12 @@
<!-- Assets --> <!-- Assets -->
<assets path="assets/preload" rename="assets" exclude="*.ogg" if="web" /> <assets path="assets/preload" rename="assets" exclude="*.ogg" if="web" />
<assets path="assets/preload" rename="assets" exclude="*.mp3" unless="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|*.ogg" if="web" />
<assets path="assets/songs" library="songs" exclude="*.fla|*.mp3" unless="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|*.ogg" if="web" />
<assets path="assets/tutorial" library="tutorial" exclude="*.fla|*.mp3" unless="web" /> <assets path="assets/tutorial" library="tutorial" exclude="*.fla|*.mp3" unless="web" />
<assets path="assets/week1" library="week1" exclude="*.fla|*.ogg" if="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|*.ogg" if="web" />
<assets path="assets/weekend1" library="weekend1" exclude="*.fla|*.mp3" unless="web" /> <assets path="assets/weekend1" library="weekend1" exclude="*.fla|*.mp3" unless="web" />
<library name="songs" preload="true" /> <library name="songs" preload="true" />
<library name="shared" preload="true" />
<library name="tutorial" preload="true" /> <library name="tutorial" preload="true" />
<library name="week1" preload="true" /> <library name="week1" preload="true" />
<library name="week2" preload="true" /> <library name="week2" preload="true" />
@ -87,8 +87,8 @@
<haxedef name="FLX_RECORD" /> <haxedef name="FLX_RECORD" />
<!-- Clean up the output --> <!-- Clean up the output -->
<haxedef name="no-traces" />
<!-- <!--
<haxedef name="echo-traces" />
--> -->
<haxedef name="ignore-inline" /> <haxedef name="ignore-inline" />
<haxeflag name="-w" value="-WDeprecated" /> <haxeflag name="-w" value="-WDeprecated" />

View file

@ -127,4 +127,54 @@ class FunkinAssert
}; };
validateThrows(targetFunc, predicate, info); 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);
}
} }

View file

@ -30,8 +30,8 @@ class MockTest extends FunkinTest
{ {
// Test that mocking works. // Test that mocking works.
var mockSprite = mockatoo.Mockatoo.mock(flixel.FlxSprite); var mockSprite = Mockatoo.mock(flixel.FlxSprite);
var mockAnim = mockatoo.Mockatoo.mock(flixel.animation.FlxAnimationController); var mockAnim = Mockatoo.mock(flixel.animation.FlxAnimationController);
mockSprite.animation = mockAnim; mockSprite.animation = mockAnim;
var animData:funkin.data.animation.AnimationData = var animData:funkin.data.animation.AnimationData =
@ -44,12 +44,12 @@ class MockTest extends FunkinTest
// Verify that the method was called once. // Verify that the method was called once.
// If not, a VerificationException will be thrown and the test will fail. // 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() { FunkinAssert.validateThrows(function() {
// Attempt to verify the method was called. // Attempt to verify the method was called.
// This should FAIL, since we didn't call the method. // 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) { }, function(err) {
return Std.isOfType(err, mockatoo.exception.VerificationException); return Std.isOfType(err, mockatoo.exception.VerificationException);
}); });

View file

@ -3,7 +3,7 @@ package funkin;
import flixel.FlxG; import flixel.FlxG;
import flixel.FlxState; import flixel.FlxState;
import funkin.Conductor; import funkin.Conductor;
import funkin.play.song.SongData.SongTimeChange; import funkin.data.song.SongData.SongTimeChange;
import funkin.util.Constants; import funkin.util.Constants;
import massive.munit.Assert; import massive.munit.Assert;
@ -16,6 +16,8 @@ class ConductorTest extends FunkinTest
@Before @Before
function before() function before()
{ {
FunkinAssert.initAssertTrace();
resetGame(); resetGame();
// The ConductorState will advance the conductor when step() is called. // The ConductorState will advance the conductor when step() is called.
@ -193,16 +195,7 @@ class ConductorTest extends FunkinTest
function testSingleTimeChange():Void function testSingleTimeChange():Void
{ {
// Start the song with a BPM of 120. // Start the song with a BPM of 120.
var songTimeChanges:Array<SongTimeChange> = [ var songTimeChanges:Array<SongTimeChange> = [new SongTimeChange(0, 120)];
{
t: 0,
b: 0,
bpm: 120,
n: 4,
d: 4,
bt: [4, 4, 4, 4]
}, // 120 bpm starting 0 sec/0 beats
];
Conductor.mapTimeChanges(songTimeChanges); Conductor.mapTimeChanges(songTimeChanges);
// All should be at 0. // All should be at 0.
@ -253,24 +246,7 @@ class ConductorTest extends FunkinTest
function testDoubleTimeChange():Void function testDoubleTimeChange():Void
{ {
// Start the song with a BPM of 120. // Start the song with a BPM of 120.
var songTimeChanges:Array<SongTimeChange> = [ var songTimeChanges:Array<SongTimeChange> = [new SongTimeChange(0, 120), new SongTimeChange(3000, 90)];
{
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
];
Conductor.mapTimeChanges(songTimeChanges); Conductor.mapTimeChanges(songTimeChanges);
// All should be at 0. // 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. // Start the song with a BPM of 120, then move to 90, then move to 180.
var songTimeChanges:Array<SongTimeChange> = [ var songTimeChanges:Array<SongTimeChange> = [
{ new SongTimeChange(0, 120),
t: 0, new SongTimeChange(3000, 90),
b: null, new SongTimeChange(6000, 180)
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
]; ];
Conductor.mapTimeChanges(songTimeChanges); Conductor.mapTimeChanges(songTimeChanges);

View file

@ -17,7 +17,10 @@ class BaseRegistryTest extends FunkinTest
} }
@BeforeClass @BeforeClass
public function beforeClass() {} public function beforeClass()
{
FunkinAssert.initAssertTrace();
}
@AfterClass @AfterClass
public function afterClass() {} public function afterClass() {}
@ -118,7 +121,7 @@ class MyType implements IRegistryEntry<MyTypeData>
return 'MyType($id)'; 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)); 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, // JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry. // otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<MyTypeData>(); 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) if (parser.errors.length > 0)
{ {
trace('[${registryId}] Failed to parse entry data: ${id}'); printErrors(parser.errors, id);
for (error in parser.errors)
{
trace(error);
}
return null; return null;
} }
return parser.value; return parser.value;
@ -177,31 +181,33 @@ class MyTypeRegistry extends BaseRegistry<MyType, MyTypeData>
// JsonParser does not take type parameters, // JsonParser does not take type parameters,
// otherwise this function would be in BaseRegistry. // otherwise this function would be in BaseRegistry.
var parser = new json2object.JsonParser<MyTypeData_v0_1_x>(); 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) if (parser.errors.length > 0)
{ {
trace('[${registryId}] Failed to parse entry data: ${id}'); printErrors(parser.errors, id);
for (error in parser.errors)
{
trace(error);
}
return null; return null;
} }
var oldData:MyTypeData_v0_1_x = parser.value; var oldData:MyTypeData_v0_1_x = parser.value;
return migrateData_v0_1_x(oldData);
}
var result:MyTypeData = function migrateData_v0_1_x(input:MyTypeData_v0_1_x):MyTypeData
{ {
version: DATA_VERSION, return {
id: '${oldData.id}', version: DATA_VERSION,
name: oldData.name, id: '${input.id}',
data: [] name: input.name,
}; data: []
};
return result;
} }
public override function parseEntryDataWithMigration(id:String, version:thx.semver.Version):Null<MyTypeData> public override function parseEntryDataWithMigration(id:String, version:thx.semver.Version):Null<MyTypeData>

View file

@ -123,9 +123,12 @@ class LevelRegistryTest extends FunkinTest
} }
@Test @Test
@Ignore("Requires redoing validation.")
public function testCreateEntryBlankPath():Void 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() { FunkinAssert.validateThrows(function() {
var result:Null<Level> = LevelRegistry.instance.createEntry("blankpathtest"); var result:Null<Level> = LevelRegistry.instance.createEntry("blankpathtest");
}, function(err) { }, function(err) {
@ -134,7 +137,6 @@ class LevelRegistryTest extends FunkinTest
} }
@Test @Test
@Ignore("Requires redoing validation.")
public function testFetchBadEntry():Void public function testFetchBadEntry():Void
{ {
var result:Null<Level> = LevelRegistry.instance.fetchEntry("blablabla"); var result:Null<Level> = LevelRegistry.instance.fetchEntry("blablabla");

View file

@ -86,7 +86,7 @@ class NoteStyleRegistryTest extends FunkinTest
@Test @Test
public function testFetchDefault():Void public function testFetchDefault():Void
{ {
var nsrMock:NoteStyleRegistry = mock(NoteStyleRegistry); var nsrMock = Mockatoo.mock(NoteStyleRegistry);
nsrMock.fetchDefault().callsRealMethod(); nsrMock.fetchDefault().callsRealMethod();

View file

@ -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");
}
}

View file

@ -1,11 +1,18 @@
package funkin.play.notes.notestyle; 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.util.Timer;
import massive.munit.Assert; import massive.munit.Assert;
import massive.munit.async.AsyncFactory; import massive.munit.async.AsyncFactory;
import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.notestyle.NoteStyleRegistry;
import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.notestyle.NoteStyle;
import flixel.animation.FlxAnimationController; import flixel.animation.FlxAnimationController;
import openfl.utils.Assets;
import flixel.math.FlxPoint;
@:access(funkin.play.notes.notestyle.NoteStyle) @:access(funkin.play.notes.notestyle.NoteStyle)
class NoteStyleTest extends FunkinTest class NoteStyleTest extends FunkinTest
@ -31,20 +38,142 @@ class NoteStyleTest extends FunkinTest
public function tearDown() {} public function tearDown() {}
@Test @Test
@Ignore("This test doesn't work, crashes when the project has 2 mocks of the same class???")
public function testBuildNoteSprite() public function testBuildNoteSprite()
{ {
var target:Null<NoteStyle> = NoteStyleRegistry.instance.fetchEntry("funkin"); var target:Null<NoteStyle> = NoteStyleRegistry.instance.fetchEntry("funkin");
Assert.isNotNull(target); Assert.isNotNull(target);
var mockNoteSprite:NoteSprite = mock(NoteSprite); // Arrange
// var mockAnim = mock(FlxAnimationController); var mockNoteSprite = Mockatoo.mock(NoteSprite);
// mockNoteSprite.animation = mockAnim; 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); 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 @Test

View file

@ -3,7 +3,7 @@ package funkin.util;
import flixel.FlxObject; import flixel.FlxObject;
import flixel.FlxSprite; import flixel.FlxSprite;
import flixel.util.FlxSort; import flixel.util.FlxSort;
import funkin.play.song.SongData.SongNoteData; import funkin.data.song.SongData.SongNoteData;
import massive.munit.util.Timer; import massive.munit.util.Timer;
import massive.munit.Assert; import massive.munit.Assert;
import massive.munit.async.AsyncFactory; import massive.munit.async.AsyncFactory;

View file

@ -34,8 +34,8 @@ class FlxAnimationUtilTest extends FunkinTest
public function testAddAtlasAnimation() public function testAddAtlasAnimation()
{ {
// Build a mock child class of FlxSprite // Build a mock child class of FlxSprite
var mockSprite = mock(FlxSprite); var mockSprite = Mockatoo.mock(FlxSprite);
var mockAnim = mock(FlxAnimationController); var mockAnim = Mockatoo.mock(FlxAnimationController);
mockSprite.animation = mockAnim; mockSprite.animation = mockAnim;
var animData:AnimationData = var animData:AnimationData =
@ -85,8 +85,8 @@ class FlxAnimationUtilTest extends FunkinTest
public function testAddAtlasAnimations() public function testAddAtlasAnimations()
{ {
// Build a mock child class of FlxSprite // Build a mock child class of FlxSprite
var mockSprite = mock(FlxSprite); var mockSprite = Mockatoo.mock(FlxSprite);
var mockAnim = mock(FlxAnimationController); var mockAnim = Mockatoo.mock(FlxAnimationController);
mockSprite.animation = mockAnim; mockSprite.animation = mockAnim;
var animData:Array<AnimationData> = [ var animData:Array<AnimationData> = [