# # # add_file "unit-testsuite.lua" # content [d5d49df91e7937d51be8c84992065755c2bc43ea] # # patch "Makefile.am" # from [f75610a2ac86bf39076d267e18d3c3cfb0c8381f] # to [2326caf686d4416c7cb25211ca3ac4340d6943eb] # # patch "tester-tests/isolated-1/__driver__.lua" # from [f582bd30540a6f07049350746a4d1483e6b36344] # to [e617f484d767b12ba5b160f0e47b2ef381668eb9] # # patch "testlib.lua" # from [0b2490ce83794ce3a3404c57af4fd51aa9b766a3] # to [8c72b168db4237136c272bded45474b1ca43da38] # # patch "testsuite.lua" # from [08c414f640e805ce96691236e9c920f0aa0306c2] # to [9ca3325fc516d2e6235f800c444e1152562d8eae] # # patch "unit_tests.cc" # from [67d62056df1c88797eb0e2c2b2503a839e4532c8] # to [1fac50797304ac4b4addf8b5153fd9dbb02863da] # # patch "unit_tests.hh" # from [7d72d4e087ff7c58368b1debccec192505a64efd] # to [bdedb58135c3943f535e60efeb81f93c79e4e153] # ============================================================ --- unit-testsuite.lua d5d49df91e7937d51be8c84992065755c2bc43ea +++ unit-testsuite.lua d5d49df91e7937d51be8c84992065755c2bc43ea @@ -0,0 +1,71 @@ +-- This test suite is special; it synthesizes all its __driver__.lua +-- files on the fly. Each one runs the 'unit_tests' binary over just +-- one of the test cases it can run. + +testdir = initial_dir .. "/unit-tests" + +function prepare_to_enumerate_tests () + local unit_test_path = getpathof("unit_tester") + if unit_test_path == nil then return 1 end + + writefile_q("in", nil) + prepare_redirect("in", "out", "err") + local status = execute(unit_test_path) + local out = readfile_q("out") + local err = readfile_q("err") + + if status == 0 and err == "" and out ~= "" then + unlogged_remove(testdir) + unlogged_mkdir(testdir) + for tcase in string.gmatch(out, "[%w_:]+") do + local tdir = string.gsub(tcase, ':', '_') + unlogged_mkdir(testdir .. "/" .. tdir) + writefile_q(testdir .. "/" .. tdir .. "/__driver__.lua", + string.format("check({ %q, %q }, 0, true, true)\n", + unit_test_path, tcase)) + end + else + P(string.format("%s: exit %d\nstdout:\n", unit_test_path, status)) + P(out) + P("stderr:\n") + P(err) + + if status == 0 then status = 1 end + end + + unlogged_remove("in") + unlogged_remove("out") + unlogged_remove("err") + return status +end + +-- Cloned from testsuite.lua; just dumps information about the monotone +-- build into the master logfile. + +function prepare_to_run_tests () + local monotone_path = getpathof("mtn") + if monotone_path == nil then monotone_path = "mtn" end + + writefile_q("in", nil) + prepare_redirect("in", "out", "err") + + local status = execute(monotone_path, "version", "--full") + local out = readfile_q("out") + local err = readfile_q("err") + + if status == 0 and err == "" and out ~= "" then + logfile:write(out) + else + P(string.format("mtn version --full: exit %d\nstdout:\n", status)) + P(out) + P("stderr:\n") + P(err) + + if status == 0 then status = 1 end + end + + unlogged_remove("in") + unlogged_remove("out") + unlogged_remove("err") + return status +end ============================================================ --- Makefile.am f75610a2ac86bf39076d267e18d3c3cfb0c8381f +++ Makefile.am 2326caf686d4416c7cb25211ca3ac4340d6943eb @@ -296,8 +296,8 @@ usher_SOURCES = contrib/usher.cc mtn_SOURCES = $(MOST_SOURCES) monotone.cc nodist_mtn_SOURCES = std_hooks.c schema.c usher_SOURCES = contrib/usher.cc -unit_tests_SOURCES = $(MOST_SOURCES) unit_tests.cc crypto_tests.cc -nodist_unit_tests_SOURCES = std_hooks.c test_hooks.c schema.c +unit_tester_SOURCES = $(MOST_SOURCES) unit_tests.cc crypto_tests.cc +nodist_unit_tester_SOURCES = std_hooks.c test_hooks.c schema.c tester_SOURCES = $(SANITY_CORE_SOURCES) $(LUAEXT_SOURCES) tester.cc nodist_tester_SOURCES = testlib.c @@ -359,10 +359,10 @@ mtn_LDADD = lib3rdparty.a $(BOOSTLIBS) l mtn_CXXFLAGS = $(AM_CXXFLAGS) $(PCH_FLAGS) $(MTN_CXXFLAGS) mtn_LDADD = lib3rdparty.a $(BOOSTLIBS) libplatform.a $(LIBICONV) $(LIBINTL) -unit_tests_LDFLAGS = -unit_tests_CPPFLAGS = -DBUILD_UNIT_TESTS -I$(top_srcdir)/lua -unit_tests_CXXFLAGS = $(AM_CXXFLAGS) $(PCH_FLAGS) -unit_tests_LDADD = lib3rdparty.a $(BOOSTLIBS) \ +unit_tester_LDFLAGS = +unit_tester_CPPFLAGS = -DBUILD_UNIT_TESTS -I$(top_srcdir)/lua +unit_tester_CXXFLAGS = $(AM_CXXFLAGS) $(PCH_FLAGS) +unit_tester_LDADD = lib3rdparty.a $(BOOSTLIBS) \ libplatform.a $(LIBICONV) $(LIBINTL) tester_LDFLAGS = @@ -374,7 +374,7 @@ if WIN32_PLATFORM libplatform_a_SOURCES += $(WIN32_PLATFORM_SOURCES) mtn_SOURCES += win32/main.cc mtn_LDADD += -lshfolder -lws2_32 -lintl -liconv -liphlpapi - unit_tests_LDADD += -lshfolder -lws2_32 -lintl -liconv -liphlpapi + unit_tester_LDADD += -lshfolder -lws2_32 -lintl -liconv -liphlpapi lib3rdparty_a_CPPFLAGS += -DWIN32 -DBOTAN_EXT_ENTROPY_SRC_CAPI -DBOTAN_EXT_ENTROPY_SRC_WIN32 lib3rdparty_a_SOURCES += botan/es_capi.cpp botan/es_win32.cpp else @@ -501,16 +501,17 @@ monotone-$(PACKAGE_VERSION).dmg: monoton # testsuite stuff (could this possibly be more ugly?) -check_PROGRAMS = unit_tests tester -TESTS = unit_tests run_tester_tests run_lua_tests +check_PROGRAMS = unit_tester tester +TESTS = run_unit_tests run_tester_tests run_lua_tests TESTS_ENVIRONMENT=AUTOTEST_PATH="." mostlyclean-local: - rm -rf tester_dir package_full_revision.txt + rm -rf tester_dir unit-tests package_full_revision.txt -DISTCLEANFILES = mt-stdint.h xgettext.opts pch-build.hh.gch.dep run_tester_tests run_lua_tests unit_tests.log +DISTCLEANFILES = mt-stdint.h xgettext.opts pch-build.hh.gch.dep \ + run_tester_tests run_lua_tests run_unit_tests # distcheck stuff @@ -545,6 +546,12 @@ run_tester_tests: Makefile echo 'exec $(top_builddir)/tester $(srcdir)/tester-testsuite.lua "$$@"' >> $@ ; \ chmod 755 $@ +run_unit_tests: Makefile + echo '#!/bin/sh' > $@ ; \ + echo 'PATH=$(top_builddir):$$PATH' >> $@ ; \ + echo 'exec $(top_builddir)/tester $(srcdir)/unit-testsuite.lua "$$@"' >> $@ ; \ + chmod 755 $@ + # we generate some source files to copy data into the executable # note that the only things that should go in BUILT_SOURCES are things # that need to be generated early on 'make all'; this is _not_ true of ============================================================ --- tester-tests/isolated-1/__driver__.lua f582bd30540a6f07049350746a4d1483e6b36344 +++ tester-tests/isolated-1/__driver__.lua e617f484d767b12ba5b160f0e47b2ef381668eb9 @@ -1,8 +1,8 @@ old_L = L -- functions can be redefined foo = "bar" old_L = L -L = function () old_mkdir("xxx") end +L = function () unlogged_mkdir("xxx") end mkdir("bar") -- calls L() L = old_L check(exists("xxx")) ============================================================ --- testlib.lua 0b2490ce83794ce3a3404c57af4fd51aa9b766a3 +++ testlib.lua 8c72b168db4237136c272bded45474b1ca43da38 @@ -20,6 +20,9 @@ files = {stdout = nil, stdin = nil, stde -- for this (at least on Windows). files = {stdout = nil, stdin = nil, stderr = nil} +-- for convenience, this is the first word of what get_ostype() returns. +ostype = string.sub(get_ostype(), 1, string.find(get_ostype(), " ")-1) + -- table of per-test values test = {} -- misc per-test values @@ -104,22 +107,22 @@ do -- replace some builtings with logged end do -- replace some builtings with logged versions - old_mtime = mtime + unlogged_mtime = mtime mtime = function(name) - local x = old_mtime(name) + local x = unlogged_mtime(name) L(locheader(), "mtime(", name, ") = ", tostring(x), "\n") return x end - old_mkdir = mkdir + unlogged_mkdir = mkdir mkdir = function(name) L(locheader(), "mkdir ", name, "\n") - old_mkdir(name) + unlogged_mkdir(name) end - old_existsonpath = existsonpath + unlogged_existsonpath = existsonpath existsonpath = function(name) - local r = (old_existsonpath(name) == 0) + local r = (unlogged_existsonpath(name) == 0) local what if r then what = "exists" @@ -280,6 +283,45 @@ end return string.gsub(str, "^%s*(.-)%s*$", "%1") end +function getpathof(exe, ext) + local function gotit(now) + if test.log == nil then + logfile:write(exe, " found at ", now, "\n") + else + test.log:write(exe, " found at ", now, "\n") + end + return now + end + local path = os.getenv("PATH") + local char + if ostype == "Windows" then + char = ';' + else + char = ':' + end + if ostype == "Windows" then + if ext == nil then ext = ".exe" end + else + if ext == nil then ext = "" end + end + local now = initial_dir.."/"..exe..ext + if exists(now) then return gotit(now) end + for x in string.gmatch(path, "[^"..char.."]*"..char) do + local dir = string.sub(x, 0, -2) + if string.find(dir, "[\\/]$") then + dir = string.sub(dir, 0, -2) + end + local now = dir.."/"..exe..ext + if exists(now) then return gotit(now) end + end + if test.log == nil then + logfile:write("Cannot find ", exe, "\n") + else + test.log:write("Cannot find ", exe, "\n") + end + return nil +end + function prepare_redirect(fin, fout, ferr) local cwd = chdir(".").."/" redir = {fin = cwd..fin, fout = cwd..fout, ferr = cwd..ferr} ============================================================ --- testsuite.lua 08c414f640e805ce96691236e9c920f0aa0306c2 +++ testsuite.lua 9ca3325fc516d2e6235f800c444e1152562d8eae @@ -1,45 +1,5 @@ #!./tester --- maybe this should go in tester.lua instead? -function getpathof(exe, ext) - local function gotit(now) - if test.log == nil then - logfile:write(exe, " found at ", now, "\n") - else - test.log:write(exe, " found at ", now, "\n") - end - return now - end - local path = os.getenv("PATH") - local char - if ostype == "Windows" then - char = ';' - else - char = ':' - end - if ostype == "Windows" then - if ext == nil then ext = ".exe" end - else - if ext == nil then ext = "" end - end - local now = initial_dir.."/"..exe..ext - if exists(now) then return gotit(now) end - for x in string.gmatch(path, "[^"..char.."]*"..char) do - local dir = string.sub(x, 0, -2) - if string.find(dir, "[\\/]$") then - dir = string.sub(dir, 0, -2) - end - local now = dir.."/"..exe..ext - if exists(now) then return gotit(now) end - end - if test.log == nil then - logfile:write("Cannot find ", exe, "\n") - else - test.log:write("Cannot find ", exe, "\n") - end - return nil -end - function safe_mtn(...) return {monotone_path, "--norc", "--root=" .. test.root, "--confdir="..test.root, unpack(arg)} @@ -330,8 +290,6 @@ function prepare_to_run_tests () -- test error handling behavior). require_not_root() - ostype = string.sub(get_ostype(), 1, string.find(get_ostype(), " ")-1) - monotone_path = getpathof("mtn") if monotone_path == nil then monotone_path = "mtn" end set_env("mtn", monotone_path) @@ -350,11 +308,12 @@ function prepare_to_run_tests () P(out) P("stderr:\n") P(err) - return 1 + + if status == 0 then status = 1 end end unlogged_remove("in") unlogged_remove("out") unlogged_remove("err") - return 0 + return status end ============================================================ --- unit_tests.cc 67d62056df1c88797eb0e2c2b2503a839e4532c8 +++ unit_tests.cc 1fac50797304ac4b4addf8b5153fd9dbb02863da @@ -49,8 +49,9 @@ unit_test::unit_test_case::unit_test_cas unit_test::unit_test_case::unit_test_case(char const * group, char const * name, - void (*func)()) - : group(group), name(name), func(func) + void (*func)(), + bool fis) + : group(group), name(name), func(func), failure_is_success(fis) { unit_tests()[group][name] = *this; } @@ -58,14 +59,8 @@ unit_test::unit_test_case::unit_test_cas unit_test::unit_test_case::unit_test_case() {} -// Testsuite state. -static bool any_test_failed = false; -static int number_of_failed_tests = 0; -static int number_of_succeeded_tests = 0; -static bool log_to_stderr = false; - // Test state. -static bool this_test_failed; +static bool this_test_failed = false; namespace { struct require_failed {}; } @@ -154,126 +149,46 @@ void unit_test::do_checkpoint(char const log_state(file, line, "CHECKPOINT", message); } -static void run_test(test_t test) +static int run_test(test_t test) { - this_test_failed = false; - - L(FL("----------------------------------------\n" - "Beginning test %s:%s") % test.group % test.name); - - if (!log_to_stderr) - { - string groupname = string(test.group) + ':' + test.name; - cerr << " " << std::left << std::setw(46) << groupname; - if (groupname.size() >= 46) - cerr << " "; - // lack of carriage return is intentional - } - - try - { - test.func(); - } - catch(require_failed &) - { - // no action required - } - catch(std::exception & e) - { - log_exception(e); - this_test_failed = true; - } - catch(...) - { - log_exception(); - this_test_failed = true; - } - - if (this_test_failed) - { - ++number_of_failed_tests; - L(FL("Test %s:%s failed.\n") % test.group % test.name); - if (!log_to_stderr) - cerr << "FAIL\n"; - } - else - { - ++number_of_succeeded_tests; - L(FL("Test %s:%s succeeded.\n") % test.group % test.name); - if (!log_to_stderr) - cerr << "ok\n"; - } - - if (this_test_failed) - any_test_failed = true; } int main(int argc, char * argv[]) { bool help(false); - bool list_groups(false); - bool list_tests(false); - bool debug(false); - string log; - vector tests; + string test_to_run; try { option::concrete_option_set os; os("help,h", "display help message", option::setter(help)) - ("list-groups,l", "list all test groups", option::setter(list_groups)) - ("list-tests,L", "list all test cases", option::setter(list_tests)) - ("debug", "write verbose debug log to stderr", option::setter(debug)) - ("log", "write verbose debug log to this file" - " (default is unit_tests.log)", option::setter(log)) - ("--", "", option::setter(tests)); + ("--", "", option::setter(test_to_run)); os.from_command_line(argc, argv); if (help) { - cout << (FL("Usage: %s [options] [tests]\nOptions") % argv[0]) - << os.get_usage_str() << '\n'; - exit(0); + cout << (FL("Usage: %s [-h|--help] [test]\n" + " With no arguments, lists all test cases.\n" + " With the name of a test case, runs that test.\n" + " -h or --help prints this message.\n") % argv[0]); + return 0; } } catch (option::option_error const & e) { cerr << argv[0] << ": " << e.what() << '\n'; - exit(2); + return 2; } - if (list_groups && list_tests) + if (test_to_run == "") { - cerr << argv[0] - << ": only one of --list-groups and --list-tests at a time\n"; - exit(2); - } - - if (list_groups) - { for (group_list_t::const_iterator i = unit_tests().begin(); i != unit_tests().end(); i++) - { - if (i->first != "") - cout << i->first << '\n'; - } - return 0; - } + for (test_list_t::const_iterator j = i->second.begin(); + j != i->second.end(); ++j) + cout << i->first << ":" << j->first << "\n"; - if (list_tests) - { - for (group_list_t::const_iterator i = unit_tests().begin(); - i != unit_tests().end(); i++) - { - if (i->first == "") - continue; - for (test_list_t::const_iterator j = i->second.begin(); - j != i->second.end(); ++j) - { - cout << i->first << ":" << j->first << "\n"; - } - } return 0; } @@ -284,143 +199,74 @@ int main(int argc, char * argv[]) global_sanity.initialize(argc, argv, "C"); // we didn't call setlocale Botan::LibraryInitializer::initialize(); - if (!debug) - { - // We would _like_ to use ui.redirect_log_to() but it takes a - // system_path and we're not set up to use that here. - char const * logname; - if (log.empty()) - logname = "unit_tests.log"; - else - logname = log.c_str(); + // Make clog and cout use the same streambuf as cerr; this ensures + // that all messages will appear in the order written, no matter what + // stream each one is written to. + clog.rdbuf(cerr.rdbuf()); + cout.rdbuf(cerr.rdbuf()); - std::filebuf * logbuf = new std::filebuf; - if (!logbuf->open(logname, std::ios_base::out|std::ios_base::trunc)) - { - char const * syserr = std::strerror(errno); - cerr << argv[0] << ": failed to open " << logname - << ": " << syserr << '\n'; - exit(1); - } - clog.rdbuf(logbuf); - // Nobody should be writing to cout, but just in case, send it to - // the log. - cout.rdbuf(logbuf); - } - else - { - if (!log.empty()) - { - cerr << argv[0] - << ": only one of --debug and --log at a time\n"; - exit(2); - } + global_sanity.set_debug(); - // Make clog and cout use the same streambuf as cerr; this ensures - // that all messages will appear in the order written, no matter what - // stream each one is written to. - clog.rdbuf(cerr.rdbuf()); - cout.rdbuf(cerr.rdbuf()); + string::size_type sep = test_to_run.find(":"); - // Suppress double progress messages. - log_to_stderr = true; + if (sep == string::npos) // it's a group name + { + cerr << argv[0] << ": must specify a test, not a group, to run\n"; + return 2; } - global_sanity.set_debug(); + string group, test; + group = test_to_run.substr(0, sep); + test = test_to_run.substr(sep+1, string::npos); + + group_list_t::const_iterator g = unit_tests().find(group); - if (tests.size() == 0) // run all tests + if (g == unit_tests().end()) { - if (!log_to_stderr) - cerr << "Running unit tests...\n"; - - for (group_list_t::const_iterator i = unit_tests().begin(); - i != unit_tests().end(); i++) - { - if (i->first == "") - continue; - for (test_list_t::const_iterator j = i->second.begin(); - j != i->second.end(); ++j) - { - run_test(j->second); - } - } + cerr << argv[0] << ": unrecognized test group: " + << group << '\n'; + return 2; } - else + + test_list_t::const_iterator t = g->second.find(test); + if (t == g->second.end()) { - bool unrecognized = false; + cerr << argv[0] << ": unrecognized test: " + << group << ':' << test << '\n'; + return 2; + } - vector to_run; + L(FL("Beginning test %s:%s") % group % test); - for(vector::const_iterator i = tests.begin(); - i != tests.end(); - i++) - { - string group, test; - string::size_type sep = (*i).find(":"); + try + { + t->second.func(); + } + catch(require_failed &) + { + // no action required + } + catch(std::exception & e) + { + log_exception(e); + this_test_failed = true; + } + catch(...) + { + log_exception(); + this_test_failed = true; + } - if (sep >= (*i).length()) // it's a group name - group = *i; - else - { - group = (*i).substr(0, sep); - test = (*i).substr(sep+1, string::npos); - } - - group_list_t::const_iterator g = unit_tests().find(group); - - if (g == unit_tests().end()) - { - unrecognized = true; - cerr << argv[0] << ": unrecognized test group: " - << group << '\n'; - continue; - } - - if (test == "") // entire group - { - for (test_list_t::const_iterator t = g->second.begin(); - t != g->second.end(); ++t) - { - to_run.push_back(t->second); - } - } - else - { - test_list_t::const_iterator t = g->second.find(test); - if (t == g->second.end()) - { - unrecognized = true; - cerr << argv[0] << ": unrecognized test: " - << group << ':' << test << '\n'; - continue; - } - to_run.push_back(t->second); - } - } - - if (unrecognized) - { - return 1; - } - else - { - if (!log_to_stderr) - cerr << "Running unit tests...\n"; - for (vector::const_iterator i = to_run.begin(); - i != to_run.end(); ++i) - { - run_test(*i); - } - } + if (this_test_failed && !t->second.failure_is_success) + { + L(FL("Test %s:%s failed.\n") % group % test); + return 1; } - - if (!log_to_stderr) - cerr << "\nOf " << (number_of_failed_tests + number_of_succeeded_tests) - << " tests run:\n\t" - << number_of_succeeded_tests << " succeeded\n\t" - << number_of_failed_tests << " failed\n"; - - return any_test_failed?1:0; + else + { + L(FL("Test %s:%s succeeded.\n") % group % test); + return 0; + } } // Stub for options.cc's sake. @@ -429,47 +275,56 @@ localize_monotone() { } +// These are tests of the unit testing mechanism itself. They would all +// fail, but we make use of a special mechanism to convert that failure +// into a success. Since we don't want that mechanism used elsewhere, +// the necessary definition macro is defined here and not in unit_test.hh. -// Obviously, these tests are not run by default. -// They are also not listed by --list-groups or --list-tests . -// Use "unit_tests :" to run them; they should all fail. +#define NEGATIVE_UNIT_TEST(GROUP, TEST) \ + namespace unit_test { \ + static void t_##GROUP##_##TEST(); \ + static unit_test_case r_##GROUP##_##TEST \ + (#GROUP, #TEST, t_##GROUP##_##TEST, true); \ + } \ + static void unit_test::t_##GROUP##_##TEST() + #include -UNIT_TEST(, fail_check) +NEGATIVE_UNIT_TEST(_unit_tester, fail_check) { UNIT_TEST_CHECKPOINT("checkpoint"); UNIT_TEST_CHECK(false); UNIT_TEST_CHECK(false); } -UNIT_TEST(, fail_require) +NEGATIVE_UNIT_TEST(_unit_tester, fail_require) { UNIT_TEST_CHECKPOINT("checkpoint"); UNIT_TEST_REQUIRE(false); UNIT_TEST_CHECK(false); } -UNIT_TEST(, fail_throw) +NEGATIVE_UNIT_TEST(_unit_tester, fail_throw) { UNIT_TEST_CHECK_THROW(string().size(), int); } -UNIT_TEST(, fail_nothrow) +NEGATIVE_UNIT_TEST(_unit_tester, fail_nothrow) { UNIT_TEST_CHECK_NOT_THROW(throw int(), int); } -UNIT_TEST(, uncaught) +NEGATIVE_UNIT_TEST(_unit_tester, uncaught) { throw int(); } -UNIT_TEST(, uncaught_std) +NEGATIVE_UNIT_TEST(_unit_tester, uncaught_std) { throw std::bad_exception(); } -UNIT_TEST(, uncaught_std_what) +NEGATIVE_UNIT_TEST(_unit_tester, uncaught_std_what) { throw std::runtime_error("There is no spoon."); } ============================================================ --- unit_tests.hh 7d72d4e087ff7c58368b1debccec192505a64efd +++ unit_tests.hh bdedb58135c3943f535e60efeb81f93c79e4e153 @@ -75,9 +75,11 @@ namespace unit_test { char const *group; char const *name; void (*func)(); + bool failure_is_success; unit_test_case(char const * group, char const * name, - void (*func)()); + void (*func)(), + bool fis); unit_test_case(); }; } @@ -86,12 +88,12 @@ namespace unit_test { // names of symbols in the code being tested, despite their being in a // separate namespace, so that references _from_ the test functions _to_ the // code under test resolve correctly. -#define UNIT_TEST(GROUP, TEST) \ - namespace unit_test { \ - static void t_##GROUP##_##TEST(); \ - static unit_test_case r_##GROUP##_##TEST \ - (#GROUP, #TEST, t_##GROUP##_##TEST); \ - } \ +#define UNIT_TEST(GROUP, TEST) \ + namespace unit_test { \ + static void t_##GROUP##_##TEST(); \ + static unit_test_case r_##GROUP##_##TEST \ + (#GROUP, #TEST, t_##GROUP##_##TEST, false); \ + } \ static void unit_test::t_##GROUP##_##TEST() // Local Variables: