Funkin/tests/unit/source/funkin/data/BaseRegistryTest.hx
Eric 279277b18c Unit Tests: Coverage Reporting and Github Actions Integration (#131)
* Initial test suite

* Fix some build warnings

* Implemented working unit tests with coverage

* Reduced some warnings

* Fix a mac-specific issue

* Add 2 additional unit test classes.

* Multiple new unit tests

* Some fixins

* Remove auto-generated file

* WIP on hiding ignored tests

* Added list of debug hotkeys

* Remove old website

* Remove empty file

* Add more unit tests

* Fix bug where arrows would nudge BF

* Fix bug where ctrl/alt would flash capsules

* Fixed bug where bf-old easter egg broke

* Remove duplicate lines

* More test-related stuff

* Some code cleanup

* Add mocking and a test assets folder

* More TESTS!

* Update Hmm...

* Update artist on Monster

* More minor fixes to individual functions

* 1.38% unit test coverage!

* Even more tests? :O

* More unit test work

* Rework migration for BaseRegistry

* gameover fix

* Fix an issue with Lime

* Fix issues with version parsing on data files

* 100 total unit tests!

* Added even MORE unit tests!

* Additional test tweaks :3

* Fixed tests on windows by updating libraries.

* A bunch of smaller syntax tweaks.

* New crash handler catches and logs critical errors!

* Chart editor now has null safety enabled.

* Null safety on all tests

* New Level data test

* Generate proper code coverage reports!

* Disable null safety on ChartEditorState for unit testing

* Update openfl to use latest fixes for crash reporting

* Added unit test to Github Workflow

* Updated unit tests to compile with null safety enabled by inlining assertions.

* Added coverage gutters as a recommended extension

* Impreovements to tests involving exceptions

* Disable a few incomplete tests.

* Add scripts for building unit coverage reports on linux

---------

Co-authored-by: Cameron Taylor <cameron.taylor.ninja@gmail.com>
2023-08-30 18:31:59 -04:00

230 lines
5.1 KiB
Haxe

package funkin.data;
import massive.munit.util.Timer;
import massive.munit.Assert;
import massive.munit.async.AsyncFactory;
import funkin.data.BaseRegistry;
import funkin.util.SortUtil;
import funkin.util.VersionUtil;
@:nullSafety
@:access(funkin.data.BaseRegistry)
class BaseRegistryTest extends FunkinTest
{
public function new()
{
super();
}
@BeforeClass
public function beforeClass() {}
@AfterClass
public function afterClass() {}
@Before
public function setup() {}
@After
public function tearDown() {}
@Test
public function testMyTypeRegistry()
{
// This shouldn't crash.
MyTypeRegistry.instance.loadEntries();
// Ensure all entries were loaded.
var entryList = MyTypeRegistry.instance.listEntryIds();
entryList.sort(SortUtil.alphabetically);
Assert.areEqual(entryList, [
"blablabla",
"fizzbuzz",
"foobar",
// "junk"
]);
// Ensure this one is not in the list.
Assert.areEqual(entryList.indexOf("junk"), -1);
// Ensure blablabla got parsed correctly.
var blablabla = MyTypeRegistry.instance.fetchEntry("blablabla");
Assert.isNotNull(blablabla);
Assert.areEqual(blablabla.id, "blablabla");
Assert.areEqual(blablabla._data.version, "1.0.0");
Assert.areEqual(blablabla._data.name, "blablabla API");
}
}
typedef MyTypeData =
{
/**
* The version number of the data schema.
* When making changes to the note style data format, this should be incremented,
* and a migration function should be added to handle old versions.
*/
@:default(funkin.data.BaseRegistryTest.MyTypeRegistry.DATA_VERSION)
var version:String;
var id:String;
var name:String;
var data:Array<MySubTypeData>;
}
typedef MySubTypeData =
{
var foo:String;
var bar:String;
};
typedef MyTypeData_v0_1_x =
{
var version:String;
var id:Int;
var name:String;
};
class MyType implements IRegistryEntry<MyTypeData>
{
/**
* The ID of the mytype.
*/
public final id:String;
/**
* Mytype data as parsed from the JSON file.
*/
public final _data:MyTypeData;
/**
* @param id The ID of the JSON file to parse.
*/
public function new(id:String)
{
this.id = id;
_data = _fetchData(id);
if (_data == null)
{
throw 'Could not parse mytype data for id: $id';
}
}
public function destroy():Void {}
public function toString():String
{
return 'MyType($id)';
}
public function _fetchData(id:String):Null<MyTypeData>
{
return MyTypeRegistry.instance.parseEntryDataWithMigration(id, MyTypeRegistry.instance.fetchEntryVersion(id));
}
public function getSubData():Array<MySubTypeData>
{
return _data.data;
}
}
class MyTypeRegistry extends BaseRegistry<MyType, MyTypeData>
{
/**
* The current version string for the note style data format.
* Handle breaking changes by incrementing this value
* and adding migration to the `migrateMyTypeData()` function.
*/
public static final DATA_VERSION:String = "1.0.0";
public static final instance:MyTypeRegistry = new MyTypeRegistry();
public function new()
{
super('MYTYPE', 'mytype');
}
/**
* Read, parse, and validate the JSON data and produce the corresponding data object.
*/
public function parseEntryData(id:String):Null<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);
if (parser.errors.length > 0)
{
trace('[${registryId}] Failed to parse entry data: ${id}');
for (error in parser.errors)
{
trace(error);
}
return null;
}
return parser.value;
}
/**
* Read, parse, and validate the JSON data and produce the corresponding data object.
*/
public function parseEntryData_v0_1_x(id:String):Null<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);
if (parser.errors.length > 0)
{
trace('[${registryId}] Failed to parse entry data: ${id}');
for (error in parser.errors)
{
trace(error);
}
return null;
}
var oldData:MyTypeData_v0_1_x = parser.value;
var result:MyTypeData =
{
version: DATA_VERSION,
id: '${oldData.id}',
name: oldData.name,
data: []
};
return result;
}
public override function parseEntryDataWithMigration(id:String, version:thx.semver.Version):Null<MyTypeData>
{
if (VersionUtil.validateVersion(version, "0.1.x"))
{
trace('Migrating mytype data from ${version} to ${DATA_VERSION}');
return parseEntryData_v0_1_x(id);
}
else
{
trace('Parsing mytype data with version ${version}');
return super.parseEntryDataWithMigration(id, version);
}
}
function createScriptedEntry(clsName:String):MyType
{
return null;
}
function getScriptedClassNames():Array<String>
{
return [];
}
}