Convert unit test framework to C

I commented out most of the file and directory modification code while I
was at it.  I'll re-add these as I convert more files to C.

Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
This commit is contained in:
Anna Schumaker 2015-08-19 17:15:46 -04:00
parent 34a15d58c4
commit 63769c9c0b
7 changed files with 398 additions and 279 deletions

View File

@ -15,6 +15,7 @@ class OEnvironment(Environment):
Environment.__init__(self, CCFLAGS = CCFLAGS)
self.Append(CPPPATH = os.path.abspath("include"))
self.Append(CXXCOMSTR = "C++ $TARGET")
self.Append(CCCOMSTR = "CC $TARGET")
self.Append(LINKCOMSTR = "Linking $TARGET")
self.Debug = CONFIG_DEBUG
self.Version = CONFIG_VERSION

View File

@ -4,46 +4,93 @@
#ifndef OCARINA_TESTS_TEST_H
#define OCARINA_TESTS_TEST_H
#include <string>
#include <fcntl.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
namespace test
{
extern unsigned int failed;
void run(const std::string &, void (*)());
#ifndef __cplusplus
static inline gchar *stos(const char *s) { return g_strdup(s); }
static inline gchar *utos(unsigned int u) { return g_strdup_printf("%u", u); }
static inline gchar *itos(int i) { return g_strdup_printf("%i", i); }
static inline gchar *btos(bool b) { return g_strdup(b ? "true" : "false"); }
static inline gchar *ptos(void *p) { return g_strdup_printf("%p", p); }
void equal(const int, const int, unsigned int);
void equal(const std::string &, const std::string &, unsigned int);
void equal(const void *, const void *, unsigned int);
void not_equal(const int, const int, unsigned int);
void not_equal(const std::string &, const std::string &, unsigned int);
void not_equal(const void *, const void *, unsigned int);
#define tostring(x) (_Generic((x), \
char *: stos, \
const char *: stos, \
bool: btos, \
unsigned int: utos, \
int: itos, \
void *:ptos \
) (x))
#endif /* __cplusplus */
void for_each(unsigned int, unsigned int, unsigned int,
unsigned int (*)(unsigned int), unsigned int);
std::string data_dir();
std::string data_file(const std::string &name);
bool data_dir_exists();
bool data_file_exists(const std::string &name);
void rm_data_dir();
void reset_data_dir();
void cp_data_dir();
void gen_library();
void rm_library_dirs();
}
struct UnitTest {
const char *t_name;
void (*t_func)();
};
#define test_equal(lhs, rhs) \
test :: equal(lhs, rhs, __LINE__)
extern struct UnitTest unit_tests[];
extern unsigned int unit_tests_size;;
#define test_not_equal(lhs, rhs) \
test :: not_equal(lhs, rhs, __LINE__)
#define test_for_each(init, max, inc, func) \
test :: for_each(init, max, inc, func, __LINE__)
#define UNIT_TEST(name, func) \
[__COUNTER__] = { \
.t_name = name, \
.t_func = func, \
}
#define LOOP_PASSED 0
#define LOOP_FAILED __LINE__
#define DECLARE_UNIT_TESTS(...) \
struct UnitTest unit_tests[] = {\
__VA_ARGS__ \
}; \
unsigned int unit_tests_size = __COUNTER__
extern unsigned int tests_failed;
void test_strings_equal(gchar *, gchar *, unsigned int);
void test_strings_not_equal(gchar *, gchar *, unsigned int);
void loop_strings_equal(gchar *, gchar *, unsigned int, unsigned int);
void loop_strings_not_equal(gchar *, gchar *, unsigned int, unsigned int);
#define test_equal(lhs, rhs) \
test_strings_equal(tostring(lhs), tostring(rhs), __LINE__)
#define test_not_equal(lhs, rhs) \
test_strings_not_equal(tostring(lhs), tostring(rhs), __LINE__)
#define test_loop_equal(lhs, rhs, i) \
if (1) { \
loop_strings_equal(tostring(lhs), tostring(rhs), i, __LINE__); \
if (tests_failed > 0) \
break; \
}
#define test_loop_not_equal(lhs, rhs, i) \
if (1) { \
loop_strings_not_equal(tostring(lhs), tostring(rhs), i, __LINE__); \
if (tests_failed > 0) \
break; \
}
#define test_loop_passed() \
if (tests_failed == 0) \
test_equal(tests_failed, 0)
void test_cp_data_dir();
void test_rm_data_dir();
gchar *test_data_file(const gchar *);
bool test_data_file_exists(const gchar *);
void test_generate_library();
void test_rm_library_dirs();
#endif /* OCARINA_TESTS_TEST_H */

View File

@ -3,75 +3,34 @@ import sys, os
Import("env")
tests = []
check_sanity = False
for arg in sys.argv[1:]:
if arg.find("tests") == 0:
tests += [ arg ]
if arg in set(["tests", "tests/", "tests/sanity"]):
check_sanity = True
if len(tests) == 0:
Return()
env.Append(CCFLAGS = "-DCONFIG_TEST")
env.Append(CCFLAGS = "-DCONFIG_TESTING")
env.UsePackage("glib-2.0")
test_o = env.Object("test", "test.c")
class UnitTest:
def __init__(self, test, sources):
name = os.path.basename(test)
make = env.Program(name, sources)
run = Command("%s.fake" % name, [], "tests/%s" % test)
Depends(run, make)
Alias("tests/%s" % test, run)
res = UnitTest("sanity", [ "sanity.cpp", "test.cpp" ])
Return("res")
def UnitTest(test, sources):
name = os.path.basename(test)
make = env.Program(name, sources + [ test_o ])
run = Command("%s.fake" % name, [], "tests/%s" % test)
Depends(run, make)
Alias("tests/%s" % test, run)
return run
Export("UnitTest")
res = []
def all_tests_enabled(dir):
for arg in sys.argv[1:]:
arg = os.path.normpath(arg)
name = os.path.basename(arg)
if (arg.find("tests") == 0) and (name == "tests"):
return True
if (arg.find("tests/%s" % dir) == 0) and (name == dir):
return True
return False
def add_test(test, dir):
global res
if (all_tests_enabled(dir) == True) and (len(res) > 0):
Depends(test, res[-1])
res += [ test ]
def get_test_obj(name, dir):
src = "../../%s/%s.cpp" % (dir, name)
if os.path.exists(src):
return test_env.Object("%s.cpp-%s" % (name, dir), src)
return None
def generic_test(name, dir, objs, extra):
global test_obj;
obj = get_test_obj(name, dir)
test_objs = extra
if obj != None:
objs += [ obj ]
test_objs = extra + objs
exe = test_env.Program(name, [ "%s.cpp" % name, test_lib ] + test_objs)
test = Command("%s.fake" % name, [], "tests/%s/%s" % (dir, name))
Alias("tests/%s/%s" % (dir, name), test)
Depends(test, exe)
add_test(test, dir)
return objs
Export("get_test_obj", "generic_test")
#SConscript("core/Sconscript")
#res = SConscript("core/Sconscript")
if check_sanity == True:
res = [ UnitTest("sanity", [ "sanity.c" ]) ] + res
for t in res[1:]:
Depends(t, res[0])
Return("res")

105
tests/sanity.c Normal file
View File

@ -0,0 +1,105 @@
/*
* Copyright 2015 (c) Anna Schumaker.
*/
#include <tests/test.h>
#include <stdlib.h>
#ifdef CONFIG_TESTING
static const char *str = "Enabled";
#else /* CONFIG_TESTING */
static const char *str = "Disabled";
#endif /* CONFIG_TESTING */
static unsigned int counter = 0;
static unsigned int inc_counter() { return counter++; }
static void test_passing_tests()
{
unsigned int i;
test_equal(0, 0);
test_equal("2", "2");
test_equal((void *)4, (void *)4);
test_equal(str, "Enabled");
test_equal(inc_counter(), 0);
test_equal((bool)true, (bool)true);
for (i = 0; i < 10; i++)
test_loop_equal(i, i, i);
test_loop_passed();
test_not_equal(0, 1);
test_not_equal("2", "3");
test_not_equal((void *)4, (void *)5);
test_not_equal(str, "Disabled");
test_not_equal(inc_counter(), 2);
test_not_equal((bool)true, (bool)false);
for (i = 0; i < 10; i++)
test_loop_not_equal(i, i + 1, i);
test_loop_passed();
}
static void test_failing_tests()
{
unsigned int i;
test_equal(0, 1);
test_equal("2", "3");
test_equal((void *)4, (void *)5);
test_equal(str, "Disabled");
test_equal(inc_counter(), 3);
test_equal((bool)true, (bool)false);
for (i = 0; i < 10; i++)
test_loop_equal(i, i + 1, i);
test_loop_passed();
test_not_equal(0, 0);
test_not_equal("2", "2");
test_not_equal((void *)4, (void *)4);
test_not_equal(str, "Enabled");
test_not_equal(inc_counter(), 3);
test_not_equal((bool)true, (bool)true);
for (i = 0; i < 10; i++)
test_loop_not_equal(i, i, i);
test_loop_passed();
tests_failed = 0;
}
static void test_utilities()
{
gchar *dir = test_data_file(NULL);
gchar *file = test_data_file("sanity");
const gchar *data = g_get_user_data_dir();
gchar *ddata = g_strjoin("/", data, "ocarina-test", NULL);
gchar *fdata = g_strjoin("/", data, "ocarina-test", "sanity", NULL);
test_equal(dir, ddata);
test_equal(file, fdata);
test_equal(test_data_file_exists(NULL), (bool)false);
test_equal(test_data_file_exists("sanity"), (bool)false);
test_cp_data_dir();
test_equal(test_data_file_exists(NULL), (bool)true);
test_equal(test_data_file_exists("artist.db"), (bool)true);
test_equal(test_data_file_exists("cur_track"), (bool)true);
test_equal(test_data_file_exists("library.db"), (bool)true);
test_equal(test_data_file_exists("genre.db"), (bool)true);
test_equal(test_data_file_exists("playlist.db"), (bool)true);
test_equal(test_data_file_exists("track.db"), (bool)true);
test_equal(test_data_file_exists("album.db"), (bool)true);
test_equal(test_data_file_exists("deck"), (bool)true);
g_free(dir);
g_free(file);
g_free(ddata);
g_free(fdata);
}
DECLARE_UNIT_TESTS(
UNIT_TEST("Sanity Check", test_passing_tests),
UNIT_TEST("Sanity Check (All should fail)", test_failing_tests),
UNIT_TEST("Sanity Check Utility Functions", test_utilities),
);

View File

@ -1,52 +0,0 @@
/*
* Copyright 2015 (c) Anna Schumaker.
*/
#include <tests/test.h>
#include <cstdlib>
static unsigned int expected_failures = 0;
#define should_fail(code) \
code; \
expected_failures++
static unsigned int test_for_each_body(unsigned int i)
{
if (i > 10)
return LOOP_FAILED;
return LOOP_PASSED;
}
static void test_tests()
{
test_equal(0, 0);
test_equal((std::string)"2", (std::string)"2");
test_equal((void *)4, (void *)4);
should_fail(test_equal(0, 1));
should_fail(test_equal((std::string)"2", "3"));
should_fail(test_equal((void *)4, (void *)5));
test_not_equal(0, 1);
test_not_equal((std::string)"2", "3");
test_not_equal((void *)4, (void *)5);
should_fail(test_not_equal(0, 0));
should_fail(test_not_equal((std::string)"2", "2"));
should_fail(test_not_equal((void *)4, (void *)4));
test_for_each(0, 10, 1, test_for_each_body);
should_fail(test_for_each(0, 15, 1, test_for_each_body));
test_equal(test :: failed, expected_failures);
test :: failed -= expected_failures;
}
int main(int argc, char **argv)
{
test :: run("Testing Sanity Check", test_tests);
return 0;
}

196
tests/test.c Normal file
View File

@ -0,0 +1,196 @@
/*
* Copyright 2015 (c) Anna Schumaker.
*/
#include <tests/test.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static unsigned int test_num;
unsigned int tests_failed = 0;
static void test_print_result(bool passed, unsigned int line)
{
printf("\t%u: ", test_num++);
if (passed)
printf("Success!\n");
else {
printf("Failed at line %u.\n", line);
tests_failed++;
}
}
static void test_loop_print_result(bool passed, unsigned int i,
unsigned int line)
{
if (!passed) {
printf("\t%u: ", test_num++);
printf("Failed loop at line: %u (i = %u)\n", line, i);
tests_failed++;
}
}
/*
* lhs and rhs should be newly allocated strings so we can
* free them at the end of this function.
*/
void test_strings_equal(gchar *lhs, gchar *rhs, unsigned int line)
{
bool passed = strcmp(lhs, rhs) == 0;
test_print_result(passed, line);
if (!passed) {
printf("\t\t Actual: %s\n", lhs);
printf("\t\tExpected: %s\n", rhs);
}
g_free(lhs);
g_free(rhs);
}
void test_strings_not_equal(gchar *lhs, gchar *rhs, unsigned int line)
{
bool passed = strcmp(lhs, rhs) != 0;
test_print_result(passed, line);
if (!passed)
printf("\t\tUnexpected: %s\n", lhs);
g_free(lhs);
g_free(rhs);
}
void loop_strings_equal(gchar *lhs, gchar *rhs, unsigned int i,
unsigned int line)
{
bool passed = strcmp(lhs, rhs) == 0;
test_loop_print_result(passed, i, line);
if (!passed) {
printf("\t\t Actual: %s\n", lhs);
printf("\t\tExpected: %s\n", rhs);
}
g_free(lhs);
g_free(rhs);
}
void loop_strings_not_equal(gchar *lhs, gchar *rhs, unsigned int i,
unsigned int line)
{
bool passed = strcmp(lhs, rhs) != 0;
test_loop_print_result(passed, i, line);
if (!passed)
printf("\t\tUnexpected: %s\n", lhs);
g_free(lhs);
g_free(rhs);
}
static void run_test(struct UnitTest *test)
{
test_num = 0;
printf("Testing %s\n", test->t_name);
test->t_func();
printf("\n");
if (tests_failed > 0)
printf("\n%u tests failed =(\n\n", tests_failed);
}
gchar *test_data_file(const gchar *name)
{
return g_strjoin("/", g_get_user_data_dir(), "ocarina-test", name, NULL);
}
bool test_data_file_exists(const gchar *name)
{
GFileTest test = G_FILE_TEST_EXISTS;
gchar *dir;
bool ret;
if (!name)
test = G_FILE_TEST_IS_DIR;
dir = g_strjoin("/", g_get_user_data_dir(), "ocarina-test", name, NULL);
ret = g_file_test(dir, test);
g_free(dir);
return ret;
}
void test_cp_data_dir()
{
const gchar *file = NULL;
gchar *dst_dir = test_data_file(NULL);
GDir *dir = g_dir_open("tests/Data", 0, NULL);
if (!dir)
goto out;
g_mkdir_with_parents(dst_dir, 0755);
/* TODO: This could be a good place to use vfs_copy_file_range() */
while((file = g_dir_read_name(dir)) != NULL) {
gchar *orig = g_strjoin("/", "tests/Data", file, NULL);
gchar *dst = g_strjoin("/", dst_dir, file, NULL);
gchar *cmd = g_strjoin(" ", "cp", orig, dst, NULL);
system(cmd);
g_free(orig);
g_free(dst);
g_free(cmd);
}
g_dir_close(dir);
out:
g_free(dst_dir);
}
void test_rm_data_dir()
{
const gchar *file = NULL;
gchar *path = test_data_file(NULL);
GDir *dir = g_dir_open(path, 0, NULL);
if (!dir)
goto out;
while ((file = g_dir_read_name(dir)) != NULL) {
gchar *fullpath = g_strjoin("/", path, file, NULL);
g_remove(fullpath);
g_free(fullpath);
}
g_dir_close(dir);
g_rmdir(path);
out:
g_free(path);
}
void test_generate_library()
{
system("tests/gen_library.sh");
}
void test_rm_library_dirs()
{
system("rm -r /tmp/ocarina/dir2 /tmp/ocarina/dir4");
}
int main(int argc, char **argv)
{
unsigned int i;
if (test_data_file_exists(NULL))
test_rm_data_dir();
for (i = 0; i < unit_tests_size; i++) {
run_test(&unit_tests[i]);
if (tests_failed != 0)
break;
}
printf("\n");
return tests_failed;
}

View File

@ -1,137 +0,0 @@
/*
* Copyright 2015 (c) Anna Schumaker.
*/
#include <tests/test.h>
#include <glib.h>
#include <iostream>
#include <stdlib.h>
static unsigned int test_num;
unsigned int test :: failed;
template <class T>
static void generic_equal(const T &a, const T &b, const unsigned int line)
{
std::cout << " " << test_num++ << ": ";
if (a == b)
std::cout << "Success!" << std::endl;
else {
std::cout << "Failed at line " << line << ":" << std::endl;
std::cout << " Actual: " << a << std::endl;
std::cout << " Expected: " << b << std::endl;
test :: failed++;
}
}
template <class T>
static void generic_not_equal(const T &a, const T &b, const unsigned int line)
{
std::cout << " " << test_num++ << ": ";
if (a != b)
std::cout << "Success!" << std::endl;
else {
std::cout << "Failed at line " << line << ":" << std::endl;
std::cout << " Unexpected: " << a << std::endl;
test :: failed++;
}
}
void test :: run(const std::string &name, void (*func)())
{
failed = 0;
test_num = 0;
std::cout << name << std::endl;
func();
std::cout << std::endl;
if (failed > 0) {
std::cout << failed << " tests failed =(" << std::endl << std::endl;
exit(failed);
}
}
void test :: for_each(unsigned int init, unsigned int max, unsigned int inc,
unsigned int (*func)(unsigned int), unsigned int line)
{
std::cout << " " << test_num++ << ": ";
for (unsigned int i = init; i < max; i += inc) {
int ret = func(i);
if (ret != 0) {
std::cout << "Failed loop at line: " << ret;
std::cout << " (i = " << i << ")" << std::endl;
std::cout << " Called from line: " << line << std::endl;
test :: failed++;
return;
}
}
std::cout << "Success!" << std::endl;
}
#define DEFINE_COMPARE_FUNC(name, type) \
void test :: name(const type a, const type b, unsigned int line) \
{ generic_##name(a, b, line); }
#define DEFINE_COMPARE(type) \
DEFINE_COMPARE_FUNC(equal, type) \
DEFINE_COMPARE_FUNC(not_equal, type)
DEFINE_COMPARE(int)
DEFINE_COMPARE(std::string &)
DEFINE_COMPARE(void *)
std::string test :: data_dir()
{
std::string res = g_get_user_data_dir();
res += "/ocarina-test";
return res;
}
std::string test :: data_file(const std::string &name)
{
return data_dir() + "/" + name;
}
bool test :: data_dir_exists()
{
return g_file_test(data_dir().c_str(), G_FILE_TEST_IS_DIR);
}
bool test :: data_file_exists(const std::string &name)
{
return g_file_test(data_file(name).c_str(), G_FILE_TEST_EXISTS);
}
void test :: rm_data_dir()
{
std::string cmd = "rm -r " + data_dir() + " 2>/dev/null";
if (data_dir_exists())
system(cmd.c_str());
}
void test :: reset_data_dir()
{
std::string cmd = "mkdir -p " + data_dir();
rm_data_dir();
system(cmd.c_str());
}
void test :: cp_data_dir()
{
reset_data_dir();
std::string cmd = "cp -r tests/Data/* " + data_dir();
system(cmd.c_str());
}
void test :: gen_library()
{
system("tests/gen_library.sh");
}
void test :: rm_library_dirs()
{
system("rm -r /tmp/ocarina/dir2 /tmp/ocarina/dir4");
}