#include "test/jemalloc_test.h" #include "jemalloc/internal/conf.h" #include "jemalloc/internal/malloc_io.h" static unsigned conf_error_message_count; static void conf_error_message(void *opaque, const char *s) { (void)opaque; (void)s; conf_error_message_count++; } static void expect_conf_error(const char *key, bool expect_message, bool expect_error) { write_cb_t *saved_malloc_message = malloc_message_set(conf_error_message); conf_error_message_count = 0; had_conf_error = false; conf_error("Invalid conf pair", key, strlen(key), "value", sizeof("value") - 1); malloc_message_set(saved_malloc_message); expect_u_eq(conf_error_message_count, expect_message ? 1 : 0, "Unexpected diagnostic count for %s", key); expect_b_eq(had_conf_error, expect_error, "Unexpected error state for %s", key); } 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 TEST_BEGIN(test_conf_error_deprecated) { const struct { const char *name; bool expect_message; bool expect_error; } options[] = {{"experimental_tcache_gc", false, false}, {"hpa_sec_bytes_after_flush", false, false}, {"hpa_sec_batch_fill_extra", false, false}, {"lg_tcache_nslots_mul", false, false}, {"tcache_nslots_small_min", false, false}, {"tcache_nslots_small_max", false, false}, {"tcache_nslots_large", false, false}, {"tcache_gc_delay_bytes", false, false}, {"lg_tcache_flush_small_div", false, false}, {"lg_tcache_flush_large_div", false, false}, {"invalid", true, true}}; for (unsigned i = 0; i < sizeof(options) / sizeof(options[0]); i++) { expect_conf_error(options[i].name, options[i].expect_message, options[i].expect_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, test_conf_error_deprecated); }