diff --git a/clar.c b/clar.c index 2caa871..2d1c785 100644 --- a/clar.c +++ b/clar.c @@ -106,6 +106,15 @@ struct clar_error { struct clar_error *next; }; +struct clar_timing { + const char *test; + const char *suite; + + double elapsed; + + struct clar_timing *next; +}; + static struct { int argc; char **argv; @@ -124,9 +133,16 @@ static struct { int exit_on_error; int report_suite_names; + int report_benchmarks; + double timing_start; + double timing_end; + struct clar_error *errors; struct clar_error *last_error; + struct clar_timing *timings; + struct clar_timing *last_timing; + void (*local_cleanup)(void *); void *local_cleanup_payload; @@ -156,6 +172,7 @@ struct clar_suite { static void clar_print_init(int test_count, int suite_count, const char *suite_names); static void clar_print_shutdown(int test_count, int suite_count, int error_count); static void clar_print_error(int num, const struct clar_error *error); +static void clar_print_timing(const struct clar_timing *timing); static void clar_print_ontest(const char *test_name, int test_number, enum cl_test_status failed); static void clar_print_onsuite(const char *suite_name, int suite_index); static void clar_print_onabort(const char *msg, ...); @@ -164,6 +181,12 @@ static void clar_print_onabort(const char *msg, ...); static void clar_unsandbox(void); static int clar_sandbox(void); +/* From time.h */ +/** + * Return the time from a monotonic timer. + */ +static double clar_timer(void); + /* Load the declarations for the test suite */ #include "clar.suite" @@ -203,6 +226,22 @@ clar_report_errors(void) _clar.errors = _clar.last_error = NULL; } +static void +clar_report_timings(void) +{ + struct clar_timing *timing, *next; + + timing = _clar.timings; + + while (timing != NULL) { + next = timing->next; + clar_print_timing(timing); + free(timing); + timing = next; + } + +} + static void clar_run_test( const struct clar_func *test, @@ -210,6 +249,8 @@ clar_run_test( const struct clar_func *cleanup) { _clar.test_status = CL_TEST_OK; + _clar.timing_start = 0.0; + _clar.timing_end = 0.0; _clar.trampoline_enabled = 1; CL_TRACE(CL_TRACE__TEST__BEGIN); @@ -219,7 +260,9 @@ clar_run_test( initialize->ptr(); CL_TRACE(CL_TRACE__TEST__RUN_BEGIN); + _clar.timing_start = clar_timer(); test->ptr(); + _clar.timing_end = clar_timer(); CL_TRACE(CL_TRACE__TEST__RUN_END); } @@ -246,6 +289,8 @@ clar_run_test( } } +static void clar_store_timing(void); + static void clar_run_suite(const struct clar_suite *suite, const char *filter) { @@ -287,6 +332,8 @@ clar_run_suite(const struct clar_suite *suite, const char *filter) if (_clar.exit_on_error && _clar.total_errors) return; + + clar_store_timing(); } _clar.active_test = NULL; @@ -305,6 +352,7 @@ clar_usage(const char *arg) printf(" -q \tOnly report tests that had an error\n"); printf(" -Q \tQuit as soon as a test fails\n"); printf(" -l \tPrint suite names\n"); + printf(" -b \tReport test benchmarks\n"); exit(-1); } @@ -356,6 +404,11 @@ clar_parse_args(int argc, char **argv) } } + if (_clar.report_benchmarks) { + puts(""); + clar_report_timings(); + } + if (!found) { clar_print_onabort("No suite matching '%s' found.\n", argument); exit(-1); @@ -363,6 +416,10 @@ clar_parse_args(int argc, char **argv) break; } + case 'b': + _clar.report_benchmarks = 1; + break; + case 'q': _clar.report_errors_only = 1; break; @@ -418,6 +475,11 @@ clar_test_run() size_t i; for (i = 0; i < _clar_suite_count; ++i) clar_run_suite(&_clar_suites[i], NULL); + + if (_clar.report_benchmarks) { + puts(""); + clar_report_timings(); + } } return _clar.total_errors; @@ -447,6 +509,29 @@ clar_test(int argc, char **argv) return errors; } +static void clar_store_timing(void) +{ + struct clar_timing *timing; + + /* Failed tests jump over the timing code */ + if (_clar.timing_end == 0) + return; + + timing = calloc(1, sizeof(struct clar_timing)); + + if (_clar.timings == NULL) + _clar.timings = timing; + + if (_clar.last_timing != NULL) + _clar.last_timing->next = timing; + + _clar.last_timing = timing; + + timing->elapsed = _clar.timing_end - _clar.timing_start; + timing->test = _clar.active_test; + timing->suite = _clar.active_suite; +} + static void abort_test(void) { if (!_clar.trampoline_enabled) { @@ -460,6 +545,11 @@ static void abort_test(void) longjmp(_clar.trampoline, -1); } +void clar__reset_timer(void) +{ + _clar.timing_start = clar_timer(); +} + void clar__skip(void) { _clar.test_status = CL_TEST_SKIP; @@ -640,3 +730,4 @@ void cl_set_cleanup(void (*cleanup)(void *), void *opaque) #include "clar/fixtures.h" #include "clar/fs.h" #include "clar/print.h" +#include "clar/time.h" diff --git a/clar.h b/clar.h index 5c674d7..2195a02 100644 --- a/clar.h +++ b/clar.h @@ -110,6 +110,11 @@ void cl_fixture_cleanup(const char *fixture_name); #define cl_skip() clar__skip() +/** + * Timer-related functions + */ +#define cl_reset_timer() clar__reset_timer() + /** * Typed assertion macros */ @@ -133,6 +138,8 @@ void cl_fixture_cleanup(const char *fixture_name); #define cl_assert_equal_p(p1,p2) clar__assert_equal(__FILE__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2)) +void clar__reset_timer(void); + void clar__skip(void); void clar__fail( diff --git a/clar/print.h b/clar/print.h index 6529b6b..9fce4d9 100644 --- a/clar/print.h +++ b/clar/print.h @@ -35,6 +35,12 @@ static void clar_print_error(int num, const struct clar_error *error) fflush(stdout); } +static void clar_print_timing(const struct clar_timing *timing) +{ + printf("Benchmark %s::%s %f\n", timing->suite, timing->test, timing->elapsed); + fflush(stdout); +} + static void clar_print_ontest(const char *test_name, int test_number, enum cl_test_status status) { (void)test_name; diff --git a/clar/time.h b/clar/time.h new file mode 100644 index 0000000..9f1ce2c --- /dev/null +++ b/clar/time.h @@ -0,0 +1,71 @@ +#ifdef GIT_WIN32 + +double clar_timer(void) +{ + /* We need the initial tick count to detect if the tick + * count has rolled over. */ + static DWORD initial_tick_count = 0; + + /* GetTickCount returns the number of milliseconds that have + * elapsed since the system was started. */ + DWORD count = GetTickCount(); + + if(initial_tick_count == 0) { + initial_tick_count = count; + } else if (count < initial_tick_count) { + /* The tick count has rolled over - adjust for it. */ + count = (0xFFFFFFFF - initial_tick_count) + count; + } + + return (double count / (double 1000)); +} + +#elif __APPLE__ + +#include + +double clar_timer(void) +{ + uint64_t time = mach_absolute_time(); + static double scaling_factor = 0; + + if (scaling_factor == 0) { + mach_timebase_info_data_t info; + (void)mach_timebase_info(&info); + scaling_factor = (double)info.numer / (double)info.denom; + } + + return (double)time * scaling_factor / 1.0E9; +} + +#elif defined(AMIGA) + +#include + +double clar_timer(void) +{ + struct TimeVal tv; + ITimer->GetUpTime(&tv); + return (doubletv.Seconds + (doubletv.Microseconds / 1.0E6)); +} + +#else + +#include + +double clar_timer(void) +{ + struct timespec tp; + + if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) { + return (double tp.tv_sec + (double tp.tv_nsec / 1.0E9)); + } else { + /* Fall back to using gettimeofday */ + struct timeval tv; + struct timezone tz; + gettimeofday(&tv, &tz); + return (doubletv.tv_sec + (doubletv.tv_usec / 1.0E6)); + } +} + +#endif diff --git a/test/clar_test.h b/test/clar_test.h index 0fcaa63..c713769 100644 --- a/test/clar_test.h +++ b/test/clar_test.h @@ -12,5 +12,6 @@ /* Your custom shared includes / defines here */ extern int global_test_counter; +extern int global_is_bench; #endif diff --git a/test/main.c b/test/main.c index 59e56ad..2721c85 100644 --- a/test/main.c +++ b/test/main.c @@ -34,7 +34,7 @@ int main(int argc, char *argv[]) ret = clar_test(argc, argv); /* Your custom cleanup here */ - cl_assert_equal_i(8, global_test_counter); + cl_assert_equal_i(11, global_test_counter); return ret; } diff --git a/test/sample.c b/test/sample.c index faa1209..cf8163a 100644 --- a/test/sample.c +++ b/test/sample.c @@ -82,3 +82,33 @@ void test_sample__ptr(void) cl_assert_equal_p(actual, actual); /* pointers to same object */ cl_assert_equal_p(&actual, actual); } + +void test_sample__bench_loop(void) +{ + int i; + + for (i = 0; i < 1000000; i++) { + } +} + +void test_sample__bench_loop2(void) +{ + int i; + + for (i = 0; i < 1000000; i++) { + int dummy = i*1000; + dummy = dummy; + } +} + +void test_sample__bench_reset_timer(void) +{ + int i; + + for (i = 0; i < 100000000; i++) { + int dummy = i*1000; + dummy = dummy; + } + + cl_reset_timer(); +}