module test_auto; /* Read test files from the test/ directory, and run the tests here. If a test fails, execution does *not* stop (unless it's an uncaught exception); rather, we display a list of tests that failed. */ // TODO: // - handle errors import std.file; import std.path; import std.stdio; import std.string; import batchinterpreter; import listtools; import lqtypes; import stringtools; const string TEST_DIR = "tests"; string[] read_lines(string filename) { char[] data = cast(char[])std.file.read(filename); return std.string.split(data, "\n"); } class TestCase { string expr; string result; bool clear; /* do we reset the interpreter? */ string name; this(string expr, string result, bool clear, string name) { this.expr = expr; this.result = result; this.clear = clear; this.name = name; } } class Tester { private BatchInterpreter _bi; private TestCase[] _testcases = []; int success = 0; int failed = 0; int errors = 0; this() { _bi = new BatchInterpreter(); } void load() { string[] testfiles = get_test_files(); foreach (string filename; testfiles) { //writefln("Loading tests: %s...", filename); string full_filename = std.path.join(TEST_DIR, filename); TestCase[] testcases = get_testcases(full_filename); this._testcases ~= testcases; } } string[] get_test_files() { string[] filenames = std.file.listdir(TEST_DIR); string[] keep = []; foreach(string fn; filenames) { if (stringtools.starts_with(fn, ".")) continue; if (!stringtools.ends_with(fn, ".txt")) continue; keep ~= fn; } return keep; } TestCase[] get_testcases(string filename) { TestCase[] testcases = []; string[] lines = read_lines(filename); string[] current = []; int count = 0; foreach(string line; lines) { if (stringtools.starts_with(line, "=>")) { string result = line[2..line.length].strip(); bool clear = false; if (string_in_list("#!clear", current)) { clear = true; current = listtools.remove(current, "#!clear"); } string name = std.string.format("%s_%d", getName(getBaseName(filename)), ++count); string expr = std.string.join(current, "\n").strip(); TestCase t = new TestCase(expr, result, clear, name); testcases ~= t; current = []; } else current ~= line; } return testcases; } void test(TestCase testcase) { LqType result; if (testcase.clear) this._bi = new BatchInterpreter(); try { result = this._bi.eval(testcase.expr); } catch (Exception e) { writefln("%s: error occurred: %s", testcase.name, e.msg); this.errors++; return; } if (result.lq_repr() != testcase.result) { writefln("%s: expected %s; got %s", testcase.name, testcase.result, result.lq_repr()); this.failed++; } else this.success++; } void run() { foreach(TestCase testcase; this._testcases) { //writefln(">> %s", testcase.name); test(testcase); } writefln("unittest: auto: %d ok, %d failed, %d errors", this.success, this.failed, this.errors); } } unittest { writefln("unittest: auto"); //string[] filenames = get_test_files(); //writefln("We have these files: %s", std.string.join(filenames, "; ")); auto tester = new Tester(); tester.load(); tester.run(); }