From 14016d2df7ce6e7726012c2b0ec023b2a64ef2d1 Mon Sep 17 00:00:00 2001 From: xu0o0 Date: Mon, 9 Sep 2024 13:01:25 +0800 Subject: [PATCH] Migrate listpack.c unit tests to new framework (#949) This PR migrates the tests related to listpack into new test framework as part of #428. Signed-off-by: haoqixu Signed-off-by: Binbin Co-authored-by: Binbin --- src/listpack.c | 977 -------------------------- src/listpack.h | 4 - src/server.c | 1 - src/unit/test_files.h | 54 ++ src/unit/test_listpack.c | 1396 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 1450 insertions(+), 982 deletions(-) create mode 100644 src/unit/test_listpack.c diff --git a/src/listpack.c b/src/listpack.c index 3bb6686f6..4c51b285d 100644 --- a/src/listpack.c +++ b/src/listpack.c @@ -1630,980 +1630,3 @@ void lpRepr(unsigned char *lp) { } printf("{end}\n\n"); } - -#ifdef SERVER_TEST - -#include -#include "adlist.h" -#include "sds.h" -#include "testhelp.h" - -#define UNUSED(x) (void)(x) -#define TEST(name) printf("test — %s\n", name); - -char *mixlist[] = {"hello", "foo", "quux", "1024"}; -char *intlist[] = {"4294967296", "-100", "100", "128000", "non integer", "much much longer non integer"}; - -static unsigned char *createList(void) { - unsigned char *lp = lpNew(0); - lp = lpAppend(lp, (unsigned char *)mixlist[1], strlen(mixlist[1])); - lp = lpAppend(lp, (unsigned char *)mixlist[2], strlen(mixlist[2])); - lp = lpPrepend(lp, (unsigned char *)mixlist[0], strlen(mixlist[0])); - lp = lpAppend(lp, (unsigned char *)mixlist[3], strlen(mixlist[3])); - return lp; -} - -static unsigned char *createIntList(void) { - unsigned char *lp = lpNew(0); - lp = lpAppend(lp, (unsigned char *)intlist[2], strlen(intlist[2])); - lp = lpAppend(lp, (unsigned char *)intlist[3], strlen(intlist[3])); - lp = lpPrepend(lp, (unsigned char *)intlist[1], strlen(intlist[1])); - lp = lpPrepend(lp, (unsigned char *)intlist[0], strlen(intlist[0])); - lp = lpAppend(lp, (unsigned char *)intlist[4], strlen(intlist[4])); - lp = lpAppend(lp, (unsigned char *)intlist[5], strlen(intlist[5])); - return lp; -} - -static long long usec(void) { - struct timeval tv; - gettimeofday(&tv, NULL); - return (((long long)tv.tv_sec) * 1000000) + tv.tv_usec; -} - -static void stress(int pos, int num, int maxsize, int dnum) { - int i, j, k; - unsigned char *lp; - char posstr[2][5] = {"HEAD", "TAIL"}; - long long start; - for (i = 0; i < maxsize; i += dnum) { - lp = lpNew(0); - for (j = 0; j < i; j++) { - lp = lpAppend(lp, (unsigned char *)"quux", 4); - } - - /* Do num times a push+pop from pos */ - start = usec(); - for (k = 0; k < num; k++) { - if (pos == 0) { - lp = lpPrepend(lp, (unsigned char *)"quux", 4); - } else { - lp = lpAppend(lp, (unsigned char *)"quux", 4); - } - lp = lpDelete(lp, lpFirst(lp), NULL); - } - printf("List size: %8d, bytes: %8zu, %dx push+pop (%s): %6lld usec\n", i, lpBytes(lp), num, posstr[pos], - usec() - start); - lpFree(lp); - } -} - -static unsigned char *pop(unsigned char *lp, int where) { - unsigned char *p, *vstr; - int64_t vlen; - - p = lpSeek(lp, where == 0 ? 0 : -1); - vstr = lpGet(p, &vlen, NULL); - if (where == 0) - printf("Pop head: "); - else - printf("Pop tail: "); - - if (vstr) { - if (vlen && fwrite(vstr, vlen, 1, stdout) == 0) perror("fwrite"); - } else { - printf("%lld", (long long)vlen); - } - - printf("\n"); - return lpDelete(lp, p, &p); -} - -static int randstring(char *target, unsigned int min, unsigned int max) { - int p = 0; - int len = min + rand() % (max - min + 1); - int minval, maxval; - switch (rand() % 3) { - case 0: - minval = 0; - maxval = 255; - break; - case 1: - minval = 48; - maxval = 122; - break; - case 2: - minval = 48; - maxval = 52; - break; - default: assert(NULL); - } - - while (p < len) target[p++] = minval + rand() % (maxval - minval + 1); - return len; -} - -static void verifyEntry(unsigned char *p, unsigned char *s, size_t slen) { - assert(lpCompare(p, s, slen)); -} - -static int lpValidation(unsigned char *p, unsigned int head_count, void *userdata) { - UNUSED(p); - UNUSED(head_count); - - int ret; - long *count = userdata; - ret = lpCompare(p, (unsigned char *)mixlist[*count], strlen(mixlist[*count])); - (*count)++; - return ret; -} - -int listpackTest(int argc, char *argv[], int flags) { - UNUSED(argc); - UNUSED(argv); - - int i; - unsigned char *lp, *p, *vstr; - int64_t vlen; - unsigned char intbuf[LP_INTBUF_SIZE]; - int accurate = (flags & TEST_ACCURATE); - - TEST("Create int list") { - lp = createIntList(); - assert(lpLength(lp) == 6); - lpFree(lp); - } - - TEST("Create list") { - lp = createList(); - assert(lpLength(lp) == 4); - lpFree(lp); - } - - TEST("Test lpPrepend") { - lp = lpNew(0); - lp = lpPrepend(lp, (unsigned char *)"abc", 3); - lp = lpPrepend(lp, (unsigned char *)"1024", 4); - verifyEntry(lpSeek(lp, 0), (unsigned char *)"1024", 4); - verifyEntry(lpSeek(lp, 1), (unsigned char *)"abc", 3); - lpFree(lp); - } - - TEST("Test lpPrependInteger") { - lp = lpNew(0); - lp = lpPrependInteger(lp, 127); - lp = lpPrependInteger(lp, 4095); - lp = lpPrependInteger(lp, 32767); - lp = lpPrependInteger(lp, 8388607); - lp = lpPrependInteger(lp, 2147483647); - lp = lpPrependInteger(lp, 9223372036854775807); - verifyEntry(lpSeek(lp, 0), (unsigned char *)"9223372036854775807", 19); - verifyEntry(lpSeek(lp, -1), (unsigned char *)"127", 3); - lpFree(lp); - } - - TEST("Get element at index") { - lp = createList(); - verifyEntry(lpSeek(lp, 0), (unsigned char *)"hello", 5); - verifyEntry(lpSeek(lp, 3), (unsigned char *)"1024", 4); - verifyEntry(lpSeek(lp, -1), (unsigned char *)"1024", 4); - verifyEntry(lpSeek(lp, -4), (unsigned char *)"hello", 5); - assert(lpSeek(lp, 4) == NULL); - assert(lpSeek(lp, -5) == NULL); - lpFree(lp); - } - - TEST("Pop list") { - lp = createList(); - lp = pop(lp, 1); - lp = pop(lp, 0); - lp = pop(lp, 1); - lp = pop(lp, 1); - lpFree(lp); - } - - TEST("Get element at index") { - lp = createList(); - verifyEntry(lpSeek(lp, 0), (unsigned char *)"hello", 5); - verifyEntry(lpSeek(lp, 3), (unsigned char *)"1024", 4); - verifyEntry(lpSeek(lp, -1), (unsigned char *)"1024", 4); - verifyEntry(lpSeek(lp, -4), (unsigned char *)"hello", 5); - assert(lpSeek(lp, 4) == NULL); - assert(lpSeek(lp, -5) == NULL); - lpFree(lp); - } - - TEST("Iterate list from 0 to end") { - lp = createList(); - p = lpFirst(lp); - i = 0; - while (p) { - verifyEntry(p, (unsigned char *)mixlist[i], strlen(mixlist[i])); - p = lpNext(lp, p); - i++; - } - lpFree(lp); - } - - TEST("Iterate list from 1 to end") { - lp = createList(); - i = 1; - p = lpSeek(lp, i); - while (p) { - verifyEntry(p, (unsigned char *)mixlist[i], strlen(mixlist[i])); - p = lpNext(lp, p); - i++; - } - lpFree(lp); - } - - TEST("Iterate list from 2 to end") { - lp = createList(); - i = 2; - p = lpSeek(lp, i); - while (p) { - verifyEntry(p, (unsigned char *)mixlist[i], strlen(mixlist[i])); - p = lpNext(lp, p); - i++; - } - lpFree(lp); - } - - TEST("Iterate from back to front") { - lp = createList(); - p = lpLast(lp); - i = 3; - while (p) { - verifyEntry(p, (unsigned char *)mixlist[i], strlen(mixlist[i])); - p = lpPrev(lp, p); - i--; - } - lpFree(lp); - } - - TEST("Iterate from back to front, deleting all items") { - lp = createList(); - p = lpLast(lp); - i = 3; - while ((p = lpLast(lp))) { - verifyEntry(p, (unsigned char *)mixlist[i], strlen(mixlist[i])); - lp = lpDelete(lp, p, &p); - assert(p == NULL); - i--; - } - lpFree(lp); - } - - TEST("Delete whole listpack when num == -1"); - { - lp = createList(); - lp = lpDeleteRange(lp, 0, -1); - assert(lpLength(lp) == 0); - assert(lp[LP_HDR_SIZE] == LP_EOF); - assert(lpBytes(lp) == (LP_HDR_SIZE + 1)); - zfree(lp); - - lp = createList(); - unsigned char *ptr = lpFirst(lp); - lp = lpDeleteRangeWithEntry(lp, &ptr, -1); - assert(lpLength(lp) == 0); - assert(lp[LP_HDR_SIZE] == LP_EOF); - assert(lpBytes(lp) == (LP_HDR_SIZE + 1)); - zfree(lp); - } - - TEST("Delete whole listpack with negative index"); - { - lp = createList(); - lp = lpDeleteRange(lp, -4, 4); - assert(lpLength(lp) == 0); - assert(lp[LP_HDR_SIZE] == LP_EOF); - assert(lpBytes(lp) == (LP_HDR_SIZE + 1)); - zfree(lp); - - lp = createList(); - unsigned char *ptr = lpSeek(lp, -4); - lp = lpDeleteRangeWithEntry(lp, &ptr, 4); - assert(lpLength(lp) == 0); - assert(lp[LP_HDR_SIZE] == LP_EOF); - assert(lpBytes(lp) == (LP_HDR_SIZE + 1)); - zfree(lp); - } - - TEST("Delete inclusive range 0,0"); - { - lp = createList(); - lp = lpDeleteRange(lp, 0, 1); - assert(lpLength(lp) == 3); - assert(lpSkip(lpLast(lp))[0] == LP_EOF); /* check set LP_EOF correctly */ - zfree(lp); - - lp = createList(); - unsigned char *ptr = lpFirst(lp); - lp = lpDeleteRangeWithEntry(lp, &ptr, 1); - assert(lpLength(lp) == 3); - assert(lpSkip(lpLast(lp))[0] == LP_EOF); /* check set LP_EOF correctly */ - zfree(lp); - } - - TEST("Delete inclusive range 0,1"); - { - lp = createList(); - lp = lpDeleteRange(lp, 0, 2); - assert(lpLength(lp) == 2); - verifyEntry(lpFirst(lp), (unsigned char *)mixlist[2], strlen(mixlist[2])); - zfree(lp); - - lp = createList(); - unsigned char *ptr = lpFirst(lp); - lp = lpDeleteRangeWithEntry(lp, &ptr, 2); - assert(lpLength(lp) == 2); - verifyEntry(lpFirst(lp), (unsigned char *)mixlist[2], strlen(mixlist[2])); - zfree(lp); - } - - TEST("Delete inclusive range 1,2"); - { - lp = createList(); - lp = lpDeleteRange(lp, 1, 2); - assert(lpLength(lp) == 2); - verifyEntry(lpFirst(lp), (unsigned char *)mixlist[0], strlen(mixlist[0])); - zfree(lp); - - lp = createList(); - unsigned char *ptr = lpSeek(lp, 1); - lp = lpDeleteRangeWithEntry(lp, &ptr, 2); - assert(lpLength(lp) == 2); - verifyEntry(lpFirst(lp), (unsigned char *)mixlist[0], strlen(mixlist[0])); - zfree(lp); - } - - TEST("Delete with start index out of range"); - { - lp = createList(); - lp = lpDeleteRange(lp, 5, 1); - assert(lpLength(lp) == 4); - zfree(lp); - } - - TEST("Delete with num overflow"); - { - lp = createList(); - lp = lpDeleteRange(lp, 1, 5); - assert(lpLength(lp) == 1); - verifyEntry(lpFirst(lp), (unsigned char *)mixlist[0], strlen(mixlist[0])); - zfree(lp); - - lp = createList(); - unsigned char *ptr = lpSeek(lp, 1); - lp = lpDeleteRangeWithEntry(lp, &ptr, 5); - assert(lpLength(lp) == 1); - verifyEntry(lpFirst(lp), (unsigned char *)mixlist[0], strlen(mixlist[0])); - zfree(lp); - } - - TEST("Batch delete") { - unsigned char *lp = createList(); /* char *mixlist[] = {"hello", "foo", "quux", "1024"} */ - assert(lpLength(lp) == 4); /* Pre-condition */ - unsigned char *p0 = lpFirst(lp), *p1 = lpNext(lp, p0), *p2 = lpNext(lp, p1), *p3 = lpNext(lp, p2); - unsigned char *ps[] = {p0, p1, p3}; - lp = lpBatchDelete(lp, ps, 3); - assert(lpLength(lp) == 1); - verifyEntry(lpFirst(lp), (unsigned char *)mixlist[2], strlen(mixlist[2])); - assert(lpValidateIntegrity(lp, lpBytes(lp), 1, NULL, NULL) == 1); - lpFree(lp); - } - - TEST("Delete foo while iterating") { - lp = createList(); - p = lpFirst(lp); - while (p) { - if (lpCompare(p, (unsigned char *)"foo", 3)) { - lp = lpDelete(lp, p, &p); - } else { - p = lpNext(lp, p); - } - } - lpFree(lp); - } - - TEST("Replace with same size") { - lp = createList(); /* "hello", "foo", "quux", "1024" */ - unsigned char *orig_lp = lp; - p = lpSeek(lp, 0); - lp = lpReplace(lp, &p, (unsigned char *)"zoink", 5); - p = lpSeek(lp, 3); - lp = lpReplace(lp, &p, (unsigned char *)"y", 1); - p = lpSeek(lp, 1); - lp = lpReplace(lp, &p, (unsigned char *)"65536", 5); - p = lpSeek(lp, 0); - assert(!memcmp((char *)p, - "\x85zoink\x06" - "\xf2\x00\x00\x01\x04" /* 65536 as int24 */ - "\x84quux\05" - "\x81y\x02" - "\xff", - 22)); - assert(lp == orig_lp); /* no reallocations have happened */ - lpFree(lp); - } - - TEST("Replace with different size") { - lp = createList(); /* "hello", "foo", "quux", "1024" */ - p = lpSeek(lp, 1); - lp = lpReplace(lp, &p, (unsigned char *)"squirrel", 8); - p = lpSeek(lp, 0); - assert(!strncmp((char *)p, - "\x85hello\x06" - "\x88squirrel\x09" - "\x84quux\x05" - "\xc4\x00\x02" - "\xff", - 27)); - lpFree(lp); - } - - TEST("Regression test for >255 byte strings") { - char v1[257] = {0}, v2[257] = {0}; - memset(v1, 'x', 256); - memset(v2, 'y', 256); - lp = lpNew(0); - lp = lpAppend(lp, (unsigned char *)v1, strlen(v1)); - lp = lpAppend(lp, (unsigned char *)v2, strlen(v2)); - - /* Pop values again and compare their value. */ - p = lpFirst(lp); - vstr = lpGet(p, &vlen, NULL); - assert(strncmp(v1, (char *)vstr, vlen) == 0); - p = lpSeek(lp, 1); - vstr = lpGet(p, &vlen, NULL); - assert(strncmp(v2, (char *)vstr, vlen) == 0); - lpFree(lp); - } - - TEST("Create long list and check indices") { - lp = lpNew(0); - char buf[32]; - int i, len; - for (i = 0; i < 1000; i++) { - len = snprintf(buf, sizeof(buf), "%d", i); - lp = lpAppend(lp, (unsigned char *)buf, len); - } - for (i = 0; i < 1000; i++) { - p = lpSeek(lp, i); - vstr = lpGet(p, &vlen, NULL); - assert(i == vlen); - - p = lpSeek(lp, -i - 1); - vstr = lpGet(p, &vlen, NULL); - assert(999 - i == vlen); - } - lpFree(lp); - } - - TEST("Compare strings with listpack entries") { - lp = createList(); - p = lpSeek(lp, 0); - assert(lpCompare(p, (unsigned char *)"hello", 5)); - assert(!lpCompare(p, (unsigned char *)"hella", 5)); - - p = lpSeek(lp, 3); - assert(lpCompare(p, (unsigned char *)"1024", 4)); - assert(!lpCompare(p, (unsigned char *)"1025", 4)); - lpFree(lp); - } - - TEST("lpMerge two empty listpacks") { - unsigned char *lp1 = lpNew(0); - unsigned char *lp2 = lpNew(0); - - /* Merge two empty listpacks, get empty result back. */ - lp1 = lpMerge(&lp1, &lp2); - assert(lpLength(lp1) == 0); - zfree(lp1); - } - - TEST("lpMerge two listpacks - first larger than second") { - unsigned char *lp1 = createIntList(); - unsigned char *lp2 = createList(); - - size_t lp1_bytes = lpBytes(lp1); - size_t lp2_bytes = lpBytes(lp2); - unsigned long lp1_len = lpLength(lp1); - unsigned long lp2_len = lpLength(lp2); - - unsigned char *lp3 = lpMerge(&lp1, &lp2); - assert(lp3 == lp1); - assert(lp2 == NULL); - assert(lpLength(lp3) == (lp1_len + lp2_len)); - assert(lpBytes(lp3) == (lp1_bytes + lp2_bytes - LP_HDR_SIZE - 1)); - verifyEntry(lpSeek(lp3, 0), (unsigned char *)"4294967296", 10); - verifyEntry(lpSeek(lp3, 5), (unsigned char *)"much much longer non integer", 28); - verifyEntry(lpSeek(lp3, 6), (unsigned char *)"hello", 5); - verifyEntry(lpSeek(lp3, -1), (unsigned char *)"1024", 4); - zfree(lp3); - } - - TEST("lpMerge two listpacks - second larger than first") { - unsigned char *lp1 = createList(); - unsigned char *lp2 = createIntList(); - - size_t lp1_bytes = lpBytes(lp1); - size_t lp2_bytes = lpBytes(lp2); - unsigned long lp1_len = lpLength(lp1); - unsigned long lp2_len = lpLength(lp2); - - unsigned char *lp3 = lpMerge(&lp1, &lp2); - assert(lp3 == lp2); - assert(lp1 == NULL); - assert(lpLength(lp3) == (lp1_len + lp2_len)); - assert(lpBytes(lp3) == (lp1_bytes + lp2_bytes - LP_HDR_SIZE - 1)); - verifyEntry(lpSeek(lp3, 0), (unsigned char *)"hello", 5); - verifyEntry(lpSeek(lp3, 3), (unsigned char *)"1024", 4); - verifyEntry(lpSeek(lp3, 4), (unsigned char *)"4294967296", 10); - verifyEntry(lpSeek(lp3, -1), (unsigned char *)"much much longer non integer", 28); - zfree(lp3); - } - - TEST("lpNextRandom normal usage") { - /* Create some data */ - unsigned char *lp = lpNew(0); - unsigned char buf[100] = "asdf"; - unsigned int size = 100; - for (size_t i = 0; i < size; i++) { - lp = lpAppend(lp, buf, i); - } - assert(lpLength(lp) == size); - - /* Pick a subset of the elements of every possible subset size */ - for (unsigned int count = 0; count <= size; count++) { - unsigned int remaining = count; - unsigned char *p = lpFirst(lp); - unsigned char *prev = NULL; - unsigned index = 0; - while (remaining > 0) { - assert(p != NULL); - p = lpNextRandom(lp, p, &index, remaining--, 0); - assert(p != NULL); - assert(p != prev); - prev = p; - p = lpNext(lp, p); - index++; - } - } - lpFree(lp); - } - - TEST("lpNextRandom corner cases") { - unsigned char *lp = lpNew(0); - unsigned i = 0; - - /* Pick from empty listpack returns NULL. */ - assert(lpNextRandom(lp, NULL, &i, 2, 0) == NULL); - - /* Add some elements and find their pointers within the listpack. */ - lp = lpAppend(lp, (unsigned char *)"abc", 3); - lp = lpAppend(lp, (unsigned char *)"def", 3); - lp = lpAppend(lp, (unsigned char *)"ghi", 3); - assert(lpLength(lp) == 3); - unsigned char *p0 = lpFirst(lp); - unsigned char *p1 = lpNext(lp, p0); - unsigned char *p2 = lpNext(lp, p1); - assert(lpNext(lp, p2) == NULL); - - /* Pick zero elements returns NULL. */ - i = 0; - assert(lpNextRandom(lp, lpFirst(lp), &i, 0, 0) == NULL); - - /* Pick all returns all. */ - i = 0; - assert(lpNextRandom(lp, p0, &i, 3, 0) == p0 && i == 0); - i = 1; - assert(lpNextRandom(lp, p1, &i, 2, 0) == p1 && i == 1); - i = 2; - assert(lpNextRandom(lp, p2, &i, 1, 0) == p2 && i == 2); - - /* Pick more than one when there's only one left returns the last one. */ - i = 2; - assert(lpNextRandom(lp, p2, &i, 42, 0) == p2 && i == 2); - - /* Pick all even elements returns p0 and p2. */ - i = 0; - assert(lpNextRandom(lp, p0, &i, 10, 1) == p0 && i == 0); - i = 1; - assert(lpNextRandom(lp, p1, &i, 10, 1) == p2 && i == 2); - - /* Don't crash even for bad index. */ - for (int j = 0; j < 100; j++) { - unsigned char *p; - switch (j % 4) { - case 0: p = p0; break; - case 1: p = p1; break; - case 2: p = p2; break; - case 3: p = NULL; break; - } - i = j % 7; - unsigned int remaining = j % 5; - p = lpNextRandom(lp, p, &i, remaining, 0); - assert(p == p0 || p == p1 || p == p2 || p == NULL); - } - lpFree(lp); - } - - TEST("Random pair with one element") { - listpackEntry key, val; - unsigned char *lp = lpNew(0); - lp = lpAppend(lp, (unsigned char *)"abc", 3); - lp = lpAppend(lp, (unsigned char *)"123", 3); - lpRandomPair(lp, 1, &key, &val); - assert(memcmp(key.sval, "abc", key.slen) == 0); - assert(val.lval == 123); - lpFree(lp); - } - - TEST("Random pair with many elements") { - listpackEntry key, val; - unsigned char *lp = lpNew(0); - lp = lpAppend(lp, (unsigned char *)"abc", 3); - lp = lpAppend(lp, (unsigned char *)"123", 3); - lp = lpAppend(lp, (unsigned char *)"456", 3); - lp = lpAppend(lp, (unsigned char *)"def", 3); - lpRandomPair(lp, 2, &key, &val); - if (key.sval) { - assert(!memcmp(key.sval, "abc", key.slen)); - assert(key.slen == 3); - assert(val.lval == 123); - } - if (!key.sval) { - assert(key.lval == 456); - assert(!memcmp(val.sval, "def", val.slen)); - } - lpFree(lp); - } - - TEST("Random pairs with one element") { - int count = 5; - unsigned char *lp = lpNew(0); - listpackEntry *keys = zmalloc(sizeof(listpackEntry) * count); - listpackEntry *vals = zmalloc(sizeof(listpackEntry) * count); - - lp = lpAppend(lp, (unsigned char *)"abc", 3); - lp = lpAppend(lp, (unsigned char *)"123", 3); - lpRandomPairs(lp, count, keys, vals); - assert(memcmp(keys[4].sval, "abc", keys[4].slen) == 0); - assert(vals[4].lval == 123); - zfree(keys); - zfree(vals); - lpFree(lp); - } - - TEST("Random pairs with many elements") { - int count = 5; - lp = lpNew(0); - listpackEntry *keys = zmalloc(sizeof(listpackEntry) * count); - listpackEntry *vals = zmalloc(sizeof(listpackEntry) * count); - - lp = lpAppend(lp, (unsigned char *)"abc", 3); - lp = lpAppend(lp, (unsigned char *)"123", 3); - lp = lpAppend(lp, (unsigned char *)"456", 3); - lp = lpAppend(lp, (unsigned char *)"def", 3); - lpRandomPairs(lp, count, keys, vals); - for (int i = 0; i < count; i++) { - if (keys[i].sval) { - assert(!memcmp(keys[i].sval, "abc", keys[i].slen)); - assert(keys[i].slen == 3); - assert(vals[i].lval == 123); - } - if (!keys[i].sval) { - assert(keys[i].lval == 456); - assert(!memcmp(vals[i].sval, "def", vals[i].slen)); - } - } - zfree(keys); - zfree(vals); - lpFree(lp); - } - - TEST("Random pairs unique with one element") { - unsigned picked; - int count = 5; - lp = lpNew(0); - listpackEntry *keys = zmalloc(sizeof(listpackEntry) * count); - listpackEntry *vals = zmalloc(sizeof(listpackEntry) * count); - - lp = lpAppend(lp, (unsigned char *)"abc", 3); - lp = lpAppend(lp, (unsigned char *)"123", 3); - picked = lpRandomPairsUnique(lp, count, keys, vals); - assert(picked == 1); - assert(memcmp(keys[0].sval, "abc", keys[0].slen) == 0); - assert(vals[0].lval == 123); - zfree(keys); - zfree(vals); - lpFree(lp); - } - - TEST("Random pairs unique with many elements") { - unsigned picked; - int count = 5; - lp = lpNew(0); - listpackEntry *keys = zmalloc(sizeof(listpackEntry) * count); - listpackEntry *vals = zmalloc(sizeof(listpackEntry) * count); - - lp = lpAppend(lp, (unsigned char *)"abc", 3); - lp = lpAppend(lp, (unsigned char *)"123", 3); - lp = lpAppend(lp, (unsigned char *)"456", 3); - lp = lpAppend(lp, (unsigned char *)"def", 3); - picked = lpRandomPairsUnique(lp, count, keys, vals); - assert(picked == 2); - for (int i = 0; i < 2; i++) { - if (keys[i].sval) { - assert(!memcmp(keys[i].sval, "abc", keys[i].slen)); - assert(keys[i].slen == 3); - assert(vals[i].lval == 123); - } - if (!keys[i].sval) { - assert(keys[i].lval == 456); - assert(!memcmp(vals[i].sval, "def", vals[i].slen)); - } - } - zfree(keys); - zfree(vals); - lpFree(lp); - } - - TEST("push various encodings") { - lp = lpNew(0); - - /* Push integer encode element using lpAppend */ - lp = lpAppend(lp, (unsigned char *)"127", 3); - assert(LP_ENCODING_IS_7BIT_UINT(lpLast(lp)[0])); - lp = lpAppend(lp, (unsigned char *)"4095", 4); - assert(LP_ENCODING_IS_13BIT_INT(lpLast(lp)[0])); - lp = lpAppend(lp, (unsigned char *)"32767", 5); - assert(LP_ENCODING_IS_16BIT_INT(lpLast(lp)[0])); - lp = lpAppend(lp, (unsigned char *)"8388607", 7); - assert(LP_ENCODING_IS_24BIT_INT(lpLast(lp)[0])); - lp = lpAppend(lp, (unsigned char *)"2147483647", 10); - assert(LP_ENCODING_IS_32BIT_INT(lpLast(lp)[0])); - lp = lpAppend(lp, (unsigned char *)"9223372036854775807", 19); - assert(LP_ENCODING_IS_64BIT_INT(lpLast(lp)[0])); - - /* Push integer encode element using lpAppendInteger */ - lp = lpAppendInteger(lp, 127); - assert(LP_ENCODING_IS_7BIT_UINT(lpLast(lp)[0])); - verifyEntry(lpLast(lp), (unsigned char *)"127", 3); - lp = lpAppendInteger(lp, 4095); - verifyEntry(lpLast(lp), (unsigned char *)"4095", 4); - assert(LP_ENCODING_IS_13BIT_INT(lpLast(lp)[0])); - lp = lpAppendInteger(lp, 32767); - verifyEntry(lpLast(lp), (unsigned char *)"32767", 5); - assert(LP_ENCODING_IS_16BIT_INT(lpLast(lp)[0])); - lp = lpAppendInteger(lp, 8388607); - verifyEntry(lpLast(lp), (unsigned char *)"8388607", 7); - assert(LP_ENCODING_IS_24BIT_INT(lpLast(lp)[0])); - lp = lpAppendInteger(lp, 2147483647); - verifyEntry(lpLast(lp), (unsigned char *)"2147483647", 10); - assert(LP_ENCODING_IS_32BIT_INT(lpLast(lp)[0])); - lp = lpAppendInteger(lp, 9223372036854775807); - verifyEntry(lpLast(lp), (unsigned char *)"9223372036854775807", 19); - assert(LP_ENCODING_IS_64BIT_INT(lpLast(lp)[0])); - - /* string encode */ - unsigned char *str = zmalloc(65535); - memset(str, 0, 65535); - lp = lpAppend(lp, (unsigned char *)str, 63); - assert(LP_ENCODING_IS_6BIT_STR(lpLast(lp)[0])); - lp = lpAppend(lp, (unsigned char *)str, 4095); - assert(LP_ENCODING_IS_12BIT_STR(lpLast(lp)[0])); - lp = lpAppend(lp, (unsigned char *)str, 65535); - assert(LP_ENCODING_IS_32BIT_STR(lpLast(lp)[0])); - zfree(str); - lpFree(lp); - } - - TEST("Test lpFind") { - lp = createList(); - assert(lpFind(lp, lpFirst(lp), (unsigned char *)"abc", 3, 0) == NULL); - verifyEntry(lpFind(lp, lpFirst(lp), (unsigned char *)"hello", 5, 0), (unsigned char *)"hello", 5); - verifyEntry(lpFind(lp, lpFirst(lp), (unsigned char *)"1024", 4, 0), (unsigned char *)"1024", 4); - lpFree(lp); - } - - TEST("Test lpValidateIntegrity") { - lp = createList(); - long count = 0; - assert(lpValidateIntegrity(lp, lpBytes(lp), 1, lpValidation, &count) == 1); - lpFree(lp); - } - - TEST("Test number of elements exceeds LP_HDR_NUMELE_UNKNOWN") { - lp = lpNew(0); - for (int i = 0; i < LP_HDR_NUMELE_UNKNOWN + 1; i++) lp = lpAppend(lp, (unsigned char *)"1", 1); - - assert(lpGetNumElements(lp) == LP_HDR_NUMELE_UNKNOWN); - assert(lpLength(lp) == LP_HDR_NUMELE_UNKNOWN + 1); - - lp = lpDeleteRange(lp, -2, 2); - assert(lpGetNumElements(lp) == LP_HDR_NUMELE_UNKNOWN); - assert(lpLength(lp) == LP_HDR_NUMELE_UNKNOWN - 1); - assert(lpGetNumElements(lp) == LP_HDR_NUMELE_UNKNOWN - 1); /* update length after lpLength */ - lpFree(lp); - } - - TEST("Stress with random payloads of different encoding") { - unsigned long long start = usec(); - int i, j, len, where; - unsigned char *p; - char buf[1024]; - int buflen; - list *ref; - listNode *refnode; - - int iteration = accurate ? 20000 : 20; - for (i = 0; i < iteration; i++) { - lp = lpNew(0); - ref = listCreate(); - listSetFreeMethod(ref, (void (*)(void *))sdsfree); - len = rand() % 256; - - /* Create lists */ - for (j = 0; j < len; j++) { - where = (rand() & 1) ? 0 : 1; - if (rand() % 2) { - buflen = randstring(buf, 1, sizeof(buf) - 1); - } else { - switch (rand() % 3) { - case 0: buflen = snprintf(buf, sizeof(buf), "%lld", (0LL + rand()) >> 20); break; - case 1: buflen = snprintf(buf, sizeof(buf), "%lld", (0LL + rand())); break; - case 2: buflen = snprintf(buf, sizeof(buf), "%lld", (0LL + rand()) << 20); break; - default: assert(NULL); - } - } - - /* Add to listpack */ - if (where == 0) { - lp = lpPrepend(lp, (unsigned char *)buf, buflen); - } else { - lp = lpAppend(lp, (unsigned char *)buf, buflen); - } - - /* Add to reference list */ - if (where == 0) { - listAddNodeHead(ref, sdsnewlen(buf, buflen)); - } else if (where == 1) { - listAddNodeTail(ref, sdsnewlen(buf, buflen)); - } else { - assert(NULL); - } - } - - assert(listLength(ref) == lpLength(lp)); - for (j = 0; j < len; j++) { - /* Naive way to get elements, but similar to the stresser - * executed from the Tcl test suite. */ - p = lpSeek(lp, j); - refnode = listIndex(ref, j); - - vstr = lpGet(p, &vlen, intbuf); - assert(memcmp(vstr, listNodeValue(refnode), vlen) == 0); - } - lpFree(lp); - listRelease(ref); - } - printf("Done. usec=%lld\n\n", usec() - start); - } - - TEST("Stress with variable listpack size") { - unsigned long long start = usec(); - int maxsize = accurate ? 16384 : 16; - stress(0, 100000, maxsize, 256); - stress(1, 100000, maxsize, 256); - printf("Done. usec=%lld\n\n", usec() - start); - } - - /* Benchmarks */ - { - int iteration = accurate ? 100000 : 100; - lp = lpNew(0); - TEST("Benchmark lpAppend") { - unsigned long long start = usec(); - for (int i = 0; i < iteration; i++) { - char buf[4096] = "asdf"; - lp = lpAppend(lp, (unsigned char *)buf, 4); - lp = lpAppend(lp, (unsigned char *)buf, 40); - lp = lpAppend(lp, (unsigned char *)buf, 400); - lp = lpAppend(lp, (unsigned char *)buf, 4000); - lp = lpAppend(lp, (unsigned char *)"1", 1); - lp = lpAppend(lp, (unsigned char *)"10", 2); - lp = lpAppend(lp, (unsigned char *)"100", 3); - lp = lpAppend(lp, (unsigned char *)"1000", 4); - lp = lpAppend(lp, (unsigned char *)"10000", 5); - lp = lpAppend(lp, (unsigned char *)"100000", 6); - } - printf("Done. usec=%lld\n", usec() - start); - } - - TEST("Benchmark lpFind string") { - unsigned long long start = usec(); - for (int i = 0; i < 2000; i++) { - unsigned char *fptr = lpFirst(lp); - fptr = lpFind(lp, fptr, (unsigned char *)"nothing", 7, 1); - } - printf("Done. usec=%lld\n", usec() - start); - } - - TEST("Benchmark lpFind number") { - unsigned long long start = usec(); - for (int i = 0; i < 2000; i++) { - unsigned char *fptr = lpFirst(lp); - fptr = lpFind(lp, fptr, (unsigned char *)"99999", 5, 1); - } - printf("Done. usec=%lld\n", usec() - start); - } - - TEST("Benchmark lpSeek") { - unsigned long long start = usec(); - for (int i = 0; i < 2000; i++) { - lpSeek(lp, 99999); - } - printf("Done. usec=%lld\n", usec() - start); - } - - TEST("Benchmark lpValidateIntegrity") { - unsigned long long start = usec(); - for (int i = 0; i < 2000; i++) { - lpValidateIntegrity(lp, lpBytes(lp), 1, NULL, NULL); - } - printf("Done. usec=%lld\n", usec() - start); - } - - TEST("Benchmark lpCompare with string") { - unsigned long long start = usec(); - for (int i = 0; i < 2000; i++) { - unsigned char *eptr = lpSeek(lp, 0); - while (eptr != NULL) { - lpCompare(eptr, (unsigned char *)"nothing", 7); - eptr = lpNext(lp, eptr); - } - } - printf("Done. usec=%lld\n", usec() - start); - } - - TEST("Benchmark lpCompare with number") { - unsigned long long start = usec(); - for (int i = 0; i < 2000; i++) { - unsigned char *eptr = lpSeek(lp, 0); - while (eptr != NULL) { - lpCompare(lp, (unsigned char *)"99999", 5); - eptr = lpNext(lp, eptr); - } - } - printf("Done. usec=%lld\n", usec() - start); - } - - lpFree(lp); - } - - return 0; -} - -#endif diff --git a/src/listpack.h b/src/listpack.h index 1f99b5945..aa7636143 100644 --- a/src/listpack.h +++ b/src/listpack.h @@ -97,8 +97,4 @@ lpNextRandom(unsigned char *lp, unsigned char *p, unsigned int *index, unsigned int lpSafeToAdd(unsigned char *lp, size_t add); void lpRepr(unsigned char *lp); -#ifdef SERVER_TEST -int listpackTest(int argc, char *argv[], int flags); -#endif - #endif diff --git a/src/server.c b/src/server.c index 922062ecd..961be992c 100644 --- a/src/server.c +++ b/src/server.c @@ -6699,7 +6699,6 @@ struct serverTest { } serverTests[] = { {"quicklist", quicklistTest}, {"dict", dictTest}, - {"listpack", listpackTest}, }; serverTestProc *getTestProcByName(const char *name) { int numtests = sizeof(serverTests) / sizeof(struct serverTest); diff --git a/src/unit/test_files.h b/src/unit/test_files.h index 90addfecb..d54e489de 100644 --- a/src/unit/test_files.h +++ b/src/unit/test_files.h @@ -23,6 +23,58 @@ int test_kvstoreIteratorRemoveAllKeysNoDeleteEmptyDict(int argc, char **argv, in int test_kvstoreIteratorRemoveAllKeysDeleteEmptyDict(int argc, char **argv, int flags); int test_kvstoreDictIteratorRemoveAllKeysNoDeleteEmptyDict(int argc, char **argv, int flags); int test_kvstoreDictIteratorRemoveAllKeysDeleteEmptyDict(int argc, char **argv, int flags); +int test_listpackCreateIntList(int argc, char **argv, int flags); +int test_listpackCreateList(int argc, char **argv, int flags); +int test_listpackLpPrepend(int argc, char **argv, int flags); +int test_listpackLpPrependInteger(int argc, char **argv, int flags); +int test_listpackGetELementAtIndex(int argc, char **argv, int flags); +int test_listpackPop(int argc, char **argv, int flags); +int test_listpackGetELementAtIndex2(int argc, char **argv, int flags); +int test_listpackIterate0toEnd(int argc, char **argv, int flags); +int test_listpackIterate1toEnd(int argc, char **argv, int flags); +int test_listpackIterate2toEnd(int argc, char **argv, int flags); +int test_listpackIterateBackToFront(int argc, char **argv, int flags); +int test_listpackIterateBackToFrontWithDelete(int argc, char **argv, int flags); +int test_listpackDeleteWhenNumIsMinusOne(int argc, char **argv, int flags); +int test_listpackDeleteWithNegativeIndex(int argc, char **argv, int flags); +int test_listpackDeleteInclusiveRange0_0(int argc, char **argv, int flags); +int test_listpackDeleteInclusiveRange0_1(int argc, char **argv, int flags); +int test_listpackDeleteInclusiveRange1_2(int argc, char **argv, int flags); +int test_listpackDeleteWitStartIndexOutOfRange(int argc, char **argv, int flags); +int test_listpackDeleteWitNumOverflow(int argc, char **argv, int flags); +int test_listpackBatchDelete(int argc, char **argv, int flags); +int test_listpackDeleteFooWhileIterating(int argc, char **argv, int flags); +int test_listpackReplaceWithSameSize(int argc, char **argv, int flags); +int test_listpackReplaceWithDifferentSize(int argc, char **argv, int flags); +int test_listpackRegressionGt255Bytes(int argc, char **argv, int flags); +int test_listpackCreateLongListAndCheckIndices(int argc, char **argv, int flags); +int test_listpackCompareStrsWithLpEntries(int argc, char **argv, int flags); +int test_listpackLpMergeEmptyLps(int argc, char **argv, int flags); +int test_listpackLpMergeLp1Larger(int argc, char **argv, int flags); +int test_listpackLpMergeLp2Larger(int argc, char **argv, int flags); +int test_listpackLpNextRandom(int argc, char **argv, int flags); +int test_listpackLpNextRandomCC(int argc, char **argv, int flags); +int test_listpackRandomPairWithOneElement(int argc, char **argv, int flags); +int test_listpackRandomPairWithManyElements(int argc, char **argv, int flags); +int test_listpackRandomPairsWithOneElement(int argc, char **argv, int flags); +int test_listpackRandomPairsWithManyElements(int argc, char **argv, int flags); +int test_listpackRandomPairsUniqueWithOneElement(int argc, char **argv, int flags); +int test_listpackRandomPairsUniqueWithManyElements(int argc, char **argv, int flags); +int test_listpackPushVariousEncodings(int argc, char **argv, int flags); +int test_listpackLpFind(int argc, char **argv, int flags); +int test_listpackLpValidateIntegrity(int argc, char **argv, int flags); +int test_listpackNumberOfElementsExceedsLP_HDR_NUMELE_UNKNOWN(int argc, char **argv, int flags); +int test_listpackStressWithRandom(int argc, char **argv, int flags); +int test_listpackSTressWithVariableSize(int argc, char **argv, int flags); +int test_listpackBenchmarkInit(int argc, char *argv[], int flags); +int test_listpackBenchmarkLpAppend(int argc, char **argv, int flags); +int test_listpackBenchmarkLpFindString(int argc, char **argv, int flags); +int test_listpackBenchmarkLpFindNumber(int argc, char **argv, int flags); +int test_listpackBenchmarkLpSeek(int argc, char **argv, int flags); +int test_listpackBenchmarkLpValidateIntegrity(int argc, char **argv, int flags); +int test_listpackBenchmarkLpCompareWithString(int argc, char **argv, int flags); +int test_listpackBenchmarkLpCompareWithNumber(int argc, char **argv, int flags); +int test_listpackBenchmarkFree(int argc, char **argv, int flags); int test_sds(int argc, char **argv, int flags); int test_typesAndAllocSize(int argc, char **argv, int flags); int test_sdsHeaderSizes(int argc, char **argv, int flags); @@ -81,6 +133,7 @@ unitTest __test_crc64combine_c[] = {{"test_crc64combine", test_crc64combine}, {N 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_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_listpack_c[] = {{"test_listpackCreateIntList", test_listpackCreateIntList}, {"test_listpackCreateList", test_listpackCreateList}, {"test_listpackLpPrepend", test_listpackLpPrepend}, {"test_listpackLpPrependInteger", test_listpackLpPrependInteger}, {"test_listpackGetELementAtIndex", test_listpackGetELementAtIndex}, {"test_listpackPop", test_listpackPop}, {"test_listpackGetELementAtIndex2", test_listpackGetELementAtIndex2}, {"test_listpackIterate0toEnd", test_listpackIterate0toEnd}, {"test_listpackIterate1toEnd", test_listpackIterate1toEnd}, {"test_listpackIterate2toEnd", test_listpackIterate2toEnd}, {"test_listpackIterateBackToFront", test_listpackIterateBackToFront}, {"test_listpackIterateBackToFrontWithDelete", test_listpackIterateBackToFrontWithDelete}, {"test_listpackDeleteWhenNumIsMinusOne", test_listpackDeleteWhenNumIsMinusOne}, {"test_listpackDeleteWithNegativeIndex", test_listpackDeleteWithNegativeIndex}, {"test_listpackDeleteInclusiveRange0_0", test_listpackDeleteInclusiveRange0_0}, {"test_listpackDeleteInclusiveRange0_1", test_listpackDeleteInclusiveRange0_1}, {"test_listpackDeleteInclusiveRange1_2", test_listpackDeleteInclusiveRange1_2}, {"test_listpackDeleteWitStartIndexOutOfRange", test_listpackDeleteWitStartIndexOutOfRange}, {"test_listpackDeleteWitNumOverflow", test_listpackDeleteWitNumOverflow}, {"test_listpackBatchDelete", test_listpackBatchDelete}, {"test_listpackDeleteFooWhileIterating", test_listpackDeleteFooWhileIterating}, {"test_listpackReplaceWithSameSize", test_listpackReplaceWithSameSize}, {"test_listpackReplaceWithDifferentSize", test_listpackReplaceWithDifferentSize}, {"test_listpackRegressionGt255Bytes", test_listpackRegressionGt255Bytes}, {"test_listpackCreateLongListAndCheckIndices", test_listpackCreateLongListAndCheckIndices}, {"test_listpackCompareStrsWithLpEntries", test_listpackCompareStrsWithLpEntries}, {"test_listpackLpMergeEmptyLps", test_listpackLpMergeEmptyLps}, {"test_listpackLpMergeLp1Larger", test_listpackLpMergeLp1Larger}, {"test_listpackLpMergeLp2Larger", test_listpackLpMergeLp2Larger}, {"test_listpackLpNextRandom", test_listpackLpNextRandom}, {"test_listpackLpNextRandomCC", test_listpackLpNextRandomCC}, {"test_listpackRandomPairWithOneElement", test_listpackRandomPairWithOneElement}, {"test_listpackRandomPairWithManyElements", test_listpackRandomPairWithManyElements}, {"test_listpackRandomPairsWithOneElement", test_listpackRandomPairsWithOneElement}, {"test_listpackRandomPairsWithManyElements", test_listpackRandomPairsWithManyElements}, {"test_listpackRandomPairsUniqueWithOneElement", test_listpackRandomPairsUniqueWithOneElement}, {"test_listpackRandomPairsUniqueWithManyElements", test_listpackRandomPairsUniqueWithManyElements}, {"test_listpackPushVariousEncodings", test_listpackPushVariousEncodings}, {"test_listpackLpFind", test_listpackLpFind}, {"test_listpackLpValidateIntegrity", test_listpackLpValidateIntegrity}, {"test_listpackNumberOfElementsExceedsLP_HDR_NUMELE_UNKNOWN", test_listpackNumberOfElementsExceedsLP_HDR_NUMELE_UNKNOWN}, {"test_listpackStressWithRandom", test_listpackStressWithRandom}, {"test_listpackSTressWithVariableSize", test_listpackSTressWithVariableSize}, {"test_listpackBenchmarkInit", test_listpackBenchmarkInit}, {"test_listpackBenchmarkLpAppend", test_listpackBenchmarkLpAppend}, {"test_listpackBenchmarkLpFindString", test_listpackBenchmarkLpFindString}, {"test_listpackBenchmarkLpFindNumber", test_listpackBenchmarkLpFindNumber}, {"test_listpackBenchmarkLpSeek", test_listpackBenchmarkLpSeek}, {"test_listpackBenchmarkLpValidateIntegrity", test_listpackBenchmarkLpValidateIntegrity}, {"test_listpackBenchmarkLpCompareWithString", test_listpackBenchmarkLpCompareWithString}, {"test_listpackBenchmarkLpCompareWithNumber", test_listpackBenchmarkLpCompareWithNumber}, {"test_listpackBenchmarkFree", test_listpackBenchmarkFree}, {NULL, NULL}}; unitTest __test_sds_c[] = {{"test_sds", test_sds}, {"test_typesAndAllocSize", test_typesAndAllocSize}, {"test_sdsHeaderSizes", test_sdsHeaderSizes}, {NULL, NULL}}; unitTest __test_sha1_c[] = {{"test_sha1", test_sha1}, {NULL, NULL}}; unitTest __test_util_c[] = {{"test_string2ll", test_string2ll}, {"test_string2l", test_string2l}, {"test_ll2string", test_ll2string}, {"test_ld2string", test_ld2string}, {"test_fixedpoint_d2string", test_fixedpoint_d2string}, {"test_version2num", test_version2num}, {"test_reclaimFilePageCache", test_reclaimFilePageCache}, {NULL, NULL}}; @@ -97,6 +150,7 @@ struct unitTestSuite { {"test_endianconv.c", __test_endianconv_c}, {"test_intset.c", __test_intset_c}, {"test_kvstore.c", __test_kvstore_c}, + {"test_listpack.c", __test_listpack_c}, {"test_sds.c", __test_sds_c}, {"test_sha1.c", __test_sha1_c}, {"test_util.c", __test_util_c}, diff --git a/src/unit/test_listpack.c b/src/unit/test_listpack.c new file mode 100644 index 000000000..4838fc895 --- /dev/null +++ b/src/unit/test_listpack.c @@ -0,0 +1,1396 @@ +#include +#include + +#include "../listpack.c" + +#include "../adlist.h" +#include "../sds.h" +#include "../zmalloc.h" +#include "test_help.h" + +char *mixlist[] = {"hello", "foo", "quux", "1024"}; +char *intlist[] = {"4294967296", "-100", "100", "128000", "non integer", "much much longer non integer"}; + +static unsigned char *createList(void) { + unsigned char *lp = lpNew(0); + lp = lpAppend(lp, (unsigned char *)mixlist[1], strlen(mixlist[1])); + lp = lpAppend(lp, (unsigned char *)mixlist[2], strlen(mixlist[2])); + lp = lpPrepend(lp, (unsigned char *)mixlist[0], strlen(mixlist[0])); + lp = lpAppend(lp, (unsigned char *)mixlist[3], strlen(mixlist[3])); + return lp; +} + +static unsigned char *createIntList(void) { + unsigned char *lp = lpNew(0); + lp = lpAppend(lp, (unsigned char *)intlist[2], strlen(intlist[2])); + lp = lpAppend(lp, (unsigned char *)intlist[3], strlen(intlist[3])); + lp = lpPrepend(lp, (unsigned char *)intlist[1], strlen(intlist[1])); + lp = lpPrepend(lp, (unsigned char *)intlist[0], strlen(intlist[0])); + lp = lpAppend(lp, (unsigned char *)intlist[4], strlen(intlist[4])); + lp = lpAppend(lp, (unsigned char *)intlist[5], strlen(intlist[5])); + return lp; +} + +static long long usec(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return (((long long)tv.tv_sec) * 1000000) + tv.tv_usec; +} + +static void stress(int pos, int num, int maxsize, int dnum) { + int i, j, k; + unsigned char *lp; + char posstr[2][5] = {"HEAD", "TAIL"}; + long long start; + for (i = 0; i < maxsize; i += dnum) { + lp = lpNew(0); + for (j = 0; j < i; j++) { + lp = lpAppend(lp, (unsigned char *)"quux", 4); + } + + /* Do num times a push+pop from pos */ + start = usec(); + for (k = 0; k < num; k++) { + if (pos == 0) { + lp = lpPrepend(lp, (unsigned char *)"quux", 4); + } else { + lp = lpAppend(lp, (unsigned char *)"quux", 4); + } + lp = lpDelete(lp, lpFirst(lp), NULL); + } + printf("List size: %8d, bytes: %8zu, %dx push+pop (%s): %6lld usec\n", i, lpBytes(lp), num, posstr[pos], + usec() - start); + lpFree(lp); + } +} + +/* simple wrapper to used in functions whose return type is not int */ +static inline int _test_assert(int c) { + TEST_ASSERT(c); + return 0; +} + +static unsigned char *pop(unsigned char *lp, int where, void *expected) { + unsigned char *p, *vstr; + int64_t vlen; + + p = lpSeek(lp, where == 0 ? 0 : -1); + vstr = lpGet(p, &vlen, NULL); + + if (vstr) { + _test_assert(strcmp((const char *)vstr, (const char *)expected)); + } else { + _test_assert(vlen == *(int64_t *)expected); + } + + return lpDelete(lp, p, &p); +} + +static int randstring(char *target, unsigned int min, unsigned int max) { + int p = 0; + int len = min + rand() % (max - min + 1); + int minval, maxval; + switch (rand() % 3) { + case 0: + minval = 0; + maxval = 255; + break; + case 1: + minval = 48; + maxval = 122; + break; + case 2: + minval = 48; + maxval = 52; + break; + default: TEST_ASSERT(NULL); + } + + while (p < len) target[p++] = minval + rand() % (maxval - minval + 1); + return len; +} + +static int verifyEntry(unsigned char *p, unsigned char *s, size_t slen) { + TEST_ASSERT(lpCompare(p, s, slen)); + return 0; +} + +static int lpValidation(unsigned char *p, unsigned int head_count, void *userdata) { + UNUSED(p); + UNUSED(head_count); + + int ret; + long *count = userdata; + ret = lpCompare(p, (unsigned char *)mixlist[*count], strlen(mixlist[*count])); + (*count)++; + return ret; +} + +int test_listpackCreateIntList(int argc, char **argv, int flags) { + /* Create int list */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = createIntList(); + TEST_ASSERT(lpLength(lp) == 6); + lpFree(lp); + + return 0; +} + +int test_listpackCreateList(int argc, char **argv, int flags) { + /* Create list */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = createList(); + TEST_ASSERT(lpLength(lp) == 4); + lpFree(lp); + + return 0; +} + +int test_listpackLpPrepend(int argc, char **argv, int flags) { + /* Test lpPrepend */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = lpNew(0); + lp = lpPrepend(lp, (unsigned char *)"abc", 3); + lp = lpPrepend(lp, (unsigned char *)"1024", 4); + verifyEntry(lpSeek(lp, 0), (unsigned char *)"1024", 4); + verifyEntry(lpSeek(lp, 1), (unsigned char *)"abc", 3); + lpFree(lp); + + return 0; +} + +int test_listpackLpPrependInteger(int argc, char **argv, int flags) { + /* Test lpPrependInteger */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = lpNew(0); + lp = lpPrependInteger(lp, 127); + lp = lpPrependInteger(lp, 4095); + lp = lpPrependInteger(lp, 32767); + lp = lpPrependInteger(lp, 8388607); + lp = lpPrependInteger(lp, 2147483647); + lp = lpPrependInteger(lp, 9223372036854775807); + verifyEntry(lpSeek(lp, 0), (unsigned char *)"9223372036854775807", 19); + verifyEntry(lpSeek(lp, -1), (unsigned char *)"127", 3); + lpFree(lp); + + return 0; +} + +int test_listpackGetELementAtIndex(int argc, char **argv, int flags) { + /* Get element at index */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = createList(); + verifyEntry(lpSeek(lp, 0), (unsigned char *)"hello", 5); + verifyEntry(lpSeek(lp, 3), (unsigned char *)"1024", 4); + verifyEntry(lpSeek(lp, -1), (unsigned char *)"1024", 4); + verifyEntry(lpSeek(lp, -4), (unsigned char *)"hello", 5); + TEST_ASSERT(lpSeek(lp, 4) == NULL); + TEST_ASSERT(lpSeek(lp, -5) == NULL); + lpFree(lp); + + return 0; +} + +int test_listpackPop(int argc, char **argv, int flags) { + /* Pop list */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + int64_t expected; + + lp = createList(); + expected = 1024; + lp = pop(lp, 1, &expected); + lp = pop(lp, 0, "hello"); + lp = pop(lp, 1, "quux"); + lp = pop(lp, 1, "foo"); + lpFree(lp); + + return 0; +} + +int test_listpackGetELementAtIndex2(int argc, char **argv, int flags) { + /* Get element at index */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = createList(); + verifyEntry(lpSeek(lp, 0), (unsigned char *)"hello", 5); + verifyEntry(lpSeek(lp, 3), (unsigned char *)"1024", 4); + verifyEntry(lpSeek(lp, -1), (unsigned char *)"1024", 4); + verifyEntry(lpSeek(lp, -4), (unsigned char *)"hello", 5); + TEST_ASSERT(lpSeek(lp, 4) == NULL); + TEST_ASSERT(lpSeek(lp, -5) == NULL); + lpFree(lp); + + return 0; +} + +int test_listpackIterate0toEnd(int argc, char **argv, int flags) { + /* Iterate list from 0 to end */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + int i; + unsigned char *lp, *p; + + lp = createList(); + p = lpFirst(lp); + i = 0; + while (p) { + verifyEntry(p, (unsigned char *)mixlist[i], strlen(mixlist[i])); + p = lpNext(lp, p); + i++; + } + lpFree(lp); + + return 0; +} + +int test_listpackIterate1toEnd(int argc, char **argv, int flags) { + /* Iterate list from 1 to end */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + int i; + unsigned char *lp, *p; + + lp = createList(); + i = 1; + p = lpSeek(lp, i); + while (p) { + verifyEntry(p, (unsigned char *)mixlist[i], strlen(mixlist[i])); + p = lpNext(lp, p); + i++; + } + lpFree(lp); + + return 0; +} + +int test_listpackIterate2toEnd(int argc, char **argv, int flags) { + /* Iterate list from 2 to end */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + int i; + unsigned char *lp, *p; + + lp = createList(); + i = 2; + p = lpSeek(lp, i); + while (p) { + verifyEntry(p, (unsigned char *)mixlist[i], strlen(mixlist[i])); + p = lpNext(lp, p); + i++; + } + lpFree(lp); + + return 0; +} + +int test_listpackIterateBackToFront(int argc, char **argv, int flags) { + /* Iterate from back to front */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + int i; + unsigned char *lp, *p; + + lp = createList(); + p = lpLast(lp); + i = 3; + while (p) { + verifyEntry(p, (unsigned char *)mixlist[i], strlen(mixlist[i])); + p = lpPrev(lp, p); + i--; + } + lpFree(lp); + + return 0; +} + +int test_listpackIterateBackToFrontWithDelete(int argc, char **argv, int flags) { + /* Iterate from back to front, deleting all items */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + int i; + unsigned char *lp, *p; + + lp = createList(); + p = lpLast(lp); + i = 3; + while ((p = lpLast(lp))) { + verifyEntry(p, (unsigned char *)mixlist[i], strlen(mixlist[i])); + lp = lpDelete(lp, p, &p); + TEST_ASSERT(p == NULL); + i--; + } + lpFree(lp); + + return 0; +} + +int test_listpackDeleteWhenNumIsMinusOne(int argc, char **argv, int flags) { + /* Delete whole listpack when num == -1 */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = createList(); + lp = lpDeleteRange(lp, 0, -1); + TEST_ASSERT(lpLength(lp) == 0); + TEST_ASSERT(lp[LP_HDR_SIZE] == LP_EOF); + TEST_ASSERT(lpBytes(lp) == (LP_HDR_SIZE + 1)); + zfree(lp); + + lp = createList(); + unsigned char *ptr = lpFirst(lp); + lp = lpDeleteRangeWithEntry(lp, &ptr, -1); + TEST_ASSERT(lpLength(lp) == 0); + TEST_ASSERT(lp[LP_HDR_SIZE] == LP_EOF); + TEST_ASSERT(lpBytes(lp) == (LP_HDR_SIZE + 1)); + zfree(lp); + + return 0; +} + +int test_listpackDeleteWithNegativeIndex(int argc, char **argv, int flags) { + /* Delete whole listpack with negative index */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = createList(); + lp = lpDeleteRange(lp, -4, 4); + TEST_ASSERT(lpLength(lp) == 0); + TEST_ASSERT(lp[LP_HDR_SIZE] == LP_EOF); + TEST_ASSERT(lpBytes(lp) == (LP_HDR_SIZE + 1)); + zfree(lp); + + lp = createList(); + unsigned char *ptr = lpSeek(lp, -4); + lp = lpDeleteRangeWithEntry(lp, &ptr, 4); + TEST_ASSERT(lpLength(lp) == 0); + TEST_ASSERT(lp[LP_HDR_SIZE] == LP_EOF); + TEST_ASSERT(lpBytes(lp) == (LP_HDR_SIZE + 1)); + zfree(lp); + + return 0; +} + +int test_listpackDeleteInclusiveRange0_0(int argc, char **argv, int flags) { + /* Delete inclusive range 0,0 */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = createList(); + lp = lpDeleteRange(lp, 0, 1); + TEST_ASSERT(lpLength(lp) == 3); + TEST_ASSERT(lpSkip(lpLast(lp))[0] == LP_EOF); /* check set LP_EOF correctly */ + zfree(lp); + + lp = createList(); + unsigned char *ptr = lpFirst(lp); + lp = lpDeleteRangeWithEntry(lp, &ptr, 1); + TEST_ASSERT(lpLength(lp) == 3); + TEST_ASSERT(lpSkip(lpLast(lp))[0] == LP_EOF); /* check set LP_EOF correctly */ + zfree(lp); + + return 0; +} + +int test_listpackDeleteInclusiveRange0_1(int argc, char **argv, int flags) { + /* Delete inclusive range 0,1 */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = createList(); + lp = lpDeleteRange(lp, 0, 2); + TEST_ASSERT(lpLength(lp) == 2); + verifyEntry(lpFirst(lp), (unsigned char *)mixlist[2], strlen(mixlist[2])); + zfree(lp); + + lp = createList(); + unsigned char *ptr = lpFirst(lp); + lp = lpDeleteRangeWithEntry(lp, &ptr, 2); + TEST_ASSERT(lpLength(lp) == 2); + verifyEntry(lpFirst(lp), (unsigned char *)mixlist[2], strlen(mixlist[2])); + zfree(lp); + + return 0; +} + +int test_listpackDeleteInclusiveRange1_2(int argc, char **argv, int flags) { + /* Delete inclusive range 1,2 */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = createList(); + lp = lpDeleteRange(lp, 1, 2); + TEST_ASSERT(lpLength(lp) == 2); + verifyEntry(lpFirst(lp), (unsigned char *)mixlist[0], strlen(mixlist[0])); + zfree(lp); + + lp = createList(); + unsigned char *ptr = lpSeek(lp, 1); + lp = lpDeleteRangeWithEntry(lp, &ptr, 2); + TEST_ASSERT(lpLength(lp) == 2); + verifyEntry(lpFirst(lp), (unsigned char *)mixlist[0], strlen(mixlist[0])); + zfree(lp); + + return 0; +} + +int test_listpackDeleteWitStartIndexOutOfRange(int argc, char **argv, int flags) { + /* Delete with start index out of range */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = createList(); + lp = lpDeleteRange(lp, 5, 1); + TEST_ASSERT(lpLength(lp) == 4); + zfree(lp); + + return 0; +} + +int test_listpackDeleteWitNumOverflow(int argc, char **argv, int flags) { + /* Delete with num overflow */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = createList(); + lp = lpDeleteRange(lp, 1, 5); + TEST_ASSERT(lpLength(lp) == 1); + verifyEntry(lpFirst(lp), (unsigned char *)mixlist[0], strlen(mixlist[0])); + zfree(lp); + + lp = createList(); + unsigned char *ptr = lpSeek(lp, 1); + lp = lpDeleteRangeWithEntry(lp, &ptr, 5); + TEST_ASSERT(lpLength(lp) == 1); + verifyEntry(lpFirst(lp), (unsigned char *)mixlist[0], strlen(mixlist[0])); + zfree(lp); + + return 0; +} + +int test_listpackBatchDelete(int argc, char **argv, int flags) { + /* Batch delete */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp = createList(); /* char *mixlist[] = {"hello", "foo", "quux", "1024"} */ + TEST_ASSERT(lpLength(lp) == 4); /* Pre-condition */ + unsigned char *p0 = lpFirst(lp), *p1 = lpNext(lp, p0), *p2 = lpNext(lp, p1), *p3 = lpNext(lp, p2); + unsigned char *ps[] = {p0, p1, p3}; + lp = lpBatchDelete(lp, ps, 3); + TEST_ASSERT(lpLength(lp) == 1); + verifyEntry(lpFirst(lp), (unsigned char *)mixlist[2], strlen(mixlist[2])); + TEST_ASSERT(lpValidateIntegrity(lp, lpBytes(lp), 1, NULL, NULL) == 1); + lpFree(lp); + + return 0; +} + +int test_listpackDeleteFooWhileIterating(int argc, char **argv, int flags) { + /* Delete foo while iterating */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp, *p; + + lp = createList(); + p = lpFirst(lp); + while (p) { + if (lpCompare(p, (unsigned char *)"foo", 3)) { + lp = lpDelete(lp, p, &p); + } else { + p = lpNext(lp, p); + } + } + lpFree(lp); + + return 0; +} + +int test_listpackReplaceWithSameSize(int argc, char **argv, int flags) { + /* Replace with same size */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp, *p, *orig_lp; + + orig_lp = lp = createList(); /* "hello", "foo", "quux", "1024" */ + p = lpSeek(lp, 0); + lp = lpReplace(lp, &p, (unsigned char *)"zoink", 5); + p = lpSeek(lp, 3); + lp = lpReplace(lp, &p, (unsigned char *)"y", 1); + p = lpSeek(lp, 1); + lp = lpReplace(lp, &p, (unsigned char *)"65536", 5); + p = lpSeek(lp, 0); + TEST_ASSERT(!memcmp((char *)p, + "\x85zoink\x06" + "\xf2\x00\x00\x01\x04" /* 65536 as int24 */ + "\x84quux\05" + "\x81y\x02" + "\xff", + 22)); + TEST_ASSERT(lp == orig_lp); /* no reallocations have happened */ + lpFree(lp); + + return 0; +} + +int test_listpackReplaceWithDifferentSize(int argc, char **argv, int flags) { + /* Replace with different size */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp, *p; + + lp = createList(); /* "hello", "foo", "quux", "1024" */ + p = lpSeek(lp, 1); + lp = lpReplace(lp, &p, (unsigned char *)"squirrel", 8); + p = lpSeek(lp, 0); + TEST_ASSERT(!strncmp((char *)p, + "\x85hello\x06" + "\x88squirrel\x09" + "\x84quux\x05" + "\xc4\x00\x02" + "\xff", + 27)); + lpFree(lp); + + return 0; +} + +int test_listpackRegressionGt255Bytes(int argc, char **argv, int flags) { + /* Regression test for >255 byte strings */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp, *p, *vstr; + int64_t vlen; + + char v1[257] = {0}, v2[257] = {0}; + memset(v1, 'x', 256); + memset(v2, 'y', 256); + lp = lpNew(0); + lp = lpAppend(lp, (unsigned char *)v1, strlen(v1)); + lp = lpAppend(lp, (unsigned char *)v2, strlen(v2)); + + /* Pop values again and compare their value. */ + p = lpFirst(lp); + vstr = lpGet(p, &vlen, NULL); + TEST_ASSERT(strncmp(v1, (char *)vstr, vlen) == 0); + p = lpSeek(lp, 1); + vstr = lpGet(p, &vlen, NULL); + TEST_ASSERT(strncmp(v2, (char *)vstr, vlen) == 0); + lpFree(lp); + + return 0; +} + +int test_listpackCreateLongListAndCheckIndices(int argc, char **argv, int flags) { + /* Create long list and check indices */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp, *p; + int64_t vlen; + + lp = lpNew(0); + char buf[32]; + int i, len; + for (i = 0; i < 1000; i++) { + len = snprintf(buf, sizeof(buf), "%d", i); + lp = lpAppend(lp, (unsigned char *)buf, len); + } + for (i = 0; i < 1000; i++) { + p = lpSeek(lp, i); + lpGet(p, &vlen, NULL); + TEST_ASSERT(i == vlen); + + p = lpSeek(lp, -i - 1); + lpGet(p, &vlen, NULL); + TEST_ASSERT(999 - i == vlen); + } + lpFree(lp); + + return 0; +} + +int test_listpackCompareStrsWithLpEntries(int argc, char **argv, int flags) { + /* Compare strings with listpack entries */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp, *p; + + lp = createList(); + p = lpSeek(lp, 0); + TEST_ASSERT(lpCompare(p, (unsigned char *)"hello", 5)); + TEST_ASSERT(!lpCompare(p, (unsigned char *)"hella", 5)); + + p = lpSeek(lp, 3); + TEST_ASSERT(lpCompare(p, (unsigned char *)"1024", 4)); + TEST_ASSERT(!lpCompare(p, (unsigned char *)"1025", 4)); + lpFree(lp); + + return 0; +} + +int test_listpackLpMergeEmptyLps(int argc, char **argv, int flags) { + /* lpMerge two empty listpacks */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp1 = lpNew(0); + unsigned char *lp2 = lpNew(0); + + /* Merge two empty listpacks, get empty result back. */ + lp1 = lpMerge(&lp1, &lp2); + TEST_ASSERT(lpLength(lp1) == 0); + zfree(lp1); + + return 0; +} + +int test_listpackLpMergeLp1Larger(int argc, char **argv, int flags) { + /* lpMerge two listpacks - first larger than second */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp1 = createIntList(); + unsigned char *lp2 = createList(); + + size_t lp1_bytes = lpBytes(lp1); + size_t lp2_bytes = lpBytes(lp2); + unsigned long lp1_len = lpLength(lp1); + unsigned long lp2_len = lpLength(lp2); + + unsigned char *lp3 = lpMerge(&lp1, &lp2); + TEST_ASSERT(lp3 == lp1); + TEST_ASSERT(lp2 == NULL); + TEST_ASSERT(lpLength(lp3) == (lp1_len + lp2_len)); + TEST_ASSERT(lpBytes(lp3) == (lp1_bytes + lp2_bytes - LP_HDR_SIZE - 1)); + verifyEntry(lpSeek(lp3, 0), (unsigned char *)"4294967296", 10); + verifyEntry(lpSeek(lp3, 5), (unsigned char *)"much much longer non integer", 28); + verifyEntry(lpSeek(lp3, 6), (unsigned char *)"hello", 5); + verifyEntry(lpSeek(lp3, -1), (unsigned char *)"1024", 4); + zfree(lp3); + + return 0; +} + +int test_listpackLpMergeLp2Larger(int argc, char **argv, int flags) { + /* lpMerge two listpacks - second larger than first */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp1 = createList(); + unsigned char *lp2 = createIntList(); + + size_t lp1_bytes = lpBytes(lp1); + size_t lp2_bytes = lpBytes(lp2); + unsigned long lp1_len = lpLength(lp1); + unsigned long lp2_len = lpLength(lp2); + + unsigned char *lp3 = lpMerge(&lp1, &lp2); + TEST_ASSERT(lp3 == lp2); + TEST_ASSERT(lp1 == NULL); + TEST_ASSERT(lpLength(lp3) == (lp1_len + lp2_len)); + TEST_ASSERT(lpBytes(lp3) == (lp1_bytes + lp2_bytes - LP_HDR_SIZE - 1)); + verifyEntry(lpSeek(lp3, 0), (unsigned char *)"hello", 5); + verifyEntry(lpSeek(lp3, 3), (unsigned char *)"1024", 4); + verifyEntry(lpSeek(lp3, 4), (unsigned char *)"4294967296", 10); + verifyEntry(lpSeek(lp3, -1), (unsigned char *)"much much longer non integer", 28); + zfree(lp3); + + return 0; +} + +int test_listpackLpNextRandom(int argc, char **argv, int flags) { + /* lpNextRandom normal usage */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + /* Create some data */ + unsigned char *lp = lpNew(0); + unsigned char buf[100] = "asdf"; + unsigned int size = 100; + for (size_t i = 0; i < size; i++) { + lp = lpAppend(lp, buf, i); + } + TEST_ASSERT(lpLength(lp) == size); + + /* Pick a subset of the elements of every possible subset size */ + for (unsigned int count = 0; count <= size; count++) { + unsigned int remaining = count; + unsigned char *p = lpFirst(lp); + unsigned char *prev = NULL; + unsigned index = 0; + while (remaining > 0) { + TEST_ASSERT(p != NULL); + p = lpNextRandom(lp, p, &index, remaining--, 0); + TEST_ASSERT(p != NULL); + TEST_ASSERT(p != prev); + prev = p; + p = lpNext(lp, p); + index++; + } + } + lpFree(lp); + + return 0; +} + +int test_listpackLpNextRandomCC(int argc, char **argv, int flags) { + /* lpNextRandom corner cases */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp = lpNew(0); + unsigned i = 0; + + /* Pick from empty listpack returns NULL. */ + TEST_ASSERT(lpNextRandom(lp, NULL, &i, 2, 0) == NULL); + + /* Add some elements and find their pointers within the listpack. */ + lp = lpAppend(lp, (unsigned char *)"abc", 3); + lp = lpAppend(lp, (unsigned char *)"def", 3); + lp = lpAppend(lp, (unsigned char *)"ghi", 3); + TEST_ASSERT(lpLength(lp) == 3); + unsigned char *p0 = lpFirst(lp); + unsigned char *p1 = lpNext(lp, p0); + unsigned char *p2 = lpNext(lp, p1); + TEST_ASSERT(lpNext(lp, p2) == NULL); + + /* Pick zero elements returns NULL. */ + i = 0; + TEST_ASSERT(lpNextRandom(lp, lpFirst(lp), &i, 0, 0) == NULL); + + /* Pick all returns all. */ + i = 0; + TEST_ASSERT(lpNextRandom(lp, p0, &i, 3, 0) == p0 && i == 0); + i = 1; + TEST_ASSERT(lpNextRandom(lp, p1, &i, 2, 0) == p1 && i == 1); + i = 2; + TEST_ASSERT(lpNextRandom(lp, p2, &i, 1, 0) == p2 && i == 2); + + /* Pick more than one when there's only one left returns the last one. */ + i = 2; + TEST_ASSERT(lpNextRandom(lp, p2, &i, 42, 0) == p2 && i == 2); + + /* Pick all even elements returns p0 and p2. */ + i = 0; + TEST_ASSERT(lpNextRandom(lp, p0, &i, 10, 1) == p0 && i == 0); + i = 1; + TEST_ASSERT(lpNextRandom(lp, p1, &i, 10, 1) == p2 && i == 2); + + /* Don't crash even for bad index. */ + for (int j = 0; j < 100; j++) { + unsigned char *p; + switch (j % 4) { + case 0: p = p0; break; + case 1: p = p1; break; + case 2: p = p2; break; + case 3: p = NULL; break; + } + i = j % 7; + unsigned int remaining = j % 5; + p = lpNextRandom(lp, p, &i, remaining, 0); + TEST_ASSERT(p == p0 || p == p1 || p == p2 || p == NULL); + } + lpFree(lp); + + return 0; +} + +int test_listpackRandomPairWithOneElement(int argc, char **argv, int flags) { + /* Random pair with one element */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + listpackEntry key, val; + unsigned char *lp = lpNew(0); + lp = lpAppend(lp, (unsigned char *)"abc", 3); + lp = lpAppend(lp, (unsigned char *)"123", 3); + lpRandomPair(lp, 1, &key, &val); + TEST_ASSERT(memcmp(key.sval, "abc", key.slen) == 0); + TEST_ASSERT(val.lval == 123); + lpFree(lp); + + return 0; +} + +int test_listpackRandomPairWithManyElements(int argc, char **argv, int flags) { + /* Random pair with many elements */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + listpackEntry key, val; + unsigned char *lp = lpNew(0); + lp = lpAppend(lp, (unsigned char *)"abc", 3); + lp = lpAppend(lp, (unsigned char *)"123", 3); + lp = lpAppend(lp, (unsigned char *)"456", 3); + lp = lpAppend(lp, (unsigned char *)"def", 3); + lpRandomPair(lp, 2, &key, &val); + if (key.sval) { + TEST_ASSERT(!memcmp(key.sval, "abc", key.slen)); + TEST_ASSERT(key.slen == 3); + TEST_ASSERT(val.lval == 123); + } + if (!key.sval) { + TEST_ASSERT(key.lval == 456); + TEST_ASSERT(!memcmp(val.sval, "def", val.slen)); + } + lpFree(lp); + + return 0; +} + +int test_listpackRandomPairsWithOneElement(int argc, char **argv, int flags) { + /* Random pairs with one element */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + int count = 5; + unsigned char *lp = lpNew(0); + listpackEntry *keys = zmalloc(sizeof(listpackEntry) * count); + listpackEntry *vals = zmalloc(sizeof(listpackEntry) * count); + + lp = lpAppend(lp, (unsigned char *)"abc", 3); + lp = lpAppend(lp, (unsigned char *)"123", 3); + lpRandomPairs(lp, count, keys, vals); + TEST_ASSERT(memcmp(keys[4].sval, "abc", keys[4].slen) == 0); + TEST_ASSERT(vals[4].lval == 123); + zfree(keys); + zfree(vals); + lpFree(lp); + + return 0; +} + +int test_listpackRandomPairsWithManyElements(int argc, char **argv, int flags) { + /* Random pairs with many elements */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + int count = 5; + lp = lpNew(0); + listpackEntry *keys = zmalloc(sizeof(listpackEntry) * count); + listpackEntry *vals = zmalloc(sizeof(listpackEntry) * count); + + lp = lpAppend(lp, (unsigned char *)"abc", 3); + lp = lpAppend(lp, (unsigned char *)"123", 3); + lp = lpAppend(lp, (unsigned char *)"456", 3); + lp = lpAppend(lp, (unsigned char *)"def", 3); + lpRandomPairs(lp, count, keys, vals); + for (int i = 0; i < count; i++) { + if (keys[i].sval) { + TEST_ASSERT(!memcmp(keys[i].sval, "abc", keys[i].slen)); + TEST_ASSERT(keys[i].slen == 3); + TEST_ASSERT(vals[i].lval == 123); + } + if (!keys[i].sval) { + TEST_ASSERT(keys[i].lval == 456); + TEST_ASSERT(!memcmp(vals[i].sval, "def", vals[i].slen)); + } + } + zfree(keys); + zfree(vals); + lpFree(lp); + + return 0; +} + +int test_listpackRandomPairsUniqueWithOneElement(int argc, char **argv, int flags) { + /* Random pairs unique with one element */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + unsigned picked; + int count = 5; + lp = lpNew(0); + listpackEntry *keys = zmalloc(sizeof(listpackEntry) * count); + listpackEntry *vals = zmalloc(sizeof(listpackEntry) * count); + + lp = lpAppend(lp, (unsigned char *)"abc", 3); + lp = lpAppend(lp, (unsigned char *)"123", 3); + picked = lpRandomPairsUnique(lp, count, keys, vals); + TEST_ASSERT(picked == 1); + TEST_ASSERT(memcmp(keys[0].sval, "abc", keys[0].slen) == 0); + TEST_ASSERT(vals[0].lval == 123); + zfree(keys); + zfree(vals); + lpFree(lp); + + return 0; +} + +int test_listpackRandomPairsUniqueWithManyElements(int argc, char **argv, int flags) { + /* Random pairs unique with many elements */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + unsigned picked; + int count = 5; + lp = lpNew(0); + listpackEntry *keys = zmalloc(sizeof(listpackEntry) * count); + listpackEntry *vals = zmalloc(sizeof(listpackEntry) * count); + + lp = lpAppend(lp, (unsigned char *)"abc", 3); + lp = lpAppend(lp, (unsigned char *)"123", 3); + lp = lpAppend(lp, (unsigned char *)"456", 3); + lp = lpAppend(lp, (unsigned char *)"def", 3); + picked = lpRandomPairsUnique(lp, count, keys, vals); + TEST_ASSERT(picked == 2); + for (int i = 0; i < 2; i++) { + if (keys[i].sval) { + TEST_ASSERT(!memcmp(keys[i].sval, "abc", keys[i].slen)); + TEST_ASSERT(keys[i].slen == 3); + TEST_ASSERT(vals[i].lval == 123); + } + if (!keys[i].sval) { + TEST_ASSERT(keys[i].lval == 456); + TEST_ASSERT(!memcmp(vals[i].sval, "def", vals[i].slen)); + } + } + zfree(keys); + zfree(vals); + lpFree(lp); + + return 0; +} + +int test_listpackPushVariousEncodings(int argc, char **argv, int flags) { + /* push various encodings */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = lpNew(0); + + /* Push integer encode element using lpAppend */ + lp = lpAppend(lp, (unsigned char *)"127", 3); + TEST_ASSERT(LP_ENCODING_IS_7BIT_UINT(lpLast(lp)[0])); + lp = lpAppend(lp, (unsigned char *)"4095", 4); + TEST_ASSERT(LP_ENCODING_IS_13BIT_INT(lpLast(lp)[0])); + lp = lpAppend(lp, (unsigned char *)"32767", 5); + TEST_ASSERT(LP_ENCODING_IS_16BIT_INT(lpLast(lp)[0])); + lp = lpAppend(lp, (unsigned char *)"8388607", 7); + TEST_ASSERT(LP_ENCODING_IS_24BIT_INT(lpLast(lp)[0])); + lp = lpAppend(lp, (unsigned char *)"2147483647", 10); + TEST_ASSERT(LP_ENCODING_IS_32BIT_INT(lpLast(lp)[0])); + lp = lpAppend(lp, (unsigned char *)"9223372036854775807", 19); + TEST_ASSERT(LP_ENCODING_IS_64BIT_INT(lpLast(lp)[0])); + + /* Push integer encode element using lpAppendInteger */ + lp = lpAppendInteger(lp, 127); + TEST_ASSERT(LP_ENCODING_IS_7BIT_UINT(lpLast(lp)[0])); + verifyEntry(lpLast(lp), (unsigned char *)"127", 3); + lp = lpAppendInteger(lp, 4095); + verifyEntry(lpLast(lp), (unsigned char *)"4095", 4); + TEST_ASSERT(LP_ENCODING_IS_13BIT_INT(lpLast(lp)[0])); + lp = lpAppendInteger(lp, 32767); + verifyEntry(lpLast(lp), (unsigned char *)"32767", 5); + TEST_ASSERT(LP_ENCODING_IS_16BIT_INT(lpLast(lp)[0])); + lp = lpAppendInteger(lp, 8388607); + verifyEntry(lpLast(lp), (unsigned char *)"8388607", 7); + TEST_ASSERT(LP_ENCODING_IS_24BIT_INT(lpLast(lp)[0])); + lp = lpAppendInteger(lp, 2147483647); + verifyEntry(lpLast(lp), (unsigned char *)"2147483647", 10); + TEST_ASSERT(LP_ENCODING_IS_32BIT_INT(lpLast(lp)[0])); + lp = lpAppendInteger(lp, 9223372036854775807); + verifyEntry(lpLast(lp), (unsigned char *)"9223372036854775807", 19); + TEST_ASSERT(LP_ENCODING_IS_64BIT_INT(lpLast(lp)[0])); + + /* string encode */ + unsigned char *str = zmalloc(65535); + memset(str, 0, 65535); + lp = lpAppend(lp, (unsigned char *)str, 63); + TEST_ASSERT(LP_ENCODING_IS_6BIT_STR(lpLast(lp)[0])); + lp = lpAppend(lp, (unsigned char *)str, 4095); + TEST_ASSERT(LP_ENCODING_IS_12BIT_STR(lpLast(lp)[0])); + lp = lpAppend(lp, (unsigned char *)str, 65535); + TEST_ASSERT(LP_ENCODING_IS_32BIT_STR(lpLast(lp)[0])); + zfree(str); + lpFree(lp); + + return 0; +} + +int test_listpackLpFind(int argc, char **argv, int flags) { + /* Test lpFind */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = createList(); + TEST_ASSERT(lpFind(lp, lpFirst(lp), (unsigned char *)"abc", 3, 0) == NULL); + verifyEntry(lpFind(lp, lpFirst(lp), (unsigned char *)"hello", 5, 0), (unsigned char *)"hello", 5); + verifyEntry(lpFind(lp, lpFirst(lp), (unsigned char *)"1024", 4, 0), (unsigned char *)"1024", 4); + lpFree(lp); + + return 0; +} + +int test_listpackLpValidateIntegrity(int argc, char **argv, int flags) { + /* Test lpValidateIntegrity */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = createList(); + long count = 0; + TEST_ASSERT(lpValidateIntegrity(lp, lpBytes(lp), 1, lpValidation, &count) == 1); + lpFree(lp); + + return 0; +} + +int test_listpackNumberOfElementsExceedsLP_HDR_NUMELE_UNKNOWN(int argc, char **argv, int flags) { + /* Test number of elements exceeds LP_HDR_NUMELE_UNKNOWN */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp; + + lp = lpNew(0); + for (int i = 0; i < LP_HDR_NUMELE_UNKNOWN + 1; i++) lp = lpAppend(lp, (unsigned char *)"1", 1); + + TEST_ASSERT(lpGetNumElements(lp) == LP_HDR_NUMELE_UNKNOWN); + TEST_ASSERT(lpLength(lp) == LP_HDR_NUMELE_UNKNOWN + 1); + + lp = lpDeleteRange(lp, -2, 2); + TEST_ASSERT(lpGetNumElements(lp) == LP_HDR_NUMELE_UNKNOWN); + TEST_ASSERT(lpLength(lp) == LP_HDR_NUMELE_UNKNOWN - 1); + TEST_ASSERT(lpGetNumElements(lp) == LP_HDR_NUMELE_UNKNOWN - 1); /* update length after lpLength */ + lpFree(lp); + + return 0; +} + +int test_listpackStressWithRandom(int argc, char **argv, int flags) { + /* Stress with random payloads of different encoding */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned char *lp, *vstr; + int64_t vlen; + unsigned char intbuf[LP_INTBUF_SIZE]; + + int accurate = (flags & UNIT_TEST_ACCURATE); + + unsigned long long start = usec(); + int i, j, len, where; + unsigned char *p; + char buf[1024]; + int buflen; + list *ref; + listNode *refnode; + + int iteration = accurate ? 20000 : 20; + for (i = 0; i < iteration; i++) { + lp = lpNew(0); + ref = listCreate(); + listSetFreeMethod(ref, (void (*)(void *))sdsfree); + len = rand() % 256; + + /* Create lists */ + for (j = 0; j < len; j++) { + where = (rand() & 1) ? 0 : 1; + if (rand() % 2) { + buflen = randstring(buf, 1, sizeof(buf) - 1); + } else { + switch (rand() % 3) { + case 0: buflen = snprintf(buf, sizeof(buf), "%lld", (0LL + rand()) >> 20); break; + case 1: buflen = snprintf(buf, sizeof(buf), "%lld", (0LL + rand())); break; + case 2: buflen = snprintf(buf, sizeof(buf), "%lld", (0LL + rand()) << 20); break; + default: TEST_ASSERT(NULL); + } + } + + /* Add to listpack */ + if (where == 0) { + lp = lpPrepend(lp, (unsigned char *)buf, buflen); + } else { + lp = lpAppend(lp, (unsigned char *)buf, buflen); + } + + /* Add to reference list */ + if (where == 0) { + listAddNodeHead(ref, sdsnewlen(buf, buflen)); + } else if (where == 1) { + listAddNodeTail(ref, sdsnewlen(buf, buflen)); + } else { + TEST_ASSERT(NULL); + } + } + + TEST_ASSERT(listLength(ref) == lpLength(lp)); + for (j = 0; j < len; j++) { + /* Naive way to get elements, but similar to the stresser + * executed from the Tcl test suite. */ + p = lpSeek(lp, j); + refnode = listIndex(ref, j); + + vstr = lpGet(p, &vlen, intbuf); + TEST_ASSERT(memcmp(vstr, listNodeValue(refnode), vlen) == 0); + } + lpFree(lp); + listRelease(ref); + } + printf("Done. usec=%lld\n\n", usec() - start); + + return 0; +} + +int test_listpackSTressWithVariableSize(int argc, char **argv, int flags) { + /* Stress with variable listpack size */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + int accurate = (flags & UNIT_TEST_ACCURATE); + + unsigned long long start = usec(); + int maxsize = accurate ? 16384 : 16; + stress(0, 100000, maxsize, 256); + stress(1, 100000, maxsize, 256); + printf("Done. usec=%lld\n\n", usec() - start); + + return 0; +} + +/* Benchmarks */ +static unsigned char *lp; +static int accurate, iteration; + +int test_listpackBenchmarkInit(int argc, char *argv[], int flags) { + UNUSED(argc); + UNUSED(argv); + + accurate = (flags & UNIT_TEST_ACCURATE); + iteration = accurate ? 100000 : 100; + lp = lpNew(0); + + return 0; +} + +int test_listpackBenchmarkLpAppend(int argc, char **argv, int flags) { + /* Benchmark lpAppend */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned long long start = usec(); + for (int i = 0; i < iteration; i++) { + char buf[4096] = "asdf"; + lp = lpAppend(lp, (unsigned char *)buf, 4); + lp = lpAppend(lp, (unsigned char *)buf, 40); + lp = lpAppend(lp, (unsigned char *)buf, 400); + lp = lpAppend(lp, (unsigned char *)buf, 4000); + lp = lpAppend(lp, (unsigned char *)"1", 1); + lp = lpAppend(lp, (unsigned char *)"10", 2); + lp = lpAppend(lp, (unsigned char *)"100", 3); + lp = lpAppend(lp, (unsigned char *)"1000", 4); + lp = lpAppend(lp, (unsigned char *)"10000", 5); + lp = lpAppend(lp, (unsigned char *)"100000", 6); + } + printf("Done. usec=%lld\n", usec() - start); + return 0; +} + +int test_listpackBenchmarkLpFindString(int argc, char **argv, int flags) { + /* Benchmark lpFind string */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned long long start = usec(); + for (int i = 0; i < 2000; i++) { + unsigned char *fptr = lpFirst(lp); + fptr = lpFind(lp, fptr, (unsigned char *)"nothing", 7, 1); + } + printf("Done. usec=%lld\n", usec() - start); + return 0; +} + +int test_listpackBenchmarkLpFindNumber(int argc, char **argv, int flags) { + /* Benchmark lpFind number */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned long long start = usec(); + for (int i = 0; i < 2000; i++) { + unsigned char *fptr = lpFirst(lp); + fptr = lpFind(lp, fptr, (unsigned char *)"99999", 5, 1); + } + printf("Done. usec=%lld\n", usec() - start); + return 0; +} + +int test_listpackBenchmarkLpSeek(int argc, char **argv, int flags) { + /* Benchmark lpSeek */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned long long start = usec(); + for (int i = 0; i < 2000; i++) { + lpSeek(lp, 99999); + } + printf("Done. usec=%lld\n", usec() - start); + return 0; +} + +int test_listpackBenchmarkLpValidateIntegrity(int argc, char **argv, int flags) { + /* Benchmark lpValidateIntegrity */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned long long start = usec(); + for (int i = 0; i < 2000; i++) { + lpValidateIntegrity(lp, lpBytes(lp), 1, NULL, NULL); + } + printf("Done. usec=%lld\n", usec() - start); + return 0; +} + +int test_listpackBenchmarkLpCompareWithString(int argc, char **argv, int flags) { + /* Benchmark lpCompare with string */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned long long start = usec(); + for (int i = 0; i < 2000; i++) { + unsigned char *eptr = lpSeek(lp, 0); + while (eptr != NULL) { + lpCompare(eptr, (unsigned char *)"nothing", 7); + eptr = lpNext(lp, eptr); + } + } + printf("Done. usec=%lld\n", usec() - start); + return 0; +} + +int test_listpackBenchmarkLpCompareWithNumber(int argc, char **argv, int flags) { + /* Benchmark lpCompare with number */ + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + unsigned long long start = usec(); + for (int i = 0; i < 2000; i++) { + unsigned char *eptr = lpSeek(lp, 0); + while (eptr != NULL) { + lpCompare(lp, (unsigned char *)"99999", 5); + eptr = lpNext(lp, eptr); + } + } + printf("Done. usec=%lld\n", usec() - start); + return 0; +} + +int test_listpackBenchmarkFree(int argc, char **argv, int flags) { + UNUSED(argc); + UNUSED(argv); + UNUSED(flags); + + lpFree(lp); + return 0; +}