From c817c214033481fe59f9f44c325a9092dc337d07 Mon Sep 17 00:00:00 2001 From: Luis Pabon Date: Thu, 20 Feb 2014 13:50:19 -0500 Subject: build: GlusterFS Unit Test Framework This patch will allow for developers to create unit tests for their code. Documentation has been added to the patch and is available here: doc/hacker-guide/en-US/markdown/unittest.md Also, unit tests are run when RPM is created. BUG: 1067059 Change-Id: I95cf8bb0354d4ca4ed4476a0f2385436a17d2369 Signed-off-by: Vijay Bellur Signed-off-by: Luis Pabon Reviewed-on: http://review.gluster.org/7145 Tested-by: Gluster Build System Reviewed-by: Rajesh Joseph Reviewed-by: Justin Clift Tested-by: Justin Clift --- libglusterfs/src/Makefile.am | 17 + libglusterfs/src/mem-pool.c | 39 ++- libglusterfs/src/mem-pool.h | 13 + libglusterfs/src/unittest/global_mock.c | 24 ++ libglusterfs/src/unittest/log_mock.c | 43 +++ libglusterfs/src/unittest/mem_pool_unittest.c | 472 ++++++++++++++++++++++++++ 6 files changed, 594 insertions(+), 14 deletions(-) create mode 100644 libglusterfs/src/unittest/global_mock.c create mode 100644 libglusterfs/src/unittest/log_mock.c create mode 100644 libglusterfs/src/unittest/mem_pool_unittest.c (limited to 'libglusterfs') diff --git a/libglusterfs/src/Makefile.am b/libglusterfs/src/Makefile.am index 634e217ed..ac6a3b9ec 100644 --- a/libglusterfs/src/Makefile.am +++ b/libglusterfs/src/Makefile.am @@ -55,3 +55,20 @@ y.tab.h: graph.y CLEANFILES = graph.lex.c y.tab.c y.tab.h CONFIG_CLEAN_FILES = $(CONTRIB_BUILDDIR)/uuid/uuid_types.h + +#### UNIT TESTS ##### +CLEANFILES += *.gcda *.gcno *_xunit.xml +noinst_PROGRAMS = +TESTS = + +mem_pool_unittest_CPPFLAGS = $(UNITTEST_CPPFLAGS) $(libglusterfs_la_CPPFLAGS) +mem_pool_unittest_SOURCES = mem-pool.c \ + mem-pool.h \ + unittest/mem_pool_unittest.c \ + unittest/log_mock.c \ + unittest/global_mock.c +mem_pool_unittest_CFLAGS = $(UNITTEST_CFLAGS) +mem_pool_unittest_LDADD = $(UNITTEST_LDADD) +mem_pool_unittest_LDFLAGS = $(UNITTEST_LDFLAGS) +noinst_PROGRAMS += mem_pool_unittest +TESTS += mem_pool_unittest diff --git a/libglusterfs/src/mem-pool.c b/libglusterfs/src/mem-pool.c index b901dd7a8..96e049105 100644 --- a/libglusterfs/src/mem-pool.c +++ b/libglusterfs/src/mem-pool.c @@ -22,29 +22,30 @@ #define is_mem_chunk_in_use(ptr) (*ptr == 1) #define mem_pool_from_ptr(ptr) ((ptr) + GF_MEM_POOL_LIST_BOUNDARY) -#define GF_MEM_HEADER_SIZE (4 + sizeof (size_t) + sizeof (xlator_t *) + 4 + 8) -#define GF_MEM_TRAILER_SIZE 8 - -#define GF_MEM_HEADER_MAGIC 0xCAFEBABE -#define GF_MEM_TRAILER_MAGIC 0xBAADF00D - #define GLUSTERFS_ENV_MEM_ACCT_STR "GLUSTERFS_DISABLE_MEM_ACCT" +#include +#include + void gf_mem_acct_enable_set (void *data) { glusterfs_ctx_t *ctx = NULL; + REQUIRE(data != NULL); + ctx = data; - GF_ASSERT (ctx); + GF_ASSERT (ctx != NULL); ctx->mem_acct_enable = 1; + ENSURE(1 == ctx->mem_acct_enable); + return; } -void +int gf_mem_set_acct_info (xlator_t *xl, char **alloc_ptr, size_t size, uint32_t type) { @@ -52,7 +53,7 @@ gf_mem_set_acct_info (xlator_t *xl, char **alloc_ptr, char *ptr = NULL; if (!alloc_ptr) - return; + return -1; ptr = (char *) (*alloc_ptr); @@ -88,7 +89,7 @@ gf_mem_set_acct_info (xlator_t *xl, char **alloc_ptr, *(uint32_t *) (ptr + size) = GF_MEM_TRAILER_MAGIC; *alloc_ptr = (void *)ptr; - return; + return 0; } @@ -150,10 +151,13 @@ __gf_realloc (void *ptr, size_t size) char *orig_ptr = NULL; xlator_t *xl = NULL; uint32_t type = 0; + char *new_ptr; if (!THIS->ctx->mem_acct_enable) return REALLOC (ptr, size); + REQUIRE(NULL != ptr); + tot_size = size + GF_MEM_HEADER_SIZE + GF_MEM_TRAILER_SIZE; orig_ptr = (char *)ptr - 8 - 4; @@ -166,15 +170,22 @@ __gf_realloc (void *ptr, size_t size) orig_ptr = (char *)ptr - GF_MEM_HEADER_SIZE; type = *(uint32_t *)orig_ptr; - ptr = realloc (orig_ptr, tot_size); - if (!ptr) { + new_ptr = realloc (orig_ptr, tot_size); + if (!new_ptr) { gf_log_nomem ("", GF_LOG_ALERT, tot_size); return NULL; } - gf_mem_set_acct_info (xl, (char **)&ptr, size, type); + /* + * We used to pass (char **)&ptr as the second + * argument after the value of realloc was saved + * in ptr, but the compiler warnings complained + * about the casting to and forth from void ** to + * char **. + */ + gf_mem_set_acct_info (xl, &new_ptr, size, type); - return (void *)ptr; + return (void *)new_ptr; } int diff --git a/libglusterfs/src/mem-pool.h b/libglusterfs/src/mem-pool.h index 31f49f75c..aa6bf7843 100644 --- a/libglusterfs/src/mem-pool.h +++ b/libglusterfs/src/mem-pool.h @@ -20,6 +20,19 @@ #include #include +/* + * Need this for unit tests since inline functions + * access memory allocation and need to use the + * unit test versions + */ +#ifdef UNIT_TESTING +#include +#endif + +#define GF_MEM_HEADER_SIZE (4 + sizeof (size_t) + sizeof (xlator_t *) + 4 + 8) +#define GF_MEM_TRAILER_SIZE 8 +#define GF_MEM_HEADER_MAGIC 0xCAFEBABE +#define GF_MEM_TRAILER_MAGIC 0xBAADF00D struct mem_acct { uint32_t num_types; diff --git a/libglusterfs/src/unittest/global_mock.c b/libglusterfs/src/unittest/global_mock.c new file mode 100644 index 000000000..b50638d10 --- /dev/null +++ b/libglusterfs/src/unittest/global_mock.c @@ -0,0 +1,24 @@ +/* + Copyright (c) 2014 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#include "logging.h" +#include "xlator.h" + +#include +#include +#include +#include + +#include + +xlator_t **__glusterfs_this_location () +{ + return ((xlator_t **)(uintptr_t)mock()); +} diff --git a/libglusterfs/src/unittest/log_mock.c b/libglusterfs/src/unittest/log_mock.c new file mode 100644 index 000000000..676df7cfd --- /dev/null +++ b/libglusterfs/src/unittest/log_mock.c @@ -0,0 +1,43 @@ +/* + Copyright (c) 2014 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#include "logging.h" +#include "xlator.h" + +#include +#include +#include +#include + +#include + +int _gf_log (const char *domain, const char *file, + const char *function, int32_t line, gf_loglevel_t level, + const char *fmt, ...) +{ + return 0; +} + +int _gf_log_callingfn (const char *domain, const char *file, + const char *function, int32_t line, gf_loglevel_t level, + const char *fmt, ...) +{ + return 0; +} + +int _gf_log_nomem (const char *domain, const char *file, + const char *function, int line, gf_loglevel_t level, + size_t size) +{ + return 0; +} + +void +gf_log_globals_init (void *data) {} diff --git a/libglusterfs/src/unittest/mem_pool_unittest.c b/libglusterfs/src/unittest/mem_pool_unittest.c new file mode 100644 index 000000000..3c0724d65 --- /dev/null +++ b/libglusterfs/src/unittest/mem_pool_unittest.c @@ -0,0 +1,472 @@ +/* + Copyright (c) 2014 Red Hat, Inc. + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#include "mem-pool.h" +#include "logging.h" +#include "xlator.h" + +#include +#include +#include +#include +#include +#include +#include + +/* + * memory header for gf_mem_set_acct_info + */ +typedef struct __attribute__((packed)) { + uint32_t type; + size_t size; + xlator_t *xl; + uint32_t header_magic; + uint8_t pad[8]; +} mem_header_t; + +/* + * Prototypes to private functions + */ +int +gf_mem_set_acct_info (xlator_t *xl, char **alloc_ptr, + size_t size, uint32_t type); + +/* + * Helper functions + */ +static xlator_t * +helper_xlator_init(uint32_t num_types) +{ + xlator_t *xl; + int i, ret; + + REQUIRE(num_types > 0); + + xl = test_calloc(1, sizeof(xlator_t)); + assert_non_null(xl); + xl->mem_acct.num_types = num_types; + xl->mem_acct.rec = test_calloc(num_types, sizeof(struct mem_acct_rec)); + assert_non_null(xl->mem_acct.rec); + + xl->ctx = test_calloc(1, sizeof(glusterfs_ctx_t)); + assert_non_null(xl->ctx); + + for (i = 0; i < num_types; i++) { + ret = LOCK_INIT(&(xl->mem_acct.rec[i].lock)); + assert_int_equal(ret, 0); + } + + ENSURE(num_types == xl->mem_acct.num_types); + ENSURE(NULL != xl); + + return xl; +} + +static int +helper_xlator_destroy(xlator_t *xl) +{ + int i, ret; + + for (i = 0; i < xl->mem_acct.num_types; i++) { + ret = LOCK_DESTROY(&(xl->mem_acct.rec[i].lock)); + assert_int_equal(ret, 0); + } + + free(xl->mem_acct.rec); + free(xl->ctx); + free(xl); + return 0; +} + +static void +helper_check_memory_headers( char *mem, + xlator_t *xl, + size_t size, + uint32_t type) +{ + mem_header_t *p; + + p = (mem_header_t *)mem, + assert_int_equal(p->type, type); + assert_int_equal(p->size, size); + assert_true(p->xl == xl); + assert_int_equal(p->header_magic, GF_MEM_HEADER_MAGIC); + assert_true(*(uint32_t *)(mem+sizeof(mem_header_t)+size) == GF_MEM_TRAILER_MAGIC); + +} + +/* + * Tests + */ +static void +test_gf_mem_acct_enable_set(void **state) +{ + (void) state; + glusterfs_ctx_t test_ctx; + + expect_assert_failure(gf_mem_acct_enable_set(NULL)); + + memset(&test_ctx, 0, sizeof(test_ctx)); + assert_true(NULL == test_ctx.process_uuid); + gf_mem_acct_enable_set((void *)&test_ctx); + assert_true(1 == test_ctx.mem_acct_enable); + assert_true(NULL == test_ctx.process_uuid); +} + +static void +test_gf_mem_set_acct_info_asserts(void **state) +{ + xlator_t *xl; + xlator_t xltest; + char *alloc_ptr; + size_t size; + uint32_t type; + + memset(&xltest, 0, sizeof(xlator_t)); + xl = (xlator_t *)0xBADD; + alloc_ptr = (char *)0xBADD; + size = 8196; + type = 0; + + + // Check xl is NULL + expect_assert_failure(gf_mem_set_acct_info(NULL, &alloc_ptr, size, type)); + // Check xl->mem_acct.rec = NULL + expect_assert_failure(gf_mem_set_acct_info(&xltest, &alloc_ptr, 0, type)); + // Check type <= xl->mem_acct.num_types + type = 100; + expect_assert_failure(gf_mem_set_acct_info(&xltest, &alloc_ptr, 0, type)); + // Check alloc is NULL + assert_int_equal(-1, gf_mem_set_acct_info(&xltest, NULL, size, type)); + + // Initialize xl + xl = helper_xlator_init(10); + + // Test number of types + type = 100; + assert_true(NULL != xl->mem_acct.rec); + assert_true(type > xl->mem_acct.num_types); + expect_assert_failure(gf_mem_set_acct_info(xl, &alloc_ptr, size, type)); + + helper_xlator_destroy(xl); +} + +static void +test_gf_mem_set_acct_info_memory(void **state) +{ + xlator_t *xl; + char *alloc_ptr; + char *temp_ptr; + size_t size; + uint32_t type; + + size = 8196; + type = 9; + + // Initialize xl + xl = helper_xlator_init(10); + + // Test allocation + temp_ptr = test_calloc(1, size + GF_MEM_HEADER_SIZE + GF_MEM_TRAILER_SIZE); + assert_non_null(temp_ptr); + alloc_ptr = temp_ptr; + gf_mem_set_acct_info(xl, &alloc_ptr, size, type); + + //Check values + assert_int_equal(xl->mem_acct.rec[type].size, size); + assert_int_equal(xl->mem_acct.rec[type].num_allocs, 1); + assert_int_equal(xl->mem_acct.rec[type].total_allocs, 1); + assert_int_equal(xl->mem_acct.rec[type].max_size, size); + assert_int_equal(xl->mem_acct.rec[type].max_num_allocs, 1); + + // Check memory + helper_check_memory_headers(temp_ptr, xl, size, type); + + // Check that alloc_ptr has been moved correctly + // by gf_mem_set_acct_info + { + mem_header_t *p; + + p = (mem_header_t *)temp_ptr; + p++; + p->type = 1234; + assert_int_equal(*(uint32_t *)alloc_ptr, p->type); + } + + free(temp_ptr); + helper_xlator_destroy(xl); +} + +static void +test_gf_calloc_default_calloc(void **state) +{ + xlator_t *xl; + void *mem; + size_t size; + uint32_t type; + + // Initialize xl + xl = helper_xlator_init(10); + assert_int_equal(xl->ctx->mem_acct_enable, 0); + will_return(__glusterfs_this_location, &xl); + + // Call __gf_calloc + size = 1024; + type = 3; + mem = __gf_calloc(1, size, type); + assert_non_null(mem); + memset(mem, 0x5A, size); + + // Check xl did not change + assert_int_equal(xl->mem_acct.rec[type].size, 0); + assert_int_equal(xl->mem_acct.rec[type].num_allocs, 0); + assert_int_equal(xl->mem_acct.rec[type].total_allocs, 0); + assert_int_equal(xl->mem_acct.rec[type].max_size, 0); + assert_int_equal(xl->mem_acct.rec[type].max_num_allocs, 0); + + free(mem); + helper_xlator_destroy(xl); +} + +static void +test_gf_calloc_mem_acct_enabled(void **state) +{ + xlator_t *xl; + void *mem; + size_t size; + uint32_t type; + + // Initialize xl + xl = helper_xlator_init(10); + assert_int_equal(xl->ctx->mem_acct_enable, 0); + xl->ctx->mem_acct_enable = 1; + + // For line mem-pool.c:115 and mem-pool:118 + will_always_return(__glusterfs_this_location, &xl); + + // Call __gf_calloc + size = 1024; + type = 3; + mem = __gf_calloc(1, size, type); + assert_non_null(mem); + memset(mem, 0x5A, size); + + // Check xl values + assert_int_equal(xl->mem_acct.rec[type].size, size); + assert_int_equal(xl->mem_acct.rec[type].num_allocs, 1); + assert_int_equal(xl->mem_acct.rec[type].total_allocs, 1); + assert_int_equal(xl->mem_acct.rec[type].max_size, size); + assert_int_equal(xl->mem_acct.rec[type].max_num_allocs, 1); + + // Check memory + helper_check_memory_headers(mem - sizeof(mem_header_t), xl, size, type); + free(mem - sizeof(mem_header_t)); + helper_xlator_destroy(xl); +} + +static void +test_gf_malloc_default_malloc(void **state) +{ + xlator_t *xl; + void *mem; + size_t size; + uint32_t type; + + // Initialize xl + xl = helper_xlator_init(10); + assert_int_equal(xl->ctx->mem_acct_enable, 0); + will_return(__glusterfs_this_location, &xl); + + // Call __gf_malloc + size = 1024; + type = 3; + mem = __gf_malloc(size, type); + assert_non_null(mem); + memset(mem, 0x5A, size); + + // Check xl did not change + assert_int_equal(xl->mem_acct.rec[type].size, 0); + assert_int_equal(xl->mem_acct.rec[type].num_allocs, 0); + assert_int_equal(xl->mem_acct.rec[type].total_allocs, 0); + assert_int_equal(xl->mem_acct.rec[type].max_size, 0); + assert_int_equal(xl->mem_acct.rec[type].max_num_allocs, 0); + + free(mem); + helper_xlator_destroy(xl); +} + +static void +test_gf_malloc_mem_acct_enabled(void **state) +{ + xlator_t *xl; + void *mem; + size_t size; + uint32_t type; + + // Initialize xl + xl = helper_xlator_init(10); + assert_int_equal(xl->ctx->mem_acct_enable, 0); + xl->ctx->mem_acct_enable = 1; + + // For line mem-pool.c:115 and mem-pool:118 + will_always_return(__glusterfs_this_location, &xl); + + // Call __gf_malloc + size = 1024; + type = 3; + mem = __gf_malloc(size, type); + assert_non_null(mem); + memset(mem, 0x5A, size); + + // Check xl values + assert_int_equal(xl->mem_acct.rec[type].size, size); + assert_int_equal(xl->mem_acct.rec[type].num_allocs, 1); + assert_int_equal(xl->mem_acct.rec[type].total_allocs, 1); + assert_int_equal(xl->mem_acct.rec[type].max_size, size); + assert_int_equal(xl->mem_acct.rec[type].max_num_allocs, 1); + + // Check memory + helper_check_memory_headers(mem - sizeof(mem_header_t), xl, size, type); + free(mem - sizeof(mem_header_t)); + helper_xlator_destroy(xl); +} + +static void +test_gf_realloc_default_realloc(void **state) +{ + xlator_t *xl; + void *mem; + size_t size; + uint32_t type; + + // Initialize xl + xl = helper_xlator_init(10); + assert_int_equal(xl->ctx->mem_acct_enable, 0); + will_always_return(__glusterfs_this_location, &xl); + + // Call __gf_malloc then realloc + size = 10; + type = 3; + mem = __gf_malloc(size, type); + assert_non_null(mem); + memset(mem, 0xA5, size); + + size = 1024; + mem = __gf_realloc(mem, size); + assert_non_null(mem); + memset(mem, 0x5A, size); + + // Check xl did not change + assert_int_equal(xl->mem_acct.rec[type].size, 0); + assert_int_equal(xl->mem_acct.rec[type].num_allocs, 0); + assert_int_equal(xl->mem_acct.rec[type].total_allocs, 0); + assert_int_equal(xl->mem_acct.rec[type].max_size, 0); + assert_int_equal(xl->mem_acct.rec[type].max_num_allocs, 0); + + free(mem); + helper_xlator_destroy(xl); +} + +static void +test_gf_realloc_mem_acct_enabled(void **state) +{ + xlator_t *xl; + void *mem; + size_t size; + uint32_t type; + + // Initialize xl + xl = helper_xlator_init(10); + assert_int_equal(xl->ctx->mem_acct_enable, 0); + xl->ctx->mem_acct_enable = 1; + + // For line mem-pool.c:115 and mem-pool:118 + will_always_return(__glusterfs_this_location, &xl); + + // Call __gf_malloc then realloc + size = 1024; + type = 3; + mem = __gf_malloc(size, type); + assert_non_null(mem); + memset(mem, 0xA5, size); + + size = 2048; + mem = __gf_realloc(mem, size); + assert_non_null(mem); + memset(mem, 0x5A, size); + + // Check xl values + // + // :TODO: This is really weird. I would have expected + // xl to only have a size equal to that of the realloc + // not to the realloc + the malloc. + // Is this a bug? + // + assert_int_equal(xl->mem_acct.rec[type].size, size+1024); + assert_int_equal(xl->mem_acct.rec[type].num_allocs, 2); + assert_int_equal(xl->mem_acct.rec[type].total_allocs, 2); + assert_int_equal(xl->mem_acct.rec[type].max_size, size+1024); + assert_int_equal(xl->mem_acct.rec[type].max_num_allocs, 2); + + // Check memory + helper_check_memory_headers(mem - sizeof(mem_header_t), xl, size, type); + free(mem - sizeof(mem_header_t)); + helper_xlator_destroy(xl); +} + +static void +test_gf_realloc_ptr(void **state) +{ + xlator_t *xl; + void *mem; + size_t size; + + // Initialize xl + xl = helper_xlator_init(10); + assert_int_equal(xl->ctx->mem_acct_enable, 0); + + // For line mem-pool.c:115 and mem-pool:118 + will_always_return(__glusterfs_this_location, &xl); + + // Tests according to the manpage for realloc + + // Like a malloc + size = 1024; + mem = __gf_realloc(NULL, size); + assert_non_null(mem); + memset(mem, 0xA5, size); + + // Like a free + mem = __gf_realloc(mem, 0); + assert_null(mem); + + // Now enable xl context + xl->ctx->mem_acct_enable = 1; + expect_assert_failure(__gf_realloc(NULL, size)); + + helper_xlator_destroy(xl); +} + +int main(void) { + const UnitTest tests[] = { + unit_test(test_gf_mem_acct_enable_set), + unit_test(test_gf_mem_set_acct_info_asserts), + unit_test(test_gf_mem_set_acct_info_memory), + unit_test(test_gf_calloc_default_calloc), + unit_test(test_gf_calloc_mem_acct_enabled), + unit_test(test_gf_malloc_default_malloc), + unit_test(test_gf_malloc_mem_acct_enabled), + unit_test(test_gf_realloc_default_realloc), + unit_test(test_gf_realloc_mem_acct_enabled), + unit_test(test_gf_realloc_ptr), + }; + + return run_tests(tests, "libglusterfs_mem_pool"); +} -- cgit