From 04d31dd269ab901ac3de7a1290ed6a87bdea206a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 3 Sep 2010 22:07:40 +0930 Subject: [PATCH] tdb2: traversal test. --- ccan/tdb2/test/run-traverse.c | 255 ++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 ccan/tdb2/test/run-traverse.c diff --git a/ccan/tdb2/test/run-traverse.c b/ccan/tdb2/test/run-traverse.c new file mode 100644 index 00000000..51d84a0a --- /dev/null +++ b/ccan/tdb2/test/run-traverse.c @@ -0,0 +1,255 @@ +#include +#include +#include +#include +#include +#include +#include +#include "logging.h" + +#define NUM_RECORDS 1000 + +/* We use the same seed which we saw a failure on. */ +static uint64_t fixedhash(const void *key, size_t len, uint64_t seed, void *p) +{ + return hash64_stable((const unsigned char *)key, len, + *(uint64_t *)p); +} + +static bool store_records(struct tdb_context *tdb) +{ + int i; + struct tdb_data key = { (unsigned char *)&i, sizeof(i) }; + struct tdb_data data = { (unsigned char *)&i, sizeof(i) }; + + for (i = 0; i < NUM_RECORDS; i++) + if (tdb_store(tdb, key, data, TDB_REPLACE) != 0) + return false; + return true; +} + +struct trav_data { + unsigned int calls, call_limit; + int low, high; + bool mismatch; + bool delete; + enum TDB_ERROR delete_error; +}; + +static int trav(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, void *p) +{ + struct trav_data *td = p; + int val; + + td->calls++; + if (key.dsize != sizeof(val) || dbuf.dsize != sizeof(val) + || memcmp(key.dptr, dbuf.dptr, key.dsize) != 0) { + td->mismatch = true; + return -1; + } + memcpy(&val, dbuf.dptr, dbuf.dsize); + if (val < td->low) + td->low = val; + if (val > td->high) + td->high = val; + + if (td->delete) { + if (tdb_delete(tdb, key) != 0) { + td->delete_error = tdb_error(tdb); + return -1; + } + } + + if (td->calls == td->call_limit) + return 1; + return 0; +} + +struct trav_grow_data { + unsigned int calls; + unsigned int num_large; + bool mismatch; + enum TDB_ERROR error; +}; + +static int trav_grow(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, + void *p) +{ + struct trav_grow_data *tgd = p; + int val; + unsigned char buffer[128] = { 0 }; + + tgd->calls++; + if (key.dsize != sizeof(val) || dbuf.dsize < sizeof(val) + || memcmp(key.dptr, dbuf.dptr, key.dsize) != 0) { + tgd->mismatch = true; + return -1; + } + + if (dbuf.dsize > sizeof(val)) + /* We must have seen this before! */ + tgd->num_large++; + + /* Make a big difference to the database. */ + dbuf.dptr = buffer; + dbuf.dsize = sizeof(buffer); + if (tdb_append(tdb, key, dbuf) != 0) { + tgd->error = tdb_error(tdb); + return -1; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + unsigned int i; + int num; + struct trav_data td; + struct trav_grow_data tgd; + struct tdb_context *tdb; + uint64_t seed = 16014841315512641303ULL; + int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP, + TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT, + TDB_NOMMAP|TDB_CONVERT }; + union tdb_attribute hattr = { .hash = { .base = { TDB_ATTRIBUTE_HASH }, + .hash_fn = fixedhash, + .hash_private = &seed } }; + + hattr.base.next = &tap_log_attr; + + plan_tests(sizeof(flags) / sizeof(flags[0]) * 50 + 1); + for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) { + tdb = tdb_open("run-traverse.tdb", flags[i], + O_RDWR|O_CREAT|O_TRUNC, 0600, &hattr); + ok1(tdb); + if (!tdb) + continue; + + ok1(tdb_traverse_read(tdb, NULL, NULL) == 0); + ok1(tdb_traverse(tdb, NULL, NULL) == 0); + + ok1(store_records(tdb)); + num = tdb_traverse_read(tdb, NULL, NULL); + ok1(num == NUM_RECORDS); + num = tdb_traverse(tdb, NULL, NULL); + ok1(num == NUM_RECORDS); + + /* Full traverse (read-only). */ + td.calls = 0; + td.call_limit = UINT_MAX; + td.low = INT_MAX; + td.high = INT_MIN; + td.mismatch = false; + td.delete = false; + + num = tdb_traverse_read(tdb, trav, &td); + ok1(num == NUM_RECORDS); + ok1(!td.mismatch); + ok1(td.calls == NUM_RECORDS); + ok1(td.low == 0); + ok1(td.high == NUM_RECORDS-1); + + /* Full traverse. */ + td.calls = 0; + td.call_limit = UINT_MAX; + td.low = INT_MAX; + td.high = INT_MIN; + td.mismatch = false; + td.delete = false; + + num = tdb_traverse(tdb, trav, &td); + ok1(num == NUM_RECORDS); + ok1(!td.mismatch); + ok1(td.calls == NUM_RECORDS); + ok1(td.low == 0); + ok1(td.high == NUM_RECORDS-1); + + /* Short traverse. */ + td.calls = 0; + td.call_limit = NUM_RECORDS / 2; + td.low = INT_MAX; + td.high = INT_MIN; + td.mismatch = false; + td.delete = false; + + num = tdb_traverse(tdb, trav, &td); + ok1(num == NUM_RECORDS / 2); + ok1(!td.mismatch); + ok1(td.calls == NUM_RECORDS / 2); + ok1(td.low <= NUM_RECORDS / 2); + ok1(td.high > NUM_RECORDS / 2); + ok1(tdb_check(tdb, NULL, NULL) == 0); + + /* Growing traverse. Expect failure on r/o traverse. */ + tgd.calls = 0; + tgd.num_large = 0; + tgd.mismatch = false; + tgd.error = TDB_SUCCESS; + num = tdb_traverse_read(tdb, trav_grow, &tgd); + ok1(num == 1); + ok1(tgd.error == TDB_ERR_RDONLY); + ok1(tgd.calls == 1); + ok1(!tgd.mismatch); + ok1(tdb_check(tdb, NULL, NULL) == 0); + + /* Deleting traverse. Expect failure on r/o traverse. */ + td.calls = 0; + td.call_limit = UINT_MAX; + td.low = INT_MAX; + td.high = INT_MIN; + td.mismatch = false; + td.delete = true; + td.delete_error = TDB_SUCCESS; + num = tdb_traverse_read(tdb, trav, &td); + ok1(num == 1); + ok1(td.delete_error == TDB_ERR_RDONLY); + ok1(!td.mismatch); + ok1(td.calls == 1); + ok1(td.low == td.high); + ok1(tdb_check(tdb, NULL, NULL) == 0); + + /* Deleting traverse. */ + td.calls = 0; + td.call_limit = UINT_MAX; + td.low = INT_MAX; + td.high = INT_MIN; + td.mismatch = false; + td.delete = true; + td.delete_error = TDB_SUCCESS; + num = tdb_traverse(tdb, trav, &td); + ok1(num == NUM_RECORDS); + ok1(td.delete_error == TDB_SUCCESS); + ok1(!td.mismatch); + ok1(td.calls == NUM_RECORDS); + ok1(td.low == 0); + ok1(td.high == NUM_RECORDS-1); + ok1(tdb_check(tdb, NULL, NULL) == 0); + + /* Now it's empty! */ + ok1(tdb_traverse_read(tdb, NULL, NULL) == 0); + ok1(tdb_traverse(tdb, NULL, NULL) == 0); + + /* Re-add. */ + ok1(store_records(tdb)); + ok1(tdb_traverse_read(tdb, NULL, NULL) == NUM_RECORDS); + ok1(tdb_check(tdb, NULL, NULL) == 0); + + /* Grow. This will cause us to be reshuffled. */ + tgd.calls = 0; + tgd.num_large = 0; + tgd.mismatch = false; + tgd.error = TDB_SUCCESS; + ok1(tdb_traverse(tdb, trav_grow, &tgd) > 1); + ok1(tgd.error == 0); + ok1(!tgd.mismatch); + ok1(tdb_check(tdb, NULL, NULL) == 0); + ok1(tgd.num_large < tgd.calls); + diag("growing db: %u calls, %u repeats", + tgd.calls, tgd.num_large); + + tdb_close(tdb); + } + + ok1(tap_log_messages == 0); + return exit_status(); +} -- 2.39.2