diff --git a/.gitignore b/.gitignore index 5464120..23c437a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.o tags test +test++ diff --git a/Makefile b/Makefile index 62525bb..e64e947 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,25 @@ -UNAME=$(shell uname) - CCFLAGS=-Wall -Wextra -Wconversion -Wredundant-decls -Wshadow -Wno-unused-parameter -O3 CC=clang +CXX=clang++ -all: test +all: test test++ remake: clean all -%.o: %.c ctest.h +%.cpp: %.c + ln -fs $< $@ + +%.c.o: %.c ctest.h $(CC) $(CCFLAGS) -c -o $@ $< -test: main.o ctest.h mytests.o - $(CC) $(LDFLAGS) main.o mytests.o -o test +%.cpp.o: %.cpp ctest.h + $(CXX) $(CCFLAGS) -c -o $@ $< -clean: - rm -f test *.o +test: main.c.o ctest.h mytests.c.o + $(CC) $(LDFLAGS) main.c.o mytests.c.o -o test +test++: main.cpp.o ctest.h mytests.cpp.o + $(CXX) $(LDFLAGS) main.cpp.o mytests.cpp.o -o test++ + +clean: + rm -f test test++ *.o diff --git a/README.md b/README.md index c4ebde4..d23128e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # CTEST -ctest is a unit test framework for software written in C. +ctest is a unit test framework for software written in C/C++. Features: * adding tests with minimal hassle (no manual adding to suites or testlists!) diff --git a/ctest.h b/ctest.h index 49e742f..1dbe17f 100644 --- a/ctest.h +++ b/ctest.h @@ -16,6 +16,10 @@ #ifndef CTEST_H #define CTEST_H +#ifdef __cplusplus +extern "C" { +#endif + #ifdef __GNUC__ #define CTEST_IMPL_FORMAT_PRINTF(a, b) __attribute__ ((format(printf, a, b))) #else @@ -25,9 +29,16 @@ #include /* intmax_t, uintmax_t, PRI* */ #include /* size_t */ +typedef void (*ctest_nullary_run_func)(void); +typedef void (*ctest_unary_run_func)(void*); typedef void (*ctest_setup_func)(void*); typedef void (*ctest_teardown_func)(void*); +union ctest_run_func_union { + ctest_nullary_run_func nullary; + ctest_unary_run_func unary; +}; + #define CTEST_IMPL_PRAGMA(x) _Pragma (#x) #if defined(__GNUC__) @@ -50,12 +61,10 @@ typedef void (*ctest_teardown_func)(void*); #define CTEST_IMPL_DIAG_POP() #endif -CTEST_IMPL_DIAG_PUSH_IGNORED(strict-prototypes) - struct ctest { const char* ssname; // suite name const char* ttname; // test name - void (*run)(); + union ctest_run_func_union run; void* data; ctest_setup_func* setup; @@ -66,8 +75,6 @@ struct ctest { unsigned int magic; }; -CTEST_IMPL_DIAG_POP() - #define CTEST_IMPL_NAME(name) ctest_##name #define CTEST_IMPL_FNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_run) #define CTEST_IMPL_TNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname) @@ -75,8 +82,10 @@ CTEST_IMPL_DIAG_POP() #define CTEST_IMPL_DATA_TNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_data) #define CTEST_IMPL_SETUP_FNAME(sname) CTEST_IMPL_NAME(sname##_setup) #define CTEST_IMPL_SETUP_FPNAME(sname) CTEST_IMPL_NAME(sname##_setup_ptr) +#define CTEST_IMPL_SETUP_TPNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_setup_ptr) #define CTEST_IMPL_TEARDOWN_FNAME(sname) CTEST_IMPL_NAME(sname##_teardown) #define CTEST_IMPL_TEARDOWN_FPNAME(sname) CTEST_IMPL_NAME(sname##_teardown_ptr) +#define CTEST_IMPL_TEARDOWN_TPNAME(sname, tname) CTEST_IMPL_NAME(sname##_##tname##_teardown_ptr) #define CTEST_IMPL_MAGIC (0xdeadbeef) #ifdef __APPLE__ @@ -87,14 +96,43 @@ CTEST_IMPL_DIAG_POP() #define CTEST_IMPL_STRUCT(sname, tname, tskip, tdata, tsetup, tteardown) \ static struct ctest CTEST_IMPL_TNAME(sname, tname) CTEST_IMPL_SECTION = { \ - .ssname=#sname, \ - .ttname=#tname, \ - .run = CTEST_IMPL_FNAME(sname, tname), \ - .data = tdata, \ - .setup = (ctest_setup_func*) tsetup, \ - .teardown = (ctest_teardown_func*) tteardown, \ - .skip = tskip, \ - .magic = CTEST_IMPL_MAGIC } + #sname, \ + #tname, \ + { (ctest_nullary_run_func) CTEST_IMPL_FNAME(sname, tname) }, \ + tdata, \ + (ctest_setup_func*) tsetup, \ + (ctest_teardown_func*) tteardown, \ + tskip, \ + CTEST_IMPL_MAGIC, \ + } + +#ifdef __cplusplus + +#define CTEST_SETUP(sname) \ + template <> void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data) + +#define CTEST_TEARDOWN(sname) \ + template <> void CTEST_IMPL_TEARDOWN_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data) + +#define CTEST_DATA(sname) \ + template void CTEST_IMPL_SETUP_FNAME(sname)(T* data) { } \ + template void CTEST_IMPL_TEARDOWN_FNAME(sname)(T* data) { } \ + struct CTEST_IMPL_DATA_SNAME(sname) + +#define CTEST_IMPL_CTEST(sname, tname, tskip) \ + static void CTEST_IMPL_FNAME(sname, tname)(void); \ + CTEST_IMPL_STRUCT(sname, tname, tskip, NULL, NULL, NULL); \ + static void CTEST_IMPL_FNAME(sname, tname)(void) + +#define CTEST_IMPL_CTEST2(sname, tname, tskip) \ + static struct CTEST_IMPL_DATA_SNAME(sname) CTEST_IMPL_DATA_TNAME(sname, tname); \ + static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* data); \ + static void (*CTEST_IMPL_SETUP_TPNAME(sname, tname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_SETUP_FNAME(sname); \ + static void (*CTEST_IMPL_TEARDOWN_TPNAME(sname, tname))(struct CTEST_IMPL_DATA_SNAME(sname)*) = &CTEST_IMPL_TEARDOWN_FNAME(sname); \ + CTEST_IMPL_STRUCT(sname, tname, tskip, &CTEST_IMPL_DATA_TNAME(sname, tname), &CTEST_IMPL_SETUP_TPNAME(sname, tname), &CTEST_IMPL_TEARDOWN_TPNAME(sname, tname)); \ + static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* data) + +#else #define CTEST_SETUP(sname) \ static void CTEST_IMPL_SETUP_FNAME(sname)(struct CTEST_IMPL_DATA_SNAME(sname)* data); \ @@ -123,6 +161,7 @@ CTEST_IMPL_DIAG_POP() CTEST_IMPL_STRUCT(sname, tname, tskip, &CTEST_IMPL_DATA_TNAME(sname, tname), &CTEST_IMPL_SETUP_FPNAME(sname), &CTEST_IMPL_TEARDOWN_FPNAME(sname)); \ static void CTEST_IMPL_FNAME(sname, tname)(struct CTEST_IMPL_DATA_SNAME(sname)* data) +#endif void CTEST_LOG(const char* fmt, ...) CTEST_IMPL_FORMAT_PRINTF(1, 2); void CTEST_ERR(const char* fmt, ...) CTEST_IMPL_FORMAT_PRINTF(1, 2); // doesn't return @@ -427,7 +466,7 @@ static uint64_t getCurrentTime(void) { static void color_print(const char* color, const char* text) { if (color_output) - printf("%s%s"ANSI_NORMAL"\n", color, text); + printf("%s%s" ANSI_NORMAL "\n", color, text); else printf("%s\n", text); } @@ -512,9 +551,9 @@ __attribute__((no_sanitize_address)) int ctest_main(int argc, const char *argv[] if (result == 0) { if (test->setup && *test->setup) (*test->setup)(test->data); if (test->data) - test->run(test->data); + test->run.unary(test->data); else - test->run(); + test->run.nullary(); if (test->teardown && *test->teardown) (*test->teardown)(test->data); // if we got here it's ok #ifdef CTEST_COLOR_OK @@ -543,5 +582,9 @@ __attribute__((no_sanitize_address)) int ctest_main(int argc, const char *argv[] #endif +#ifdef __cplusplus +} +#endif + #endif