diff --git a/Makefile.in b/Makefile.in index 1a7207e0..459f98fb 100644 --- a/Makefile.in +++ b/Makefile.in @@ -216,6 +216,11 @@ TESTS_UNIT := \ $(srcroot)test/unit/buf_writer.c \ $(srcroot)test/unit/cache_bin.c \ $(srcroot)test/unit/ckh.c \ + $(srcroot)test/unit/conf.c \ + $(srcroot)test/unit/conf_init_0.c \ + $(srcroot)test/unit/conf_init_1.c \ + $(srcroot)test/unit/conf_init_confirm.c \ + $(srcroot)test/unit/conf_parse.c \ $(srcroot)test/unit/counter.c \ $(srcroot)test/unit/decay.c \ $(srcroot)test/unit/div.c \ diff --git a/test/unit/conf.c b/test/unit/conf.c new file mode 100644 index 00000000..1a1cde7c --- /dev/null +++ b/test/unit/conf.c @@ -0,0 +1,113 @@ +#include "test/jemalloc_test.h" + +#include "jemalloc/internal/conf.h" + +TEST_BEGIN(test_conf_next_simple) { + const char *opts = "key:value"; + const char *k; + size_t klen; + const char *v; + size_t vlen; + + had_conf_error = false; + bool end = conf_next(&opts, &k, &klen, &v, &vlen); + expect_false(end, "Should not be at end"); + expect_zu_eq(klen, 3, "Key length should be 3"); + expect_false(strncmp(k, "key", klen), "Key should be \"key\""); + expect_zu_eq(vlen, 5, "Value length should be 5"); + expect_false(strncmp(v, "value", vlen), "Value should be \"value\""); + expect_false(had_conf_error, "Should not have had an error"); +} +TEST_END + +TEST_BEGIN(test_conf_next_multi) { + const char *opts = "k1:v1,k2:v2"; + const char *k; + size_t klen; + const char *v; + size_t vlen; + bool end; + + had_conf_error = false; + + end = conf_next(&opts, &k, &klen, &v, &vlen); + expect_false(end, "Should not be at end after first pair"); + expect_zu_eq(klen, 2, "First key length should be 2"); + expect_false(strncmp(k, "k1", klen), "First key should be \"k1\""); + expect_zu_eq(vlen, 2, "First value length should be 2"); + expect_false(strncmp(v, "v1", vlen), "First value should be \"v1\""); + + end = conf_next(&opts, &k, &klen, &v, &vlen); + expect_false(end, "Should not be at end after second pair"); + expect_zu_eq(klen, 2, "Second key length should be 2"); + expect_false(strncmp(k, "k2", klen), "Second key should be \"k2\""); + expect_zu_eq(vlen, 2, "Second value length should be 2"); + expect_false(strncmp(v, "v2", vlen), "Second value should be \"v2\""); + + expect_false(had_conf_error, "Should not have had an error"); +} +TEST_END + +TEST_BEGIN(test_conf_next_empty) { + const char *opts = ""; + const char *k; + size_t klen; + const char *v; + size_t vlen; + + had_conf_error = false; + bool end = conf_next(&opts, &k, &klen, &v, &vlen); + expect_true(end, "Empty string should return true (end)"); + expect_false(had_conf_error, "Empty string should not set error"); +} +TEST_END + +TEST_BEGIN(test_conf_next_missing_value) { + const char *opts = "key_only"; + const char *k; + size_t klen; + const char *v; + size_t vlen; + + had_conf_error = false; + bool end = conf_next(&opts, &k, &klen, &v, &vlen); + expect_true(end, "Key without value should return true (end)"); + expect_true(had_conf_error, "Key without value should set error"); +} +TEST_END + +TEST_BEGIN(test_conf_next_malformed) { + const char *opts = "bad!key:val"; + const char *k; + size_t klen; + const char *v; + size_t vlen; + + had_conf_error = false; + bool end = conf_next(&opts, &k, &klen, &v, &vlen); + expect_true(end, "Malformed key should return true (end)"); + expect_true(had_conf_error, "Malformed key should set error"); +} +TEST_END + +TEST_BEGIN(test_conf_next_trailing_comma) { + const char *opts = "k:v,"; + const char *k; + size_t klen; + const char *v; + size_t vlen; + + had_conf_error = false; + bool end = conf_next(&opts, &k, &klen, &v, &vlen); + expect_false(end, "Should parse the first pair successfully"); + expect_true(had_conf_error, + "Trailing comma should set error"); +} +TEST_END + +int +main(void) { + return test(test_conf_next_simple, test_conf_next_multi, + test_conf_next_empty, test_conf_next_missing_value, + test_conf_next_malformed, test_conf_next_trailing_comma); +} diff --git a/test/unit/conf_init_0.c b/test/unit/conf_init_0.c new file mode 100644 index 00000000..a1f0e63f --- /dev/null +++ b/test/unit/conf_init_0.c @@ -0,0 +1,22 @@ +#include "test/jemalloc_test.h" + +TEST_BEGIN(test_default_dirty_decay_ms) { +#ifdef _WIN32 + test_skip("not supported on win32"); +#endif + + ssize_t dirty_decay_ms; + size_t sz = sizeof(dirty_decay_ms); + + int err = mallctl("opt.dirty_decay_ms", &dirty_decay_ms, &sz, NULL, 0); + assert_d_eq(err, 0, "Unexpected mallctl failure"); + expect_zd_eq(dirty_decay_ms, 10000, + "dirty_decay_ms should be the default (10000)" + " when no global variables are set"); +} +TEST_END + +int +main(void) { + return test(test_default_dirty_decay_ms); +} diff --git a/test/unit/conf_init_1.c b/test/unit/conf_init_1.c new file mode 100644 index 00000000..07aec5dc --- /dev/null +++ b/test/unit/conf_init_1.c @@ -0,0 +1,23 @@ +#include "test/jemalloc_test.h" + +const char *malloc_conf = "dirty_decay_ms:1234"; + +TEST_BEGIN(test_malloc_conf_dirty_decay_ms) { +#ifdef _WIN32 + test_skip("not supported on win32"); +#endif + + ssize_t dirty_decay_ms; + size_t sz = sizeof(dirty_decay_ms); + + int err = mallctl("opt.dirty_decay_ms", &dirty_decay_ms, &sz, NULL, 0); + assert_d_eq(err, 0, "Unexpected mallctl failure"); + expect_zd_eq(dirty_decay_ms, 1234, + "dirty_decay_ms should be 1234 (set via malloc_conf)"); +} +TEST_END + +int +main(void) { + return test(test_malloc_conf_dirty_decay_ms); +} diff --git a/test/unit/conf_init_confirm.c b/test/unit/conf_init_confirm.c new file mode 100644 index 00000000..a4358359 --- /dev/null +++ b/test/unit/conf_init_confirm.c @@ -0,0 +1,39 @@ +#include "test/jemalloc_test.h" + +const char *malloc_conf = "dirty_decay_ms:1234,confirm_conf:true"; + +TEST_BEGIN(test_confirm_conf_two_pass) { +#ifdef _WIN32 + test_skip("not supported on win32"); +#endif + + bool confirm_conf; + size_t sz = sizeof(confirm_conf); + + int err = mallctl("opt.confirm_conf", &confirm_conf, &sz, NULL, 0); + assert_d_eq(err, 0, "Unexpected mallctl failure"); + expect_true(confirm_conf, + "confirm_conf should be true (processed in pass 1)"); +} +TEST_END + +TEST_BEGIN(test_conf_option_applied_in_second_pass) { +#ifdef _WIN32 + test_skip("not supported on win32"); +#endif + + ssize_t dirty_decay_ms; + size_t sz = sizeof(dirty_decay_ms); + + int err = mallctl("opt.dirty_decay_ms", &dirty_decay_ms, &sz, NULL, 0); + assert_d_eq(err, 0, "Unexpected mallctl failure"); + expect_zd_eq(dirty_decay_ms, 1234, + "dirty_decay_ms should be 1234 (processed in pass 2)"); +} +TEST_END + +int +main(void) { + return test(test_confirm_conf_two_pass, + test_conf_option_applied_in_second_pass); +} diff --git a/test/unit/conf_parse.c b/test/unit/conf_parse.c new file mode 100644 index 00000000..b3fedb40 --- /dev/null +++ b/test/unit/conf_parse.c @@ -0,0 +1,130 @@ +#include "test/jemalloc_test.h" + +#include "jemalloc/internal/conf.h" + +TEST_BEGIN(test_conf_handle_bool_true) { + bool result = false; + bool err = conf_handle_bool("true", sizeof("true") - 1, &result); + expect_false(err, "conf_handle_bool should succeed for \"true\""); + expect_true(result, "result should be true"); +} +TEST_END + +TEST_BEGIN(test_conf_handle_bool_false) { + bool result = true; + bool err = conf_handle_bool("false", sizeof("false") - 1, &result); + expect_false(err, "conf_handle_bool should succeed for \"false\""); + expect_false(result, "result should be false"); +} +TEST_END + +TEST_BEGIN(test_conf_handle_bool_invalid) { + bool result = false; + bool err = conf_handle_bool("yes", sizeof("yes") - 1, &result); + expect_true(err, "conf_handle_bool should fail for \"yes\""); +} +TEST_END + +TEST_BEGIN(test_conf_handle_unsigned_in_range) { + uintmax_t result = 0; + bool err = conf_handle_unsigned("100", sizeof("100") - 1, + 1, 2048, true, true, true, &result); + expect_false(err, "Should succeed for in-range value"); + expect_u64_eq((uint64_t)result, 100, "result should be 100"); +} +TEST_END + +TEST_BEGIN(test_conf_handle_unsigned_clip_max) { + uintmax_t result = 0; + bool err = conf_handle_unsigned("9999", sizeof("9999") - 1, + 1, 2048, true, true, true, &result); + expect_false(err, "Should succeed with clipping"); + expect_u64_eq((uint64_t)result, 2048, + "result should be clipped to max 2048"); +} +TEST_END + +TEST_BEGIN(test_conf_handle_unsigned_clip_min) { + uintmax_t result = 0; + bool err = conf_handle_unsigned("0", sizeof("0") - 1, + 1, 2048, true, true, true, &result); + expect_false(err, "Should succeed with clipping"); + expect_u64_eq((uint64_t)result, 1, + "result should be clipped to min 1"); +} +TEST_END + +TEST_BEGIN(test_conf_handle_unsigned_no_clip_reject) { + uintmax_t result = 0; + bool err = conf_handle_unsigned("9999", sizeof("9999") - 1, + 1, 2048, true, true, false, &result); + expect_true(err, "Should fail for out-of-range value without clip"); +} +TEST_END + +TEST_BEGIN(test_conf_handle_unsigned_invalid) { + uintmax_t result = 0; + bool err = conf_handle_unsigned("abc", sizeof("abc") - 1, + 1, 2048, true, true, true, &result); + expect_true(err, "Should fail for non-numeric input"); +} +TEST_END + +TEST_BEGIN(test_conf_handle_signed_valid) { + intmax_t result = 0; + bool err = conf_handle_signed("5000", sizeof("5000") - 1, + -1, INTMAX_MAX, true, false, false, &result); + expect_false(err, "Should succeed for valid value"); + expect_d64_eq((int64_t)result, 5000, "result should be 5000"); +} +TEST_END + +TEST_BEGIN(test_conf_handle_signed_negative) { + intmax_t result = 0; + bool err = conf_handle_signed("-1", sizeof("-1") - 1, + -1, INTMAX_MAX, true, false, false, &result); + expect_false(err, "Should succeed for -1"); + expect_d64_eq((int64_t)result, -1, "result should be -1"); +} +TEST_END + +TEST_BEGIN(test_conf_handle_signed_out_of_range) { + intmax_t result = 0; + bool err = conf_handle_signed("5000", sizeof("5000") - 1, + -1, 4999, true, true, false, &result); + expect_true(err, "Should fail for out-of-range value"); +} +TEST_END + +TEST_BEGIN(test_conf_handle_char_p) { + char buf[8]; + bool err; + + /* Normal copy. */ + err = conf_handle_char_p("hello", sizeof("hello") - 1, buf, sizeof(buf)); + expect_false(err, "Should succeed"); + expect_str_eq(buf, "hello", "Should copy string"); + + /* Truncation. */ + err = conf_handle_char_p("longstring", sizeof("longstring") - 1, + buf, sizeof(buf)); + expect_false(err, "Should succeed even when truncating"); + expect_str_eq(buf, "longstr", "Should truncate to dest_sz - 1"); +} +TEST_END + +int +main(void) { + return test(test_conf_handle_bool_true, + test_conf_handle_bool_false, + test_conf_handle_bool_invalid, + test_conf_handle_unsigned_in_range, + test_conf_handle_unsigned_clip_max, + test_conf_handle_unsigned_clip_min, + test_conf_handle_unsigned_no_clip_reject, + test_conf_handle_unsigned_invalid, + test_conf_handle_signed_valid, + test_conf_handle_signed_negative, + test_conf_handle_signed_out_of_range, + test_conf_handle_char_p); +} diff --git a/test/unit/malloc_conf_2.c b/test/unit/malloc_conf_2.c index 023b7102..667e7006 100644 --- a/test/unit/malloc_conf_2.c +++ b/test/unit/malloc_conf_2.c @@ -1,6 +1,6 @@ #include "test/jemalloc_test.h" -const char *malloc_conf = "dirty_decay_ms:1000"; +const char *malloc_conf = "dirty_decay_ms:1000,muzzy_decay_ms:2000"; const char *malloc_conf_2_conf_harder = "dirty_decay_ms:1234"; TEST_BEGIN(test_malloc_conf_2) { @@ -49,7 +49,27 @@ TEST_BEGIN(test_mallctl_global_var) { } TEST_END +TEST_BEGIN(test_non_conflicting_var) { +#ifdef _WIN32 + bool windows = true; +#else + bool windows = false; +#endif + /* Windows doesn't support weak symbol linker trickery. */ + test_skip_if(windows); + + ssize_t muzzy_decay_ms; + size_t sz = sizeof(muzzy_decay_ms); + + int err = mallctl("opt.muzzy_decay_ms", &muzzy_decay_ms, &sz, NULL, 0); + assert_d_eq(err, 0, "Unexpected mallctl failure"); + expect_zd_eq(muzzy_decay_ms, 2000, + "Non-conflicting option from malloc_conf should pass through"); +} +TEST_END + int main(void) { - return test(test_malloc_conf_2, test_mallctl_global_var); + return test(test_malloc_conf_2, test_mallctl_global_var, + test_non_conflicting_var); }