module UnitTest {
use Reflection;
use TestError;
config const skipId: int = 0;
// This is a dummy test so that we get capture the function signature
private
proc testSignature(test: Test) throws { }
var tempFcf = testSignature;
type argType = tempFcf.type; //Type of First Class Test Functions
/*A test result class that can print formatted text results to a stream.*/
class TextTestResult {
var separator1 = "="* 70,
separator2 = "-"* 70;
proc startTest(test, indx) throws {
stdout.write(test: string,"(",indx: string,"): ");
}
proc addError(test, errMsg) throws {
stdout.writeln("ERROR");
PrintError(errMsg);
}
proc addFailure(test, errMsg) throws {
stdout.writeln("FAIL");
PrintError(errMsg);
}
proc addSuccess(test) throws {
stdout.writeln("OK");
}
proc addSkip(test, reason) throws {
stdout.writeln("SKIPPED");
PrintError(reason);
}
proc PrintError(err) throws {
stdout.writeln(this.separator1);
stdout.writeln(err);
stdout.writeln(this.separator2);
}
}
/*Runs the tests*/
proc runTest(tests: argType ...?n) throws {
// Assuming 1 global test suite for now
// Per-module or per-class is possible too
var testSuite = new TestSuite();
testSuite.addTests(tests);
var testResult = new TextTestResult();
// if skipId == 0 then
// stdout.writeln("Found "+testSuite.testCount+" "+printTest(testSuite.testCount));
for indx in (skipId+1)..testSuite.testCount {
var test = testSuite[indx];
try {
// Create a test object per test
var testObject = new Test();
//test is a FCF:
testResult.startTest(test: string, indx);
test(testObject);
testResult.addSuccess(test: string);
}
// A variety of catch statements will handle errors thrown
catch e: AssertionError {
testResult.addFailure(test:string, e:string);
// print info of the assertion error
}
catch e: TestSkipped {
testResult.addSkip(test:string, e:string);
// Print info on test skipped
}
catch e: TestDependencyNotMet {
// Pop test out of array and append to end
}
catch e {
testResult.addError(test:string, e:string);
}
}
// testResult.printErrors();
// stdout.writeln(testResult.separator2);
// testResult.PrintResult();
}
class TestSuite {
var testCount = 0;
var _tests: [1..0] argType;
proc addTest(test) lifetime this < test {
// var tempTest = new Test();
// param test_name = test: string;
// if !canResolve(test_name,tempTest) then
// compilerError(test + " is not callable");
this._tests.push_back(test);
this.testCount += 1;
}
proc addTests(tests) lifetime this < tests {
/*if isString(tests) then
compilerError("tests must be an iterable, not a string");*/
for test in tests do
this.addTest(test);
}
proc this(i : int) ref: argType {
return this._tests[i];
}
iter these() {
for i in this._tests do
yield i;
}
}
}
Example
use UnitTest;
proc test1(test: Test) throws {
test.assertTrue(false);
}
proc test2(test: Test) throws {
test.assertTrue(false);
}
proc test3(test: Test) throws {
test.skip("Skipping Test 3");
}
UnitTest.runTest(test1,test2,test3);
Current Output
Run 3 test
FAILED
failures= 2
skipped= 1
Some feedback
Are these init definitions necessary?
I think this can be just:
testDummy
would be more consistent camelCasing, although maybe we want to call this something more specific liketestSignature
, since it is used to capture the function signature we expect.In future steps, we could log the test result after the
test()
call, and access any fields oftestObject
, since it was passed by reference.I believe we agreed that this could be an array just as well, since arrays support
push_back
as well. In the near Chapel future, we'll have aList
type available, which we'll switch to (think python-list, rather than linked list).this.complete()
doesn't hurt to have, but note that it is implicitly added automatically. You only need it if you're calling methods from within thisinit
. Also, you can default-initialize vtestCount
above asvar testCount = 0;
, eliminating the need for aninit
definition altogether.This ref intent will allow the caller to modify the tests as they are being looped over, e.g.
Is this something we want for the array of FCFs?
Also, I tend to prefer the explicit
this.{field||method}
syntax as you do in your code. However, I noticed you omit it in a few areas, such as the iterator above,_tests
vs.this._tests
. It's up to you which style you choose, but I always encourage consistency.High-level stylistic:
TODO: document
use LinkedLists
too.