Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DataProvider support, async fixes, headless AIR output #164

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 100 additions & 28 deletions src/massive/munit/TestClassHelper.hx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,18 @@ class TestClassHelper
*/
public inline static var META_TAG_TEST_DEBUG:String = "TestDebug";

/**
* Meta tag marking a test as having an argument data provider
*/
public inline static var META_TAG_DATA_PROVIDER:String = "DataProvider";

/**
* Pseudo meta tag marking method inheritance depth. Auto generated
* by test parser, thus not in META_TAGS below as it is never expected
* to be in a test file.
**/
public inline static var META_TAG_INHERITANCE_DEPTH:String = "InheritanceDepth";

/**
* Array of all valid meta tags.
*/
Expand All @@ -116,24 +128,24 @@ class TestClassHelper
public var test(default, null):Dynamic;

/**
* The life cycle method to be called once, before tests in the class are executed.
* The life cycle methods to be called once, before tests in the class are executed.
*/
public var beforeClass(default, null):Function;
public var beforeClass(default, null):Array<Function>;

/**
* The life cycle method to be called once, after tests in the class are executed.
* The life cycle methods to be called once, after tests in the class are executed.
*/
public var afterClass(default, null):Function;
public var afterClass(default, null):Array<Function>;

/**
* The life cycle method to be called once, before each test in the class is executed.
* The life cycle methods to be called once, before each test in the class is executed.
*/
public var before(default, null):Function;
public var before(default, null):Array<Function>;

/**
* The life cycle method to be called once, after each test in the class is executed.
* The life cycle methods to be called once, after each test in the class is executed.
*/
public var after(default, null):Function;
public var after(default, null):Array<Function>;

public var className(default, null):String;
var tests:Array<TestCaseData> = [];
Expand All @@ -150,10 +162,10 @@ class TestClassHelper
this.type = type;
this.isDebug = isDebug;
className = Type.getClassName(type);
beforeClass = nullFunc;
afterClass = nullFunc;
before = nullFunc;
after = nullFunc;
beforeClass = new Array();
afterClass = new Array();
before = new Array();
after = new Array();
parse(type);
}

Expand Down Expand Up @@ -195,6 +207,9 @@ class TestClassHelper
var fieldMeta = collateFieldMeta(inherintanceChain);
scanForTests(fieldMeta);
tests.sort(sortTestsByName); // not pc as allows for possible test dependencies but useful for report consistency
// after methods should be called from subclass to base class order
after.reverse();
afterClass.reverse();
}

function getInheritanceChain(clazz:Class<Dynamic>):Array<Class<Dynamic>>
Expand All @@ -208,10 +223,18 @@ class TestClassHelper
function collateFieldMeta(inherintanceChain:Array<Class<Dynamic>>):Dynamic
{
var meta = {};
var depth = -1; // initially negative since incremented at top of loop
var i = inherintanceChain.length;
while (i-- > 0)
{
var clazz = inherintanceChain[i]; // start at root
// go to next inheritance depth
depth++;
// update lifecycle function arrays with new depth
beforeClass.push(nullFunc);
afterClass.push(nullFunc);
before.push(nullFunc);
after.push(nullFunc);
var newMeta = Meta.getFields(clazz);
var markedFieldNames = Reflect.fields(newMeta);

Expand All @@ -229,7 +252,9 @@ class TestClassHelper
var tagsCopy = {};
for (tagName in newTagNames)
Reflect.setField(tagsCopy, tagName, Reflect.field(newFieldTags, tagName));

// remember the inheritance depth of this field with a pseudo-tag
Reflect.setField(tagsCopy, META_TAG_INHERITANCE_DEPTH, [depth]);

Reflect.setField(meta, fieldName, tagsCopy);
}
else
Expand All @@ -251,6 +276,9 @@ class TestClassHelper
var tagValue = Reflect.field(newFieldTags, tagName);
Reflect.setField(recordedFieldTags, tagName, tagValue);
}
// update the inheritance depth of this field, as it overrides the
// earlier definition
Reflect.setField(recordedFieldTags, META_TAG_INHERITANCE_DEPTH, [depth]);
}
}
}
Expand All @@ -271,13 +299,16 @@ class TestClassHelper

function searchForMatchingTags(fieldName:String, func:Dynamic, funcMeta:Dynamic)
{
var depth = Reflect.field(funcMeta, META_TAG_INHERITANCE_DEPTH)[0];
for (tag in META_TAGS)
{
if (!Reflect.hasField(funcMeta, tag)) continue;
var args:Array<String> = Reflect.field(funcMeta, tag);
var description = (args != null) ? args[0] : "";
var isAsync = (args != null && description == META_PARAM_ASYNC_TEST); // deprecated support for @Test("Async")
var isIgnored = Reflect.hasField(funcMeta, META_TAG_IGNORE);
var hasDataProvider = Reflect.hasField(funcMeta, META_TAG_DATA_PROVIDER);
var dataProvider:String = null;

if (isAsync)
{
Expand All @@ -289,28 +320,69 @@ class TestClassHelper
description = (args != null) ? args[0] : "";
}

if (hasDataProvider)
{
args = Reflect.field(funcMeta, META_TAG_DATA_PROVIDER);
if (args != null)
{
dataProvider = args[0];
}
else
{
throw new MUnitException("Missing dataProvider source", null);
}
}

switch(tag)
{
case META_TAG_BEFORE_CLASS: beforeClass = func;
case META_TAG_AFTER_CLASS: afterClass = func;
case META_TAG_BEFORE: before = func;
case META_TAG_AFTER: after = func;
case META_TAG_ASYNC_TEST: if(!isDebug) addTest(fieldName, func, test, true, isIgnored, description);
case META_TAG_TEST: if(!isDebug) addTest(fieldName, func, test, isAsync, isIgnored, description);
case META_TAG_TEST_DEBUG: if(isDebug) addTest(fieldName, func, test, isAsync, isIgnored, description);
case META_TAG_BEFORE_CLASS: beforeClass[depth] = func;
case META_TAG_AFTER_CLASS: afterClass[depth] = func;
case META_TAG_BEFORE: before[depth] = func;
case META_TAG_AFTER: after[depth] = func;
case META_TAG_ASYNC_TEST: if(!isDebug) addTest(fieldName, func, test, true, isIgnored, description, dataProvider);
case META_TAG_TEST: if(!isDebug) addTest(fieldName, func, test, isAsync, isIgnored, description, dataProvider);
case META_TAG_TEST_DEBUG: if(isDebug) addTest(fieldName, func, test, isAsync, isIgnored, description, dataProvider);
}
}
}

function addTest(field:String, testFunction:Function, testInstance:Dynamic, isAsync:Bool, isIgnored:Bool, description:String)
function addTest(field:String, testFunction:Function, testInstance:Dynamic, isAsync:Bool, isIgnored:Bool, description:String, dataProvider:String)
{
var result:TestResult = new TestResult();
result.async = isAsync;
result.ignore = isIgnored;
result.className = className;
result.description = description;
result.name = field;
tests.push({scope:testInstance, test:testFunction, result:result});
var argsData:Array<Array<Dynamic>> = [[]];
if (dataProvider != null)
{
// look for object instance field
var provider:Dynamic = Reflect.field(testInstance, dataProvider);
if (null == provider) {
// look for static class field
provider = Reflect.field(Type.getClass(testInstance), dataProvider);
}
if (Reflect.isFunction(provider))
{
provider = Reflect.callMethod(testInstance, provider, []);
}
if (Std.is(provider, Array))
{
argsData = cast provider;
}
else
{
throw new MUnitException("dataProvider \'" + dataProvider +
"\' did not provide args array", null);
}
}
for (args in argsData)
{
var result:TestResult = new TestResult();
result.async = isAsync;
result.ignore = isIgnored;
result.className = className;
result.description = description;
result.name = field;
result.args = args;
var data:TestCaseData = { test:testFunction, scope:testInstance, result:result };
tests.push(data);
}
}

function sortTestsByName(x:TestCaseData, y:TestCaseData):Int
Expand Down
15 changes: 10 additions & 5 deletions src/massive/munit/TestResult.hx
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ class TestResult
public var className:String = "";

/**
* An optional description.
*/
* An optional description.
*/
public var description:String = "";

/**
Expand All @@ -78,9 +78,14 @@ class TestResult
public var async:Bool = false;

/**
* Whether the test is ignored or not.
*/
public var ignore:Bool = false;
* Whether the test is ignored or not.
*/
public var ignore:Bool = false;

/**
* Arguments for the test, or null if no args
**/
public var args:Array<Dynamic>;

/**
* If this test failed, the assertion exception that was captured.
Expand Down
Loading