Funkin/tests/unit/source/FunkinAssert.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

130 lines
4.9 KiB
Haxe

package;
import flixel.math.FlxPoint;
import flixel.math.FlxRect;
import haxe.PosInfos;
import massive.munit.Assert;
using flixel.util.FlxArrayUtil;
/**
* @see https://github.com/HaxeFlixel/flixel/tree/dev/tests/unit
*/
@:nullSafety
class FunkinAssert
{
/**
* Assert if `expected` is within `margin` of `actual`, and fail if not.
* Useful for comparting Float values.
*
* @param expected The expected value of the test.
* @param actual The actual value of the test.
* @param margin The allowed margin of error between the expected and actual values.
* @param info Info on the position this function was called from. Magic value, passed automatically.
*/
public static function areNear(expected:Float, ?actual:Float, margin:Float = 0.001, ?info:PosInfos):Void
{
if (actual == null) Assert.fail('Value [$actual] is null, and cannot be compared to [$expected]', info);
if (areNearHelper(expected, actual)) Assert.assertionCount++;
else
Assert.fail('Value [$actual] is not within [$margin] of [$expected]', info);
}
public static function rectsNear(expected:FlxRect, ?actual:FlxRect, margin:Float = 0.001, ?info:PosInfos):Void
{
if (actual == null) Assert.fail('Value [$actual] is null, and cannot be compared to [$expected]', info);
var areNear = areNearHelper(expected.x, actual.x, margin)
&& areNearHelper(expected.y, actual.y, margin)
&& areNearHelper(expected.width, actual.width, margin)
&& areNearHelper(expected.height, actual.height, margin);
if (areNear) Assert.assertionCount++;
else
Assert.fail('Value [$actual] is not within [$margin] of [$expected]', info);
}
static function areNearHelper(expected:Float, actual:Float, margin:Float = 0.001):Bool
{
return actual >= expected - margin && actual <= expected + margin;
}
public static function arraysEqual<T>(expected:Array<T>, ?actual:Array<T>, ?info:PosInfos):Void
{
if (actual == null) Assert.fail('Value [$actual] is null, and cannot be compared to [$expected]', info);
if (expected.equals(actual)) Assert.assertionCount++;
else
Assert.fail('\nExpected\n ${expected}\nbut was\n ${actual}\n', info);
}
public static function arraysNotEqual<T>(expected:Array<T>, ?actual:Array<T>, ?info:PosInfos):Void
{
if (actual == null) Assert.fail('Value [$actual] is null, and cannot be compared to [$expected]', info);
if (!expected.equals(actual)) Assert.assertionCount++;
else
Assert.fail('\nValue\n ${actual}\nwas equal to\n ${expected}\n', info);
}
public static function pointsEqual(expected:FlxPoint, ?actual:FlxPoint, ?msg:String, ?info:PosInfos)
{
if (actual == null) Assert.fail('Value [$actual] is null, and cannot be compared to [$expected]', info);
if (expected.equals(actual)) Assert.assertionCount++;
else if (msg != null) Assert.fail(msg, info);
else
Assert.fail("Value [" + actual + "] was not equal to expected value [" + expected + "]", info);
}
public static function pointsNotEqual(expected:FlxPoint, ?actual:FlxPoint, ?msg:String, ?info:PosInfos)
{
if (actual == null) Assert.fail('Value [$actual] is null, and cannot be compared to [$expected]', info);
if (!expected.equals(actual)) Assert.assertionCount++;
else if (msg != null) Assert.fail(msg, info);
else
Assert.fail("Value [" + actual + "] was equal to value [" + expected + "]", info);
}
/**
* Execute `targetFunc`, expecting it to throw an exception.
* If it doesn't, or if the exception doesn't validate against the provided `predicate`, fail.
*/
public static function validateThrows(targetFunc:Void->Void, predicate:Dynamic->Bool, ?info:PosInfos)
{
try
{
targetFunc();
Assert.fail("Expected exception to be thrown, got no failure.", info);
}
catch (e:Dynamic)
{
if (predicate(e))
{
Assert.assertionCount++;
}
else
{
Assert.fail('Expected exception to match predicate, but failed (got ${e})', info);
}
}
}
/**
* Execute `targetFunc`, expecting it to throw a `json2object.Error.CustomFunctionException` with a message matching `expected`.
* I made this its own function since it's the most common specific use case of `validateThrows`.
*/
public static function validateThrowsJ2OCustom(targetFunc:Void->Void, expected:String, ?info:PosInfos)
{
var predicate:Dynamic->Bool = function(err:Dynamic):Bool {
if (!Std.isOfType(err, json2object.Error)) Assert.fail('Expected error of type json2object.Error, got ${Type.typeof(err)}');
switch (err)
{
case json2object.Error.CustomFunctionException(msg, pos):
if (msg != expected) Assert.fail('Expected message [${expected}], got [${msg}].');
default:
Assert.fail('Expected error of type CustomFunctionException, got [${err}].');
}
return true;
};
validateThrows(targetFunc, predicate, info);
}
}