-fixed bug in EntityManager -added better support for non-betterC unittests -added many new unittests -slightly improved JUnit xml output -fixed WASM compile script -added new textures -fixed model texture coordinaes in demos -some minor bug fixes in demo TODO: demos cpu usage in non-betterC mode
387 lines
9.9 KiB
D
387 lines
9.9 KiB
D
// Example usage: dub -c unittest-runner -b unittest
|
|
module tests.runner;
|
|
|
|
import core.stdc.stdio;
|
|
import core.stdc.string;
|
|
import core.sys.posix.setjmp;
|
|
|
|
import ecs.vector;
|
|
import ecs.simple_vector;
|
|
import ecs.std;
|
|
|
|
enum int ASSERTED = 123;
|
|
enum string OUT_FILE = "test_report.xml";
|
|
|
|
static jmp_buf gEnvBuffer;
|
|
static AssertInfo gAssertInfo;
|
|
|
|
version (D_BetterC)
|
|
{
|
|
}
|
|
else version = notBetterC;
|
|
|
|
struct AssertInfo
|
|
{
|
|
const(char)* msg;
|
|
const(char)* file;
|
|
int line;
|
|
}
|
|
|
|
extern (C) void __assert(const char* msg, const char* file, int line)
|
|
{
|
|
gAssertInfo = AssertInfo(msg, file, line);
|
|
longjmp(gEnvBuffer, ASSERTED);
|
|
}
|
|
|
|
struct Test
|
|
{
|
|
string file;
|
|
string msg;
|
|
int file_line;
|
|
string name;
|
|
//string classname;
|
|
int time;
|
|
bool passed;
|
|
}
|
|
|
|
struct TestSuite
|
|
{
|
|
string name;
|
|
uint passed = 0;
|
|
uint failed = 0;
|
|
uint skipped = 0;
|
|
Vector!Test tests;
|
|
}
|
|
|
|
string copyString(const char* str)
|
|
{
|
|
auto length = strlen(str);
|
|
char[] arr = cast(char[]) str[0 .. length + 1];
|
|
return cast(string) Mallocator.makeArray(arr);
|
|
}
|
|
|
|
string copyString(string str)
|
|
{
|
|
return cast(string) Mallocator.makeArray((cast(char*)str)[0 .. str.length + 1]);
|
|
}
|
|
|
|
struct TestRunner(Args...)
|
|
{
|
|
void generateJUnit()
|
|
{
|
|
write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n");
|
|
|
|
write("<testsuites tests=\"");
|
|
write(passed + failed);
|
|
write("\" failures=\"");
|
|
write(failed);
|
|
write("\">\n");
|
|
|
|
foreach (ref TestSuite suite; suites)
|
|
{
|
|
write("\t<testsuite name=\"");
|
|
write(suite.name);
|
|
write("\" tests=\"");
|
|
write(suite.passed + suite.failed);
|
|
write("\" failures=\"");
|
|
write(suite.failed);
|
|
write("\" skipped=\"");
|
|
write(suite.skipped);
|
|
write("\">\n");
|
|
|
|
foreach (ref Test test; suite.tests)
|
|
{
|
|
write("\t\t<testcase name=\"");
|
|
write(test.name);
|
|
write("\" classname=\"");
|
|
write(suite.name);
|
|
write("\">");
|
|
if (test.msg)
|
|
{
|
|
write("\n\t\t\t<failure type=\"Fail\" message=\"");
|
|
write(test.msg[0 .. $ - 1]);
|
|
write("\">");
|
|
write("Assert! File: ");
|
|
write(test.file[0 .. $ - 1]);
|
|
write(":");
|
|
write(test.file_line);
|
|
write(" Message: ");
|
|
write(test.msg[0 .. $ - 1]);
|
|
write("</failure>\n");
|
|
write("\t\t</testcase>\n");
|
|
}
|
|
else write("</testcase>\n");
|
|
|
|
}
|
|
|
|
write("\t</testsuite>\n");
|
|
}
|
|
|
|
write("</testsuites>");
|
|
}
|
|
|
|
void runTests(string[] include = null, string[] exclude = null)
|
|
{
|
|
foreach (i, module_; Args)
|
|
{
|
|
TestSuite* suite = &suites[i];
|
|
suite.name = module_.stringof;
|
|
foreach (index, unittest_; __traits(getUnitTests, module_))
|
|
{
|
|
enum attributes = __traits(getAttributes, unittest_);
|
|
static if (attributes.length != 0)
|
|
{
|
|
if (include.length > 0)
|
|
{
|
|
bool matched = false;
|
|
foreach (str; include)
|
|
{
|
|
if (match(str, attributes[0]))
|
|
{
|
|
matched = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
foreach (str; exclude)
|
|
{
|
|
if (match(str, attributes[0]))
|
|
{
|
|
matched = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!matched)
|
|
{
|
|
suite.skipped++;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
else if (include.length > 0)
|
|
{
|
|
suite.skipped++;
|
|
continue;
|
|
}
|
|
|
|
Test test;
|
|
|
|
static if (attributes.length == 0)
|
|
test.name = "None";
|
|
else
|
|
test.name = attributes[0];
|
|
|
|
static if (__traits(hasMember, module_, "beforeEveryTest"))
|
|
module_.beforeEveryTest();
|
|
|
|
version(D_BetterC)
|
|
{
|
|
// Save calling environment for longjmp
|
|
int jmp_ret = setjmp(gEnvBuffer);
|
|
|
|
if (jmp_ret == ASSERTED)
|
|
{
|
|
test.passed = false;
|
|
test.file = copyString(gAssertInfo.file);
|
|
test.file_line = gAssertInfo.line;
|
|
test.msg = copyString(gAssertInfo.msg);
|
|
}
|
|
else
|
|
{
|
|
unittest_();
|
|
test.passed = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
import core.exception : AssertError;
|
|
try
|
|
{
|
|
unittest_();
|
|
test.passed = true;
|
|
}
|
|
catch(AssertError error)
|
|
{
|
|
test.passed = false;
|
|
test.file = copyString(error.file);
|
|
test.file_line = cast(int)error.line;
|
|
test.msg = copyString(error.msg);
|
|
}
|
|
}
|
|
|
|
if (test.passed)
|
|
suite.passed++;
|
|
else
|
|
suite.failed++;
|
|
suite.tests ~= test;
|
|
static if (__traits(hasMember, module_, "afterEveryTest"))
|
|
module_.afterEveryTest();
|
|
}
|
|
passed += suite.passed;
|
|
failed += suite.failed;
|
|
skipped += suite.skipped;
|
|
}
|
|
}
|
|
|
|
void writeFile()
|
|
{
|
|
if (junit.length == 0)
|
|
generateJUnit();
|
|
auto file = fopen(OUT_FILE, "w");
|
|
fwrite(junit.data.ptr, junit.length, 1, file);
|
|
fclose(file);
|
|
}
|
|
|
|
void writeOutput()
|
|
{
|
|
foreach (ref TestSuite suite; suites)
|
|
{
|
|
printf("Suite: \"%s\" Passed: %u/%u Skipped: %u\n", suite.name.ptr,
|
|
suite.passed, suite.passed + suite.failed, suite.skipped);
|
|
foreach (ref Test test; suite.tests)
|
|
{
|
|
if (!test.passed)
|
|
{
|
|
printf("\tTest: \"%s\" Failed! Line: %u Message: %s\n",
|
|
test.name.ptr, test.file_line, test.msg.ptr);
|
|
}
|
|
}
|
|
}
|
|
printf("Passed %u/%u tests. Skipped: %u.\n", passed, passed + failed, skipped);
|
|
}
|
|
|
|
bool match(string a, string b)
|
|
{
|
|
uint i = 0;
|
|
foreach (char c; b)
|
|
{
|
|
if (i > a.length)
|
|
return false;
|
|
if (a[i] == c)
|
|
i++;
|
|
else
|
|
{
|
|
if (a[i] == '*')
|
|
{
|
|
if (i + 1 >= a.length)
|
|
return true;
|
|
else if (a[i + 1] == c)
|
|
i += 2;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
}
|
|
return i == a.length;
|
|
}
|
|
|
|
void write(string txt)
|
|
{
|
|
junit.add(cast(ubyte[]) txt);
|
|
}
|
|
|
|
void write(uint num)
|
|
{
|
|
ubyte[20] buffer;
|
|
int len;
|
|
len = sprintf(cast(char*) buffer.ptr, "%u", num);
|
|
junit.add(buffer[0 .. len]);
|
|
}
|
|
|
|
TestSuite[Args.length] suites;
|
|
uint passed = 0;
|
|
uint failed = 0;
|
|
uint skipped = 0;
|
|
|
|
SimpleVector junit;
|
|
}
|
|
|
|
version (notBetterC)
|
|
{
|
|
extern (C) int rt_init();
|
|
extern (C) int rt_term();
|
|
version (D_Coverage) extern (C) void dmd_coverDestPath(string path);
|
|
}
|
|
|
|
void extractStrings(ref Vector!string container, string str)
|
|
{
|
|
uint s = 0;
|
|
foreach (j, char c; str)
|
|
{
|
|
if (c == ',')
|
|
{
|
|
if (s < j)
|
|
{
|
|
container.add(str[s .. j]);
|
|
}
|
|
s = cast(uint) j + 1;
|
|
}
|
|
}
|
|
if (s < str.length)
|
|
{
|
|
container.add(str[s .. $]);
|
|
}
|
|
}
|
|
|
|
extern (C) int main(int argc, char** args)
|
|
{
|
|
Vector!string include;
|
|
Vector!string exclude;
|
|
|
|
version (notBetterC)
|
|
{
|
|
rt_init();
|
|
version (D_Coverage)
|
|
dmd_coverDestPath("reports");
|
|
}
|
|
|
|
for (int i = 1; i < argc; i++)
|
|
{
|
|
string arg = cast(string) args[i][0 .. strlen(args[i])];
|
|
if (arg.length < 2)
|
|
continue;
|
|
else if (arg == "-i")
|
|
{
|
|
if (i + 1 >= argc)
|
|
break;
|
|
i++;
|
|
arg = cast(string) args[i][0 .. strlen(args[i])];
|
|
extractStrings(include, arg);
|
|
}
|
|
else if (arg == "-e")
|
|
{
|
|
if (i + 1 >= argc)
|
|
break;
|
|
i++;
|
|
arg = cast(string) args[i][0 .. strlen(args[i])];
|
|
extractStrings(exclude, arg);
|
|
}
|
|
else if (arg.length < 10)
|
|
continue;
|
|
else if (arg[0 .. 10] == "--include=")
|
|
{
|
|
extractStrings(include, arg[10 .. $]);
|
|
}
|
|
else if (arg[0 .. 10] == "--exclude=")
|
|
{
|
|
extractStrings(exclude, arg[10 .. $]);
|
|
}
|
|
}
|
|
|
|
TestRunner!(tests.runner, tests.id_manager, tests.vector, tests.basic) runner;
|
|
|
|
runner.runTests(include[], exclude[]);
|
|
|
|
runner.writeFile();
|
|
|
|
runner.writeOutput();
|
|
|
|
version (notBetterC)
|
|
rt_term();
|
|
|
|
if (!runner.failed)
|
|
return 0;
|
|
else
|
|
return 1;
|
|
}
|