Skip to content

Add unit tests to estore #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

sggeorgiev
Copy link

This PR adds unit test for estore functionality.

src/expire.c Outdated
Comment on lines 114 to 122

for(int i = 0; i < es->num_buckets && maxCascade > 0; i++)
maxCascade -= ebCascade(es->buckets + i, es->bucket_type, now, maxCascade);
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • We will need some "static" context to memorize across calls , at which bucket we stopped as well and not start from the begining (Later on, we might want increase aggressivness if too many times stopped due to maxCascade limit.)
  • Need some way to iterate over the non empty buckets, very similar to the way kvstore does. See function kvstoreGetNextNonEmptyDictIndex()

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static context is added.

src/expire.c Outdated
Comment on lines 1163 to 1190
TEST("Create estore with pre-allocated buckets") {
int num_bits = 3; // 2^3 = 8 buckets
int num_buckets = 1 << num_bits;

estore *es = estoreCreate(&testEbType, num_bits);

for (int i = 0; i < num_buckets; i++) {
ebuckets b = es->buckets[i];
assert(b == NULL); // ebCreate returns NULL
}

assert(es->num_buckets == num_buckets);
assert(es->num_buckets_bits == num_bits);
assert(es->bucket_type == &testEbType);
assert(es->count == 0);

estoreRelease(es); // Clean up
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test covers overly trivial logic and seems redundant.
Please try to use c-style comments.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Covering even the trivial test cases helps improve code coverage. Good coverage shows that the code works as expected and is designed properly. It also gives us confidence that future changes won’t break existing functionality. Without it, we’re just hoping everything keeps working.

src/expire.c Outdated
Comment on lines 1182 to 1204
TEST("estoreGetBucket with cluster mode OFF") {
server.cluster_enabled = 0; // Non-cluster mode

estore *es = estoreCreate(&testEbType, 2); // 2^2 = 4 buckets
ebuckets *expected = &es->buckets[0];

for (int i = 0; i < 4; i++) {
ebuckets *b = estoreGetBucket(es, i);
assert(b == expected); // All slots should return the first bucket in non-cluster mode
}

estoreRelease(es);
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Covering even the trivial test cases helps improve code coverage. Good coverage shows that the code works as expected and is designed properly. It also gives us confidence that future changes won’t break existing functionality. Without it, we’re just hoping everything keeps working.

src/expire.c Outdated
Comment on lines 1196 to 1217
TEST("estoreGetBucket with cluster mode ON") {
server.cluster_enabled = 1; // Cluster mode enabled

estore *es = estoreCreate(&testEbType, 2); // 4 buckets

for (int i = 0; i < 4; i++) {
ebuckets *b = estoreGetBucket(es, i);
assert(b == &es->buckets[i]); // Each slot should map to its corresponding bucket
}

estoreRelease(es);
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Better have it covered by TCL test to verify correctness of cluster mode.
  • Note that there are cluster tests currently in comment at file expire.tcl - We should strive to make it functional again.
  • If coverage is not sufficient, then we better extend it there.

src/expire.c Outdated
Comment on lines 1209 to 1251
TEST("estoreAdd inserts kvobj at correct address in expected bucket") {
server.cluster_enabled = 0;

estore *es = estoreCreate(&testEbType, 2); // 4 buckets (slots 0-3)
TestKVObj *kv = zmalloc(sizeof(TestKVObj));
long long expire_time = 1;
int slot = 1;

assert(estoreSize(es) == 0);

estoreAdd(es, (kvobj*)kv, slot, expire_time);
assert(estoreSize(es) == 1);

// Get the expected bucket
ebuckets *bucket = estoreGetBucket(es, slot);
EbucketsIterator iter;
int found = 0;

ebStart(&iter, *bucket, es->bucket_type);
while (iter.currItem) {
if (iter.currItem == kv) {
found = 1;
break;
}
ebNext(&iter);
}
ebStop(&iter);

assert(found == 1); // kvobj was found by pointer

zfree(kv);
estoreRelease(es);
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test should also be covered at TCL level and if needed by DEBUG HTSTATS (Maybe we should have dedicate DEBUG EXPIRE-STATS.

src/expire.c Outdated
Comment on lines 1302 to 1334
TEST("estoreRelease with NULL does nothing") {
// Should not crash or do anything
estoreRelease(NULL);
}

TEST("estoreRelease frees all resources for valid estore") {
// Setup
estore *es = estoreCreate(&testEbType, 2); // 4 buckets

// Add an item to ensure some real content exists
TestKVObj *kv = zmalloc(sizeof(TestKVObj));
estoreAdd(es, (kvobj*)kv, 1, 1);

// Just ensure it was added (not strictly necessary, but good check)
assert(estoreSize(es) == 1);

// Release the estore
estoreRelease(es);

// We can't assert anything post-release (pointer is now invalid),
// but Valgrind or AddressSanitizer will catch use-after-free if any.
zfree(kv); // Caller owns kvobj memory separately
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one should be also tested at TCL level. Add items and then flushdb . Then verify it is empty via statstitcs. Note that i made another commit that fixes flushdb/flushall which takes care to empty estoreRelease() before kvstoreRelease() (This is because ebuckets relies on embedded data in the keys and we should release it before releasing the keyspace)

src/expire.c Outdated
Comment on lines 1326 to 1356
TEST("estoreRemove removes kvobj and decrements count") {
server.cluster_enabled = 0;

// Create estore with 1 bucket
estore *es = estoreCreate(&testEbType, 1);

// Allocate and add a test kvobj
TestKVObj *kv = zmalloc(sizeof(TestKVObj));
long long expire_time = 1;

estoreAdd(es, (kvobj*)kv, 0, expire_time);
assert(estoreSize(es) == 1);

// Remove the kvobj
estoreRemove(es, 0, (kvobj*)kv);
assert(estoreSize(es) == 0);

// Cleanup
zfree(kv);
estoreRelease(es);
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have here few meaningfull tests. Something iterative like:

  • T = current time + 100 seconds
  • For precision : P // take care it expands also beyond 1 hour....
    • Verify ESTORE stat
    • For i:0->100
      • Add item with expiration time {T+i * P}
      • Verify ESTORE stat
    • For i:0->100
      • Verify expiration time of item
      • Remove item
      • Verify ESTORE stat

@sggeorgiev sggeorgiev closed this Jul 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants