bubel-ecs/tests/runner.d
2020-04-05 21:16:11 +02:00

305 lines
7 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;
unittest
{
assert(0,"This will fail!");
}
enum int ASSERTED = 123;
enum string OUT_FILE = "test_report.xml";
static jmp_buf gEnvBuffer;
static AssertInfo gAssertInfo;
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 fileLine;
string name;
//string classname;
int time;
bool passed;
//bool executed;
}
struct TestSuite
{
string name;
uint passed = 0;
uint failed = 0;
Vector!Test tests;
}
void writeToFile(string data)
{
auto file = fopen(OUT_FILE, "a");
fwrite(data.ptr, 1, data.length, file);
fclose(file);
}
void writeToFile(int num)
{
auto file = fopen(OUT_FILE, "a");
fprintf(file, "%d", num);
fclose(file);
}
void writeResulStart(int num)
{
// Clear file
auto file = fopen(OUT_FILE, "w");
fclose(file);
writeToFile("<testsuite tests=\"");
writeToFile(num);
writeToFile("\">\n");
}
void writeResulEnd()
{
writeToFile("</testsuite>\n");
}
void writeResult(ref TestSuite suite, ref Test result)
{
writeToFile(" <testcase classname=\"");
writeToFile(suite.name);
writeToFile("\" name=\"");
writeToFile(result.name);
writeToFile("\" time=\"");
writeToFile(result.time);
writeToFile("\" ");
//writeToFile(" executed=\"");
//writeToFile(cast(int) result.executed);
writeToFile("\" ");
if (result.passed)
{
writeToFile("/>\n");
}
else
{
writeToFile(">\n");
writeToFile(" <failure type=\"Fail\">");
if (result.file.length != 0)
{
writeToFile("Assert! File: ");
writeToFile(result.file[0 .. $ - 1]);
writeToFile(":");
writeToFile(result.fileLine);
writeToFile(" Message: ");
writeToFile(result.msg[0 .. $ - 1]);
}
else
{
writeToFile("No Message");
}
writeToFile("</failure>\n");
writeToFile(" </testcase>\n");
}
}
void writeTests(TestSuite[] suites)
{
writeResulStart(cast(int) suites.length);
foreach (ref TestSuite suite; suites)
{
foreach (ref Test test; suite.tests)
{
writeResult(suite, test);
}
}
writeResulEnd();
}
string copyString(const char* str)
{
auto length = strlen(str);
char[] arr = cast(char[]) str[0 .. length + 1];
return cast(string) Mallocator.makeArray(arr);
}
struct TestRunner(Args...)
{
TestSuite[Args.length] suites;
uint passed = 0;
uint failed = 0;
SimpleVector junit;
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]);
}
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("\">\n");
foreach(ref Test test; suite.tests)
{
write("\t\t<testcase name=\"");
write(test.name);
write("\" classname=\"");
write(suite.name);
write("\">\n");
if(test.msg)
{
write("\t\t\t<failure type=\"Fail\" message=\"");
write(test.msg[0 .. $ - 1]);
write("\">");
write("Assert! File: ");
write(test.file[0 .. $ - 1]);
write(":");
write(test.fileLine);
write(" Message: ");
write(test.msg[0 .. $ - 1]);
//write("\n");
write("</failure>\n");
}
write("\t\t</testcase>\n");
}
write("\t</testsuite>\n");
}
write("</testsuites>");
}
void runTests()
{
foreach(i, module_; Args)
{
TestSuite* suite = &suites[i];
suite.name = module_.stringof;
foreach (index, unittest_; __traits(getUnitTests, module_))
{
enum attributes = __traits(getAttributes, unittest_);
Test test;
static if (attributes.length == 0)test.name = "None";
else test.name = attributes[0];
// Save calling environment for longjmp
int jmpRet = setjmp(gEnvBuffer);
if (jmpRet == ASSERTED)
{
passed = false;
test.passed = false;
test.file = copyString(gAssertInfo.file);
test.fileLine = gAssertInfo.line;
test.msg = copyString(gAssertInfo.msg);
}
else
{
unittest_();
test.passed = true;
}
if(test.passed)suite.passed++;
else suite.failed++;
suite.tests ~= test;
}
passed += suite.passed;
failed += suite.failed;
}
}
void writeFile()
{
if(junit.length == 0)generateJUnit();
auto file = fopen(OUT_FILE, "w");
fwrite(junit.data.ptr,junit.length,1,file);
fclose(file);
//writeTests(suites);
}
void writeOutput()
{
import core.stdc.stdio;
foreach(ref TestSuite suite; suites)
{
printf("Suite: \"%s\" Passed: %u/%u\n", suite.name.ptr, suite.passed, suite.passed + suite.failed);
foreach(ref Test test;suite.tests)
{
if(!test.passed)
{
printf("\tTest: \"%s\" Failed! Line: %u Message: %s\n",test.name.ptr, test.fileLine, test.msg.ptr);
}
}
}
printf("Passed: %u/%u tests.\n", passed, passed+failed);
}
}
int main()
{
import tests.vector;
import tests.id_manager;
import tests.basic;
TestRunner!(tests.runner, tests.id_manager, tests.vector, tests.basic) runner;
runner.runTests();
runner.writeFile();
runner.writeOutput();
if(!runner.failed)return 0;
else return 1;
}