diff --git a/examples/valgrind-error.c b/examples/valgrind-error.c index 1dd729a..f56a2a8 100644 --- a/examples/valgrind-error.c +++ b/examples/valgrind-error.c @@ -61,3 +61,11 @@ SCU_TEST(memory_leak, "Memory leak") b[1] = 17; SCU_ASSERT(b[1] == 17); } + +SCU_TEST(another_memory_leak, "Another memory leak") +{ + char *b = malloc(15); + + b[1] = 17; + SCU_ASSERT(b[1] == 17); +} diff --git a/libscu-c/Makefile b/libscu-c/Makefile index 868f9d1..748c5c7 100644 --- a/libscu-c/Makefile +++ b/libscu-c/Makefile @@ -7,6 +7,10 @@ CFLAGS+=$(SCU_VALGRIND_CFLAGS) endif endif +ifneq ($(SCU_VALGRIND_LEAK_LEVEL),) +CFLAGS+=-DSCU_VALGRIND_LEAK_LEVEL=$(SCU_VALGRIND_LEAK_LEVEL) +endif + .PHONY: clean libscu-c.a: src/scu.o diff --git a/libscu-c/src/scu.c b/libscu-c/src/scu.c index b0d60d3..16c6f7b 100644 --- a/libscu-c/src/scu.c +++ b/libscu-c/src/scu.c @@ -13,6 +13,10 @@ #ifdef SCU_HAVE_VALGRIND #include +#include +#ifndef SCU_VALGRIND_LEAK_LEVEL +#define SCU_VALGRIND_LEAK_LEVEL 1 +#endif #else #define VALGRIND_COUNT_ERRORS 0 #define VALGRIND_PRINTF(format, ...) @@ -202,7 +206,7 @@ static void _scu_output_test_end(int idx, bool success, size_t asserts, double mono_time, double cpu_time, size_t num_failures, _scu_failure *failures, - size_t valgrind_errors) + size_t valgrind_errors, size_t valgrind_leaks) { json_object_start(_scu_cmd_fd); json_object_key(_scu_cmd_fd, "event"); @@ -228,6 +232,11 @@ _scu_output_test_end(int idx, bool success, size_t asserts, json_object_key(_scu_cmd_fd, "valgrind_errors"); json_integer(_scu_cmd_fd, valgrind_errors); } + if (valgrind_leaks) { + json_separator(_scu_cmd_fd); + json_object_key(_scu_cmd_fd, "valgrind_leaks"); + json_integer(_scu_cmd_fd, valgrind_leaks); + } json_object_end(_scu_cmd_fd); _scu_flush_json(); } @@ -266,6 +275,30 @@ _scu_redirect_output(char *filename, size_t len) setvbuf(stderr, NULL, _IONBF, 0); } +static unsigned _scu_valgrind_leaks (void) +{ + unsigned total_leaks = 0; + +#ifdef SCU_HAVE_VALGRIND + unsigned leaked, dubious, reachable, suppressed; + + VALGRIND_DO_QUICK_LEAK_CHECK; + VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed); + + /* Not errorous leaks */ + (void) reachable; + (void) suppressed; + + if (SCU_VALGRIND_LEAK_LEVEL >= 1) + total_leaks += leaked; + + if (SCU_VALGRIND_LEAK_LEVEL >= 2) + total_leaks += dubious; +#endif + + return total_leaks; +} + /* Module actions */ static void @@ -338,6 +371,7 @@ _scu_run_test(int idx) struct timespec start_mono_time, end_mono_time, start_cpu_time, end_cpu_time; unsigned valgrind_errors_before = VALGRIND_COUNT_ERRORS; + unsigned valgrind_leaks_before = _scu_valgrind_leaks(); _scu_before_each(); @@ -361,13 +395,18 @@ _scu_run_test(int idx) _scu_after_each(); unsigned valgrind_errors_after = VALGRIND_COUNT_ERRORS; + unsigned valgrind_leaks_after = _scu_valgrind_leaks(); unsigned valgrind_error_count = valgrind_errors_after - valgrind_errors_before; + unsigned valgrind_leak_count = valgrind_leaks_after - valgrind_leaks_before; + + success = success && !valgrind_error_count && !valgrind_leak_count; - _scu_output_test_end(idx, success && !valgrind_error_count, asserts, + _scu_output_test_end(idx, success, asserts, _scu_get_time_diff(start_mono_time, end_mono_time), _scu_get_time_diff(start_cpu_time, end_cpu_time), - num_failures, _failures, valgrind_error_count); + num_failures, _failures, valgrind_error_count, + valgrind_leak_count); } static void diff --git a/testrunner b/testrunner index f5285db..9f434fd 100755 --- a/testrunner +++ b/testrunner @@ -390,6 +390,9 @@ class TestEmitter(Observer): if event.get('valgrind_errors', 0): print(" ! {colors.RED}{vgerrs} valgrind error(s){colors.DEFAULT} reported!" .format(vgerrs=event['valgrind_errors'], colors=Colors)) + if event.get('valgrind_leaks', 0): + print(" ! {colors.RED}{vgerrs}B leaked memory{colors.DEFAULT} reported!" + .format(vgerrs=event['valgrind_leaks'], colors=Colors)) elif event_type in ('testcase_error', 'module_crash'): print('') print(" ! " + event['message']) @@ -414,6 +417,8 @@ class SummaryEmitter(Observer): self.has_reported_failing_test = {} self.tests_with_valgrind_errors_counter = 0 self.valgrind_errors_counter = 0 + self.tests_with_valgrind_leaks_counter = 0 + self.valgrind_leaks_counter = 0 self.show_valgrind_stats = False def handle_module_start(self, module, event): @@ -431,6 +436,9 @@ class SummaryEmitter(Observer): if event.get('valgrind_errors', 0): self.tests_with_valgrind_errors_counter += 1 self.valgrind_errors_counter += event['valgrind_errors'] + if event.get('valgrind_leaks', 0): + self.tests_with_valgrind_leaks_counter += 1 + self.valgrind_leaks_counter += event['valgrind_leaks'] def handle_testcase_error(self, module, event): self.test_counter += 1 @@ -449,6 +457,7 @@ class SummaryEmitter(Observer): def is_failure(self): return any((self.module_fail_counter > 0, self.valgrind_errors_counter > 0, + self.valgrind_leaks_counter > 0, self.module_init_fail_counter > 0)) def print_summary(self): @@ -482,11 +491,15 @@ class SummaryEmitter(Observer): " | Valgrind | |\n" " +--------------------+------------+\n" " | Errors: | {:10} |\n" + " | Leaks(B): | {:10} |\n" " | Tests with errors: | {:10} |\n" + " | Tests with leaks: [ {:10} |\n" " +--------------------+------------+\n" ).format( self.valgrind_errors_counter, + self.valgrind_leaks_counter, self.tests_with_valgrind_errors_counter, + self.tests_with_valgrind_leaks_counter, ))