// 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("\n"); } void writeResulEnd() { writeToFile("\n"); } void writeResult(ref TestSuite suite, ref Test result) { writeToFile(" \n"); } else { writeToFile(">\n"); writeToFile(" "); 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("\n"); writeToFile(" \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("\n\n"); write("\n"); foreach(ref TestSuite suite; suites) { write("\t\n"); foreach(ref Test test; suite.tests) { write("\t\t\n"); if(test.msg) { write("\t\t\t\n"); write("\t\t\t\tAssert! File: "); write(test.file[0 .. $ - 1]); write(":"); write(test.fileLine); write(" Message: "); write(test.msg[0 .. $ - 1]); write("\n"); write("\t\t\t\n"); } write("\t\t\n"); } write("\t\n"); } write(""); } 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; }