Migrate dict.c unit tests to new framework (#946)
This PR migrates the tests related to dict into new test framework as part of #428. Signed-off-by: haoqixu <hq.xu0o0@gmail.com> Signed-off-by: Binbin <binloveplay1314@qq.com> Co-authored-by: Binbin <binloveplay1314@qq.com>
This commit is contained in:
parent
14016d2df7
commit
20d583f774
266
src/dict.c
266
src/dict.c
@ -1828,269 +1828,3 @@ void dictGetStats(char *buf, size_t bufsize, dict *d, int full) {
|
|||||||
/* Make sure there is a NULL term at the end. */
|
/* Make sure there is a NULL term at the end. */
|
||||||
orig_buf[orig_bufsize - 1] = '\0';
|
orig_buf[orig_bufsize - 1] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------- Benchmark ---------------------------------*/
|
|
||||||
|
|
||||||
#ifdef SERVER_TEST
|
|
||||||
#include "testhelp.h"
|
|
||||||
|
|
||||||
#define TEST(name) printf("test — %s\n", name);
|
|
||||||
|
|
||||||
uint64_t hashCallback(const void *key) {
|
|
||||||
return dictGenHashFunction((unsigned char *)key, strlen((char *)key));
|
|
||||||
}
|
|
||||||
|
|
||||||
int compareCallback(dict *d, const void *key1, const void *key2) {
|
|
||||||
int l1, l2;
|
|
||||||
UNUSED(d);
|
|
||||||
|
|
||||||
l1 = strlen((char *)key1);
|
|
||||||
l2 = strlen((char *)key2);
|
|
||||||
if (l1 != l2) return 0;
|
|
||||||
return memcmp(key1, key2, l1) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void freeCallback(dict *d, void *val) {
|
|
||||||
UNUSED(d);
|
|
||||||
|
|
||||||
zfree(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *stringFromLongLong(long long value) {
|
|
||||||
char buf[32];
|
|
||||||
int len;
|
|
||||||
char *s;
|
|
||||||
|
|
||||||
len = snprintf(buf, sizeof(buf), "%lld", value);
|
|
||||||
s = zmalloc(len + 1);
|
|
||||||
memcpy(s, buf, len);
|
|
||||||
s[len] = '\0';
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
dictType BenchmarkDictType = {hashCallback, NULL, compareCallback, freeCallback, NULL, NULL};
|
|
||||||
|
|
||||||
#define start_benchmark() start = timeInMilliseconds()
|
|
||||||
#define end_benchmark(msg) \
|
|
||||||
do { \
|
|
||||||
elapsed = timeInMilliseconds() - start; \
|
|
||||||
printf(msg ": %ld items in %lld ms\n", count, elapsed); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
/* ./valkey-server test dict [<count> | --accurate] */
|
|
||||||
int dictTest(int argc, char **argv, int flags) {
|
|
||||||
long j;
|
|
||||||
long long start, elapsed;
|
|
||||||
int retval;
|
|
||||||
dict *dict = dictCreate(&BenchmarkDictType);
|
|
||||||
long count = 0;
|
|
||||||
unsigned long new_dict_size, current_dict_used, remain_keys;
|
|
||||||
int accurate = (flags & TEST_ACCURATE);
|
|
||||||
|
|
||||||
if (argc == 4) {
|
|
||||||
if (accurate) {
|
|
||||||
count = 5000000;
|
|
||||||
} else {
|
|
||||||
count = strtol(argv[3], NULL, 10);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
count = 5000;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST("Add 16 keys and verify dict resize is ok") {
|
|
||||||
dictSetResizeEnabled(DICT_RESIZE_ENABLE);
|
|
||||||
for (j = 0; j < 16; j++) {
|
|
||||||
retval = dictAdd(dict, stringFromLongLong(j), (void *)j);
|
|
||||||
assert(retval == DICT_OK);
|
|
||||||
}
|
|
||||||
while (dictIsRehashing(dict)) dictRehashMicroseconds(dict, 1000);
|
|
||||||
assert(dictSize(dict) == 16);
|
|
||||||
assert(dictBuckets(dict) == 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST("Use DICT_RESIZE_AVOID to disable the dict resize and pad to (dict_force_resize_ratio * 16)") {
|
|
||||||
/* Use DICT_RESIZE_AVOID to disable the dict resize, and pad
|
|
||||||
* the number of keys to (dict_force_resize_ratio * 16), so we can satisfy
|
|
||||||
* dict_force_resize_ratio in next test. */
|
|
||||||
dictSetResizeEnabled(DICT_RESIZE_AVOID);
|
|
||||||
for (j = 16; j < (long)dict_force_resize_ratio * 16; j++) {
|
|
||||||
retval = dictAdd(dict, stringFromLongLong(j), (void *)j);
|
|
||||||
assert(retval == DICT_OK);
|
|
||||||
}
|
|
||||||
current_dict_used = dict_force_resize_ratio * 16;
|
|
||||||
assert(dictSize(dict) == current_dict_used);
|
|
||||||
assert(dictBuckets(dict) == 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST("Add one more key, trigger the dict resize") {
|
|
||||||
retval = dictAdd(dict, stringFromLongLong(current_dict_used), (void *)(current_dict_used));
|
|
||||||
assert(retval == DICT_OK);
|
|
||||||
current_dict_used++;
|
|
||||||
new_dict_size = 1UL << _dictNextExp(current_dict_used);
|
|
||||||
assert(dictSize(dict) == current_dict_used);
|
|
||||||
assert(DICTHT_SIZE(dict->ht_size_exp[0]) == 16);
|
|
||||||
assert(DICTHT_SIZE(dict->ht_size_exp[1]) == new_dict_size);
|
|
||||||
|
|
||||||
/* Wait for rehashing. */
|
|
||||||
dictSetResizeEnabled(DICT_RESIZE_ENABLE);
|
|
||||||
while (dictIsRehashing(dict)) dictRehashMicroseconds(dict, 1000);
|
|
||||||
assert(dictSize(dict) == current_dict_used);
|
|
||||||
assert(DICTHT_SIZE(dict->ht_size_exp[0]) == new_dict_size);
|
|
||||||
assert(DICTHT_SIZE(dict->ht_size_exp[1]) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST("Delete keys until we can trigger shrink in next test") {
|
|
||||||
/* Delete keys until we can satisfy (1 / HASHTABLE_MIN_FILL) in the next test. */
|
|
||||||
for (j = new_dict_size / HASHTABLE_MIN_FILL + 1; j < (long)current_dict_used; j++) {
|
|
||||||
char *key = stringFromLongLong(j);
|
|
||||||
retval = dictDelete(dict, key);
|
|
||||||
zfree(key);
|
|
||||||
assert(retval == DICT_OK);
|
|
||||||
}
|
|
||||||
current_dict_used = new_dict_size / HASHTABLE_MIN_FILL + 1;
|
|
||||||
assert(dictSize(dict) == current_dict_used);
|
|
||||||
assert(DICTHT_SIZE(dict->ht_size_exp[0]) == new_dict_size);
|
|
||||||
assert(DICTHT_SIZE(dict->ht_size_exp[1]) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST("Delete one more key, trigger the dict resize") {
|
|
||||||
current_dict_used--;
|
|
||||||
char *key = stringFromLongLong(current_dict_used);
|
|
||||||
retval = dictDelete(dict, key);
|
|
||||||
zfree(key);
|
|
||||||
unsigned long oldDictSize = new_dict_size;
|
|
||||||
new_dict_size = 1UL << _dictNextExp(current_dict_used);
|
|
||||||
assert(retval == DICT_OK);
|
|
||||||
assert(dictSize(dict) == current_dict_used);
|
|
||||||
assert(DICTHT_SIZE(dict->ht_size_exp[0]) == oldDictSize);
|
|
||||||
assert(DICTHT_SIZE(dict->ht_size_exp[1]) == new_dict_size);
|
|
||||||
|
|
||||||
/* Wait for rehashing. */
|
|
||||||
while (dictIsRehashing(dict)) dictRehashMicroseconds(dict, 1000);
|
|
||||||
assert(dictSize(dict) == current_dict_used);
|
|
||||||
assert(DICTHT_SIZE(dict->ht_size_exp[0]) == new_dict_size);
|
|
||||||
assert(DICTHT_SIZE(dict->ht_size_exp[1]) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST("Empty the dictionary and add 128 keys") {
|
|
||||||
dictEmpty(dict, NULL);
|
|
||||||
for (j = 0; j < 128; j++) {
|
|
||||||
retval = dictAdd(dict, stringFromLongLong(j), (void *)j);
|
|
||||||
assert(retval == DICT_OK);
|
|
||||||
}
|
|
||||||
while (dictIsRehashing(dict)) dictRehashMicroseconds(dict, 1000);
|
|
||||||
assert(dictSize(dict) == 128);
|
|
||||||
assert(dictBuckets(dict) == 128);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST("Use DICT_RESIZE_AVOID to disable the dict resize and reduce to 3") {
|
|
||||||
/* Use DICT_RESIZE_AVOID to disable the dict reset, and reduce
|
|
||||||
* the number of keys until we can trigger shrinking in next test. */
|
|
||||||
dictSetResizeEnabled(DICT_RESIZE_AVOID);
|
|
||||||
remain_keys = DICTHT_SIZE(dict->ht_size_exp[0]) / (HASHTABLE_MIN_FILL * dict_force_resize_ratio) + 1;
|
|
||||||
for (j = remain_keys; j < 128; j++) {
|
|
||||||
char *key = stringFromLongLong(j);
|
|
||||||
retval = dictDelete(dict, key);
|
|
||||||
zfree(key);
|
|
||||||
assert(retval == DICT_OK);
|
|
||||||
}
|
|
||||||
current_dict_used = remain_keys;
|
|
||||||
assert(dictSize(dict) == remain_keys);
|
|
||||||
assert(dictBuckets(dict) == 128);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST("Delete one more key, trigger the dict resize") {
|
|
||||||
current_dict_used--;
|
|
||||||
char *key = stringFromLongLong(current_dict_used);
|
|
||||||
retval = dictDelete(dict, key);
|
|
||||||
zfree(key);
|
|
||||||
new_dict_size = 1UL << _dictNextExp(current_dict_used);
|
|
||||||
assert(retval == DICT_OK);
|
|
||||||
assert(dictSize(dict) == current_dict_used);
|
|
||||||
assert(DICTHT_SIZE(dict->ht_size_exp[0]) == 128);
|
|
||||||
assert(DICTHT_SIZE(dict->ht_size_exp[1]) == new_dict_size);
|
|
||||||
|
|
||||||
/* Wait for rehashing. */
|
|
||||||
dictSetResizeEnabled(DICT_RESIZE_ENABLE);
|
|
||||||
while (dictIsRehashing(dict)) dictRehashMicroseconds(dict, 1000);
|
|
||||||
assert(dictSize(dict) == current_dict_used);
|
|
||||||
assert(DICTHT_SIZE(dict->ht_size_exp[0]) == new_dict_size);
|
|
||||||
assert(DICTHT_SIZE(dict->ht_size_exp[1]) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST("Restore to original state") {
|
|
||||||
dictEmpty(dict, NULL);
|
|
||||||
dictSetResizeEnabled(DICT_RESIZE_ENABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
start_benchmark();
|
|
||||||
for (j = 0; j < count; j++) {
|
|
||||||
retval = dictAdd(dict, stringFromLongLong(j), (void *)j);
|
|
||||||
assert(retval == DICT_OK);
|
|
||||||
}
|
|
||||||
end_benchmark("Inserting");
|
|
||||||
assert((long)dictSize(dict) == count);
|
|
||||||
|
|
||||||
/* Wait for rehashing. */
|
|
||||||
while (dictIsRehashing(dict)) {
|
|
||||||
dictRehashMicroseconds(dict, 100 * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
start_benchmark();
|
|
||||||
for (j = 0; j < count; j++) {
|
|
||||||
char *key = stringFromLongLong(j);
|
|
||||||
dictEntry *de = dictFind(dict, key);
|
|
||||||
assert(de != NULL);
|
|
||||||
zfree(key);
|
|
||||||
}
|
|
||||||
end_benchmark("Linear access of existing elements");
|
|
||||||
|
|
||||||
start_benchmark();
|
|
||||||
for (j = 0; j < count; j++) {
|
|
||||||
char *key = stringFromLongLong(j);
|
|
||||||
dictEntry *de = dictFind(dict, key);
|
|
||||||
assert(de != NULL);
|
|
||||||
zfree(key);
|
|
||||||
}
|
|
||||||
end_benchmark("Linear access of existing elements (2nd round)");
|
|
||||||
|
|
||||||
start_benchmark();
|
|
||||||
for (j = 0; j < count; j++) {
|
|
||||||
char *key = stringFromLongLong(rand() % count);
|
|
||||||
dictEntry *de = dictFind(dict, key);
|
|
||||||
assert(de != NULL);
|
|
||||||
zfree(key);
|
|
||||||
}
|
|
||||||
end_benchmark("Random access of existing elements");
|
|
||||||
|
|
||||||
start_benchmark();
|
|
||||||
for (j = 0; j < count; j++) {
|
|
||||||
dictEntry *de = dictGetRandomKey(dict);
|
|
||||||
assert(de != NULL);
|
|
||||||
}
|
|
||||||
end_benchmark("Accessing random keys");
|
|
||||||
|
|
||||||
start_benchmark();
|
|
||||||
for (j = 0; j < count; j++) {
|
|
||||||
char *key = stringFromLongLong(rand() % count);
|
|
||||||
key[0] = 'X';
|
|
||||||
dictEntry *de = dictFind(dict, key);
|
|
||||||
assert(de == NULL);
|
|
||||||
zfree(key);
|
|
||||||
}
|
|
||||||
end_benchmark("Accessing missing");
|
|
||||||
|
|
||||||
start_benchmark();
|
|
||||||
for (j = 0; j < count; j++) {
|
|
||||||
char *key = stringFromLongLong(j);
|
|
||||||
retval = dictDelete(dict, key);
|
|
||||||
assert(retval == DICT_OK);
|
|
||||||
key[0] += 17; /* Change first number to letter. */
|
|
||||||
retval = dictAdd(dict, key, (void *)j);
|
|
||||||
assert(retval == DICT_OK);
|
|
||||||
}
|
|
||||||
end_benchmark("Removing and adding");
|
|
||||||
dictRelease(dict);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
@ -250,8 +250,4 @@ dictStats *dictGetStatsHt(dict *d, int htidx, int full);
|
|||||||
void dictCombineStats(dictStats *from, dictStats *into);
|
void dictCombineStats(dictStats *from, dictStats *into);
|
||||||
void dictFreeStats(dictStats *stats);
|
void dictFreeStats(dictStats *stats);
|
||||||
|
|
||||||
#ifdef SERVER_TEST
|
|
||||||
int dictTest(int argc, char *argv[], int flags);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __DICT_H */
|
#endif /* __DICT_H */
|
||||||
|
@ -6698,7 +6698,6 @@ struct serverTest {
|
|||||||
int failed;
|
int failed;
|
||||||
} serverTests[] = {
|
} serverTests[] = {
|
||||||
{"quicklist", quicklistTest},
|
{"quicklist", quicklistTest},
|
||||||
{"dict", dictTest},
|
|
||||||
};
|
};
|
||||||
serverTestProc *getTestProcByName(const char *name) {
|
serverTestProc *getTestProcByName(const char *name) {
|
||||||
int numtests = sizeof(serverTests) / sizeof(struct serverTest);
|
int numtests = sizeof(serverTests) / sizeof(struct serverTest);
|
||||||
|
331
src/unit/test_dict.c
Normal file
331
src/unit/test_dict.c
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
#include "../dict.c"
|
||||||
|
#include "test_help.h"
|
||||||
|
|
||||||
|
uint64_t hashCallback(const void *key) {
|
||||||
|
return dictGenHashFunction((unsigned char *)key, strlen((char *)key));
|
||||||
|
}
|
||||||
|
|
||||||
|
int compareCallback(dict *d, const void *key1, const void *key2) {
|
||||||
|
int l1, l2;
|
||||||
|
UNUSED(d);
|
||||||
|
|
||||||
|
l1 = strlen((char *)key1);
|
||||||
|
l2 = strlen((char *)key2);
|
||||||
|
if (l1 != l2) return 0;
|
||||||
|
return memcmp(key1, key2, l1) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeCallback(dict *d, void *val) {
|
||||||
|
UNUSED(d);
|
||||||
|
|
||||||
|
zfree(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *stringFromLongLong(long long value) {
|
||||||
|
char buf[32];
|
||||||
|
int len;
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
len = snprintf(buf, sizeof(buf), "%lld", value);
|
||||||
|
s = zmalloc(len + 1);
|
||||||
|
memcpy(s, buf, len);
|
||||||
|
s[len] = '\0';
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
dictType BenchmarkDictType = {hashCallback, NULL, compareCallback, freeCallback, NULL, NULL};
|
||||||
|
|
||||||
|
#define start_benchmark() start = timeInMilliseconds()
|
||||||
|
#define end_benchmark(msg) \
|
||||||
|
do { \
|
||||||
|
elapsed = timeInMilliseconds() - start; \
|
||||||
|
printf(msg ": %ld items in %lld ms\n", count, elapsed); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static dict *_dict = NULL;
|
||||||
|
static long j;
|
||||||
|
static int retval;
|
||||||
|
static unsigned long new_dict_size, current_dict_used, remain_keys;
|
||||||
|
|
||||||
|
int test_dictCreate(int argc, char **argv, int flags) {
|
||||||
|
_dict = dictCreate(&BenchmarkDictType);
|
||||||
|
|
||||||
|
UNUSED(argc);
|
||||||
|
UNUSED(argv);
|
||||||
|
UNUSED(flags);
|
||||||
|
|
||||||
|
monotonicInit(); /* Required for dict tests, that are relying on monotime during dict rehashing. */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_dictAdd16Keys(int argc, char **argv, int flags) {
|
||||||
|
/* Add 16 keys and verify dict resize is ok */
|
||||||
|
UNUSED(argc);
|
||||||
|
UNUSED(argv);
|
||||||
|
UNUSED(flags);
|
||||||
|
|
||||||
|
dictSetResizeEnabled(DICT_RESIZE_ENABLE);
|
||||||
|
for (j = 0; j < 16; j++) {
|
||||||
|
retval = dictAdd(_dict, stringFromLongLong(j), (void *)j);
|
||||||
|
TEST_ASSERT(retval == DICT_OK);
|
||||||
|
}
|
||||||
|
while (dictIsRehashing(_dict)) dictRehashMicroseconds(_dict, 1000);
|
||||||
|
TEST_ASSERT(dictSize(_dict) == 16);
|
||||||
|
TEST_ASSERT(dictBuckets(_dict) == 16);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_dictDisableResize(int argc, char **argv, int flags) {
|
||||||
|
/* Use DICT_RESIZE_AVOID to disable the dict resize and pad to (dict_force_resize_ratio * 16) */
|
||||||
|
UNUSED(argc);
|
||||||
|
UNUSED(argv);
|
||||||
|
UNUSED(flags);
|
||||||
|
|
||||||
|
/* Use DICT_RESIZE_AVOID to disable the dict resize, and pad
|
||||||
|
* the number of keys to (dict_force_resize_ratio * 16), so we can satisfy
|
||||||
|
* dict_force_resize_ratio in next test. */
|
||||||
|
dictSetResizeEnabled(DICT_RESIZE_AVOID);
|
||||||
|
for (j = 16; j < (long)dict_force_resize_ratio * 16; j++) {
|
||||||
|
retval = dictAdd(_dict, stringFromLongLong(j), (void *)j);
|
||||||
|
TEST_ASSERT(retval == DICT_OK);
|
||||||
|
}
|
||||||
|
current_dict_used = dict_force_resize_ratio * 16;
|
||||||
|
TEST_ASSERT(dictSize(_dict) == current_dict_used);
|
||||||
|
TEST_ASSERT(dictBuckets(_dict) == 16);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_dictAddOneKeyTriggerResize(int argc, char **argv, int flags) {
|
||||||
|
/* Add one more key, trigger the dict resize */
|
||||||
|
UNUSED(argc);
|
||||||
|
UNUSED(argv);
|
||||||
|
UNUSED(flags);
|
||||||
|
|
||||||
|
retval = dictAdd(_dict, stringFromLongLong(current_dict_used), (void *)(current_dict_used));
|
||||||
|
TEST_ASSERT(retval == DICT_OK);
|
||||||
|
current_dict_used++;
|
||||||
|
new_dict_size = 1UL << _dictNextExp(current_dict_used);
|
||||||
|
TEST_ASSERT(dictSize(_dict) == current_dict_used);
|
||||||
|
TEST_ASSERT(DICTHT_SIZE(_dict->ht_size_exp[0]) == 16);
|
||||||
|
TEST_ASSERT(DICTHT_SIZE(_dict->ht_size_exp[1]) == new_dict_size);
|
||||||
|
|
||||||
|
/* Wait for rehashing. */
|
||||||
|
dictSetResizeEnabled(DICT_RESIZE_ENABLE);
|
||||||
|
while (dictIsRehashing(_dict)) dictRehashMicroseconds(_dict, 1000);
|
||||||
|
TEST_ASSERT(dictSize(_dict) == current_dict_used);
|
||||||
|
TEST_ASSERT(DICTHT_SIZE(_dict->ht_size_exp[0]) == new_dict_size);
|
||||||
|
TEST_ASSERT(DICTHT_SIZE(_dict->ht_size_exp[1]) == 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_dictDeleteKeys(int argc, char **argv, int flags) {
|
||||||
|
/* Delete keys until we can trigger shrink in next test */
|
||||||
|
UNUSED(argc);
|
||||||
|
UNUSED(argv);
|
||||||
|
UNUSED(flags);
|
||||||
|
|
||||||
|
/* Delete keys until we can satisfy (1 / HASHTABLE_MIN_FILL) in the next test. */
|
||||||
|
for (j = new_dict_size / HASHTABLE_MIN_FILL + 1; j < (long)current_dict_used; j++) {
|
||||||
|
char *key = stringFromLongLong(j);
|
||||||
|
retval = dictDelete(_dict, key);
|
||||||
|
zfree(key);
|
||||||
|
TEST_ASSERT(retval == DICT_OK);
|
||||||
|
}
|
||||||
|
current_dict_used = new_dict_size / HASHTABLE_MIN_FILL + 1;
|
||||||
|
TEST_ASSERT(dictSize(_dict) == current_dict_used);
|
||||||
|
TEST_ASSERT(DICTHT_SIZE(_dict->ht_size_exp[0]) == new_dict_size);
|
||||||
|
TEST_ASSERT(DICTHT_SIZE(_dict->ht_size_exp[1]) == 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_dictDeleteOneKeyTriggerResize(int argc, char **argv, int flags) {
|
||||||
|
/* Delete one more key, trigger the dict resize */
|
||||||
|
UNUSED(argc);
|
||||||
|
UNUSED(argv);
|
||||||
|
UNUSED(flags);
|
||||||
|
|
||||||
|
current_dict_used--;
|
||||||
|
char *key = stringFromLongLong(current_dict_used);
|
||||||
|
retval = dictDelete(_dict, key);
|
||||||
|
zfree(key);
|
||||||
|
unsigned long oldDictSize = new_dict_size;
|
||||||
|
new_dict_size = 1UL << _dictNextExp(current_dict_used);
|
||||||
|
TEST_ASSERT(retval == DICT_OK);
|
||||||
|
TEST_ASSERT(dictSize(_dict) == current_dict_used);
|
||||||
|
TEST_ASSERT(DICTHT_SIZE(_dict->ht_size_exp[0]) == oldDictSize);
|
||||||
|
TEST_ASSERT(DICTHT_SIZE(_dict->ht_size_exp[1]) == new_dict_size);
|
||||||
|
|
||||||
|
/* Wait for rehashing. */
|
||||||
|
while (dictIsRehashing(_dict)) dictRehashMicroseconds(_dict, 1000);
|
||||||
|
TEST_ASSERT(dictSize(_dict) == current_dict_used);
|
||||||
|
TEST_ASSERT(DICTHT_SIZE(_dict->ht_size_exp[0]) == new_dict_size);
|
||||||
|
TEST_ASSERT(DICTHT_SIZE(_dict->ht_size_exp[1]) == 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_dictEmptyDirAdd128Keys(int argc, char **argv, int flags) {
|
||||||
|
/* Empty the dictionary and add 128 keys */
|
||||||
|
UNUSED(argc);
|
||||||
|
UNUSED(argv);
|
||||||
|
UNUSED(flags);
|
||||||
|
|
||||||
|
dictEmpty(_dict, NULL);
|
||||||
|
for (j = 0; j < 128; j++) {
|
||||||
|
retval = dictAdd(_dict, stringFromLongLong(j), (void *)j);
|
||||||
|
TEST_ASSERT(retval == DICT_OK);
|
||||||
|
}
|
||||||
|
while (dictIsRehashing(_dict)) dictRehashMicroseconds(_dict, 1000);
|
||||||
|
TEST_ASSERT(dictSize(_dict) == 128);
|
||||||
|
TEST_ASSERT(dictBuckets(_dict) == 128);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_dictDisableResizeReduceTo3(int argc, char **argv, int flags) {
|
||||||
|
/* Use DICT_RESIZE_AVOID to disable the dict resize and reduce to 3 */
|
||||||
|
UNUSED(argc);
|
||||||
|
UNUSED(argv);
|
||||||
|
UNUSED(flags);
|
||||||
|
|
||||||
|
/* Use DICT_RESIZE_AVOID to disable the dict reset, and reduce
|
||||||
|
* the number of keys until we can trigger shrinking in next test. */
|
||||||
|
dictSetResizeEnabled(DICT_RESIZE_AVOID);
|
||||||
|
remain_keys = DICTHT_SIZE(_dict->ht_size_exp[0]) / (HASHTABLE_MIN_FILL * dict_force_resize_ratio) + 1;
|
||||||
|
for (j = remain_keys; j < 128; j++) {
|
||||||
|
char *key = stringFromLongLong(j);
|
||||||
|
retval = dictDelete(_dict, key);
|
||||||
|
zfree(key);
|
||||||
|
TEST_ASSERT(retval == DICT_OK);
|
||||||
|
}
|
||||||
|
current_dict_used = remain_keys;
|
||||||
|
TEST_ASSERT(dictSize(_dict) == remain_keys);
|
||||||
|
TEST_ASSERT(dictBuckets(_dict) == 128);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_dictDeleteOneKeyTriggerResizeAgain(int argc, char **argv, int flags) {
|
||||||
|
/* Delete one more key, trigger the dict resize */
|
||||||
|
UNUSED(argc);
|
||||||
|
UNUSED(argv);
|
||||||
|
UNUSED(flags);
|
||||||
|
|
||||||
|
current_dict_used--;
|
||||||
|
char *key = stringFromLongLong(current_dict_used);
|
||||||
|
retval = dictDelete(_dict, key);
|
||||||
|
zfree(key);
|
||||||
|
new_dict_size = 1UL << _dictNextExp(current_dict_used);
|
||||||
|
TEST_ASSERT(retval == DICT_OK);
|
||||||
|
TEST_ASSERT(dictSize(_dict) == current_dict_used);
|
||||||
|
TEST_ASSERT(DICTHT_SIZE(_dict->ht_size_exp[0]) == 128);
|
||||||
|
TEST_ASSERT(DICTHT_SIZE(_dict->ht_size_exp[1]) == new_dict_size);
|
||||||
|
|
||||||
|
/* Wait for rehashing. */
|
||||||
|
dictSetResizeEnabled(DICT_RESIZE_ENABLE);
|
||||||
|
while (dictIsRehashing(_dict)) dictRehashMicroseconds(_dict, 1000);
|
||||||
|
TEST_ASSERT(dictSize(_dict) == current_dict_used);
|
||||||
|
TEST_ASSERT(DICTHT_SIZE(_dict->ht_size_exp[0]) == new_dict_size);
|
||||||
|
TEST_ASSERT(DICTHT_SIZE(_dict->ht_size_exp[1]) == 0);
|
||||||
|
|
||||||
|
/* This is the last one, restore to original state */
|
||||||
|
dictRelease(_dict);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_dictBenchmark(int argc, char **argv, int flags) {
|
||||||
|
long j;
|
||||||
|
long long start, elapsed;
|
||||||
|
int retval;
|
||||||
|
dict *dict = dictCreate(&BenchmarkDictType);
|
||||||
|
long count = 0;
|
||||||
|
int accurate = (flags & UNIT_TEST_ACCURATE);
|
||||||
|
|
||||||
|
if (argc == 4) {
|
||||||
|
if (accurate) {
|
||||||
|
count = 5000000;
|
||||||
|
} else {
|
||||||
|
count = strtol(argv[3], NULL, 10);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
count = 5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
monotonicInit(); /* Required for dict tests, that are relying on monotime during dict rehashing. */
|
||||||
|
|
||||||
|
start_benchmark();
|
||||||
|
for (j = 0; j < count; j++) {
|
||||||
|
retval = dictAdd(dict, stringFromLongLong(j), (void *)j);
|
||||||
|
TEST_ASSERT(retval == DICT_OK);
|
||||||
|
}
|
||||||
|
end_benchmark("Inserting");
|
||||||
|
TEST_ASSERT((long)dictSize(dict) == count);
|
||||||
|
|
||||||
|
/* Wait for rehashing. */
|
||||||
|
while (dictIsRehashing(dict)) {
|
||||||
|
dictRehashMicroseconds(dict, 100 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
start_benchmark();
|
||||||
|
for (j = 0; j < count; j++) {
|
||||||
|
char *key = stringFromLongLong(j);
|
||||||
|
dictEntry *de = dictFind(dict, key);
|
||||||
|
TEST_ASSERT(de != NULL);
|
||||||
|
zfree(key);
|
||||||
|
}
|
||||||
|
end_benchmark("Linear access of existing elements");
|
||||||
|
|
||||||
|
start_benchmark();
|
||||||
|
for (j = 0; j < count; j++) {
|
||||||
|
char *key = stringFromLongLong(j);
|
||||||
|
dictEntry *de = dictFind(dict, key);
|
||||||
|
TEST_ASSERT(de != NULL);
|
||||||
|
zfree(key);
|
||||||
|
}
|
||||||
|
end_benchmark("Linear access of existing elements (2nd round)");
|
||||||
|
|
||||||
|
start_benchmark();
|
||||||
|
for (j = 0; j < count; j++) {
|
||||||
|
char *key = stringFromLongLong(rand() % count);
|
||||||
|
dictEntry *de = dictFind(dict, key);
|
||||||
|
TEST_ASSERT(de != NULL);
|
||||||
|
zfree(key);
|
||||||
|
}
|
||||||
|
end_benchmark("Random access of existing elements");
|
||||||
|
|
||||||
|
start_benchmark();
|
||||||
|
for (j = 0; j < count; j++) {
|
||||||
|
dictEntry *de = dictGetRandomKey(dict);
|
||||||
|
TEST_ASSERT(de != NULL);
|
||||||
|
}
|
||||||
|
end_benchmark("Accessing random keys");
|
||||||
|
|
||||||
|
start_benchmark();
|
||||||
|
for (j = 0; j < count; j++) {
|
||||||
|
char *key = stringFromLongLong(rand() % count);
|
||||||
|
key[0] = 'X';
|
||||||
|
dictEntry *de = dictFind(dict, key);
|
||||||
|
TEST_ASSERT(de == NULL);
|
||||||
|
zfree(key);
|
||||||
|
}
|
||||||
|
end_benchmark("Accessing missing");
|
||||||
|
|
||||||
|
start_benchmark();
|
||||||
|
for (j = 0; j < count; j++) {
|
||||||
|
char *key = stringFromLongLong(j);
|
||||||
|
retval = dictDelete(dict, key);
|
||||||
|
TEST_ASSERT(retval == DICT_OK);
|
||||||
|
key[0] += 17; /* Change first number to letter. */
|
||||||
|
retval = dictAdd(dict, key, (void *)j);
|
||||||
|
TEST_ASSERT(retval == DICT_OK);
|
||||||
|
}
|
||||||
|
end_benchmark("Removing and adding");
|
||||||
|
dictRelease(dict);
|
||||||
|
return 0;
|
||||||
|
}
|
@ -9,6 +9,16 @@ typedef struct unitTest {
|
|||||||
|
|
||||||
int test_crc64(int argc, char **argv, int flags);
|
int test_crc64(int argc, char **argv, int flags);
|
||||||
int test_crc64combine(int argc, char **argv, int flags);
|
int test_crc64combine(int argc, char **argv, int flags);
|
||||||
|
int test_dictCreate(int argc, char **argv, int flags);
|
||||||
|
int test_dictAdd16Keys(int argc, char **argv, int flags);
|
||||||
|
int test_dictDisableResize(int argc, char **argv, int flags);
|
||||||
|
int test_dictAddOneKeyTriggerResize(int argc, char **argv, int flags);
|
||||||
|
int test_dictDeleteKeys(int argc, char **argv, int flags);
|
||||||
|
int test_dictDeleteOneKeyTriggerResize(int argc, char **argv, int flags);
|
||||||
|
int test_dictEmptyDirAdd128Keys(int argc, char **argv, int flags);
|
||||||
|
int test_dictDisableResizeReduceTo3(int argc, char **argv, int flags);
|
||||||
|
int test_dictDeleteOneKeyTriggerResizeAgain(int argc, char **argv, int flags);
|
||||||
|
int test_dictBenchmark(int argc, char **argv, int flags);
|
||||||
int test_endianconv(int argc, char *argv[], int flags);
|
int test_endianconv(int argc, char *argv[], int flags);
|
||||||
int test_intsetValueEncodings(int argc, char **argv, int flags);
|
int test_intsetValueEncodings(int argc, char **argv, int flags);
|
||||||
int test_intsetBasicAdding(int argc, char **argv, int flags);
|
int test_intsetBasicAdding(int argc, char **argv, int flags);
|
||||||
@ -130,6 +140,7 @@ int test_zmallocAllocZeroByteAndFree(int argc, char **argv, int flags);
|
|||||||
|
|
||||||
unitTest __test_crc64_c[] = {{"test_crc64", test_crc64}, {NULL, NULL}};
|
unitTest __test_crc64_c[] = {{"test_crc64", test_crc64}, {NULL, NULL}};
|
||||||
unitTest __test_crc64combine_c[] = {{"test_crc64combine", test_crc64combine}, {NULL, NULL}};
|
unitTest __test_crc64combine_c[] = {{"test_crc64combine", test_crc64combine}, {NULL, NULL}};
|
||||||
|
unitTest __test_dict_c[] = {{"test_dictCreate", test_dictCreate}, {"test_dictAdd16Keys", test_dictAdd16Keys}, {"test_dictDisableResize", test_dictDisableResize}, {"test_dictAddOneKeyTriggerResize", test_dictAddOneKeyTriggerResize}, {"test_dictDeleteKeys", test_dictDeleteKeys}, {"test_dictDeleteOneKeyTriggerResize", test_dictDeleteOneKeyTriggerResize}, {"test_dictEmptyDirAdd128Keys", test_dictEmptyDirAdd128Keys}, {"test_dictDisableResizeReduceTo3", test_dictDisableResizeReduceTo3}, {"test_dictDeleteOneKeyTriggerResizeAgain", test_dictDeleteOneKeyTriggerResizeAgain}, {"test_dictBenchmark", test_dictBenchmark}, {NULL, NULL}};
|
||||||
unitTest __test_endianconv_c[] = {{"test_endianconv", test_endianconv}, {NULL, NULL}};
|
unitTest __test_endianconv_c[] = {{"test_endianconv", test_endianconv}, {NULL, NULL}};
|
||||||
unitTest __test_intset_c[] = {{"test_intsetValueEncodings", test_intsetValueEncodings}, {"test_intsetBasicAdding", test_intsetBasicAdding}, {"test_intsetLargeNumberRandomAdd", test_intsetLargeNumberRandomAdd}, {"test_intsetUpgradeFromint16Toint32", test_intsetUpgradeFromint16Toint32}, {"test_intsetUpgradeFromint16Toint64", test_intsetUpgradeFromint16Toint64}, {"test_intsetUpgradeFromint32Toint64", test_intsetUpgradeFromint32Toint64}, {"test_intsetStressLookups", test_intsetStressLookups}, {"test_intsetStressAddDelete", test_intsetStressAddDelete}, {NULL, NULL}};
|
unitTest __test_intset_c[] = {{"test_intsetValueEncodings", test_intsetValueEncodings}, {"test_intsetBasicAdding", test_intsetBasicAdding}, {"test_intsetLargeNumberRandomAdd", test_intsetLargeNumberRandomAdd}, {"test_intsetUpgradeFromint16Toint32", test_intsetUpgradeFromint16Toint32}, {"test_intsetUpgradeFromint16Toint64", test_intsetUpgradeFromint16Toint64}, {"test_intsetUpgradeFromint32Toint64", test_intsetUpgradeFromint32Toint64}, {"test_intsetStressLookups", test_intsetStressLookups}, {"test_intsetStressAddDelete", test_intsetStressAddDelete}, {NULL, NULL}};
|
||||||
unitTest __test_kvstore_c[] = {{"test_kvstoreAdd16Keys", test_kvstoreAdd16Keys}, {"test_kvstoreIteratorRemoveAllKeysNoDeleteEmptyDict", test_kvstoreIteratorRemoveAllKeysNoDeleteEmptyDict}, {"test_kvstoreIteratorRemoveAllKeysDeleteEmptyDict", test_kvstoreIteratorRemoveAllKeysDeleteEmptyDict}, {"test_kvstoreDictIteratorRemoveAllKeysNoDeleteEmptyDict", test_kvstoreDictIteratorRemoveAllKeysNoDeleteEmptyDict}, {"test_kvstoreDictIteratorRemoveAllKeysDeleteEmptyDict", test_kvstoreDictIteratorRemoveAllKeysDeleteEmptyDict}, {NULL, NULL}};
|
unitTest __test_kvstore_c[] = {{"test_kvstoreAdd16Keys", test_kvstoreAdd16Keys}, {"test_kvstoreIteratorRemoveAllKeysNoDeleteEmptyDict", test_kvstoreIteratorRemoveAllKeysNoDeleteEmptyDict}, {"test_kvstoreIteratorRemoveAllKeysDeleteEmptyDict", test_kvstoreIteratorRemoveAllKeysDeleteEmptyDict}, {"test_kvstoreDictIteratorRemoveAllKeysNoDeleteEmptyDict", test_kvstoreDictIteratorRemoveAllKeysNoDeleteEmptyDict}, {"test_kvstoreDictIteratorRemoveAllKeysDeleteEmptyDict", test_kvstoreDictIteratorRemoveAllKeysDeleteEmptyDict}, {NULL, NULL}};
|
||||||
@ -147,6 +158,7 @@ struct unitTestSuite {
|
|||||||
} unitTestSuite[] = {
|
} unitTestSuite[] = {
|
||||||
{"test_crc64.c", __test_crc64_c},
|
{"test_crc64.c", __test_crc64_c},
|
||||||
{"test_crc64combine.c", __test_crc64combine_c},
|
{"test_crc64combine.c", __test_crc64combine_c},
|
||||||
|
{"test_dict.c", __test_dict_c},
|
||||||
{"test_endianconv.c", __test_endianconv_c},
|
{"test_endianconv.c", __test_endianconv_c},
|
||||||
{"test_intset.c", __test_intset_c},
|
{"test_intset.c", __test_intset_c},
|
||||||
{"test_kvstore.c", __test_kvstore_c},
|
{"test_kvstore.c", __test_kvstore_c},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user