diff options
Diffstat (limited to '')
-rw-r--r-- | src/tests/cache.c | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/src/tests/cache.c b/src/tests/cache.c new file mode 100644 index 0000000..667435d --- /dev/null +++ b/src/tests/cache.c @@ -0,0 +1,215 @@ +#include "tests.h" + +#include <libplacebo/cache.h> + +// Returns "foo" for even keys, "bar" for odd +static pl_cache_obj lookup_foobar(void *priv, uint64_t key) +{ + return (pl_cache_obj) { + .key = 0xFFFF, // test key sanity + .data = (key & 1) ? "bar" : "foo", + .size = 3, + }; +} + +static void update_count(void *priv, pl_cache_obj obj) +{ + int *count = priv; + *count += obj.size ? 1 : -1; +} + +enum { + KEY1 = 0x9c65575f419288f5, + KEY2 = 0x92da969be9b88086, + KEY3 = 0x7fcb62540b00bc8b, + KEY4 = 0x46c60ec11af9dde3, + KEY5 = 0xcb6760b98ece2477, + KEY6 = 0xf37dc72b7f9e5c88, + KEY7 = 0x30c18c962d82e5f5, +}; + +int main() +{ + pl_log log = pl_test_logger(); + pl_cache test = pl_cache_create(pl_cache_params( + .log = log, + .max_object_size = 16, + .max_total_size = 32, + )); + + pl_cache_obj obj1 = { .key = KEY1, .data = "abc", .size = 3 }; + pl_cache_obj obj2 = { .key = KEY2, .data = "de", .size = 2 }; + pl_cache_obj obj3 = { .key = KEY3, .data = "xyzw", .size = 4 }; + + REQUIRE(pl_cache_try_set(test, &obj1)); + REQUIRE(pl_cache_try_set(test, &obj2)); + REQUIRE(pl_cache_try_set(test, &obj3)); + REQUIRE_CMP(pl_cache_size(test), ==, 9, "zu"); + REQUIRE_CMP(pl_cache_objects(test), ==, 3, "d"); + REQUIRE(pl_cache_try_set(test, &obj2)); // delete KEY2 + REQUIRE_CMP(pl_cache_size(test), ==, 7, "zu"); + REQUIRE_CMP(pl_cache_objects(test), ==, 2, "d"); + + REQUIRE(pl_cache_get(test, &obj1)); + REQUIRE(!pl_cache_get(test, &obj2)); + REQUIRE(pl_cache_get(test, &obj3)); + REQUIRE_CMP(pl_cache_size(test), ==, 0, "zu"); + REQUIRE_CMP(pl_cache_objects(test), ==, 0, "d"); + REQUIRE_MEMEQ(obj1.data, "abc", 3); + REQUIRE_MEMEQ(obj3.data, "xyzw", 4); + + // Re-insert removed objects (in reversed order) + REQUIRE(pl_cache_try_set(test, &obj3)); + REQUIRE(pl_cache_try_set(test, &obj1)); + REQUIRE_CMP(pl_cache_size(test), ==, 7, "zu"); + REQUIRE_CMP(pl_cache_objects(test), ==, 2, "d"); + + uint8_t ref[72]; + memset(ref, 0xbe, sizeof(ref)); + uint8_t *refp = ref; + +#define PAD_ALIGN(x) PL_ALIGN2(x, sizeof(uint32_t)) +#define W(type, ...) \ + do { \ + size_t sz = sizeof((type){__VA_ARGS__}); \ + pl_assert(ref + sizeof(ref) - refp >= sz); \ + memcpy(refp, &(type){__VA_ARGS__}, sz); \ + refp += sz; \ + size_t pad_sz = PAD_ALIGN(sz) - sz; \ + pl_assert(ref + sizeof(ref) - refp >= pad_sz); \ + memcpy(refp, &(char[PAD_ALIGN(1)]){0}, pad_sz); \ + refp += pad_sz; \ + } while (0) + + W(char[], 'p', 'l', '_', 'c', 'a', 'c', 'h', 'e'); // cache magic + W(uint32_t, 1); // cache version + W(uint32_t, 2); // number of objects + + // object 3 + W(uint64_t, KEY3); // key + W(uint64_t, 4); // size +#ifdef PL_HAVE_XXHASH + W(uint64_t, 0xd43612ef3fbee8be); // hash +#else + W(uint64_t, 0xec18884e5e471117); // hash +#endif + W(char[], 'x', 'y', 'z', 'w'); // data + + // object 1 + W(uint64_t, KEY1); // key + W(uint64_t, 3); // size +#ifdef PL_HAVE_XXHASH + W(uint64_t, 0x78af5f94892f3950); // hash +#else + W(uint64_t, 0x3a204d408a2e2d77); // hash +#endif + W(char[], 'a', 'b', 'c'); // data + +#undef W +#undef PAD_ALIGN + + uint8_t data[100]; + pl_static_assert(sizeof(data) >= sizeof(ref)); + REQUIRE_CMP(pl_cache_save(test, data, sizeof(data)), ==, sizeof(ref), "zu"); + REQUIRE_MEMEQ(data, ref, sizeof(ref)); + + pl_cache test2 = pl_cache_create(pl_cache_params( .log = log )); + REQUIRE_CMP(pl_cache_load(test2, data, sizeof(data)), ==, 2, "d"); + REQUIRE_CMP(pl_cache_size(test2), ==, 7, "zu"); + REQUIRE_CMP(pl_cache_save(test2, NULL, 0), ==, sizeof(ref), "zu"); + REQUIRE_CMP(pl_cache_save(test2, data, sizeof(data)), ==, sizeof(ref), "zu"); + REQUIRE_MEMEQ(data, ref, sizeof(ref)); + + // Test loading invalid data + REQUIRE_CMP(pl_cache_load(test2, ref, 0), <, 0, "d"); // empty file + REQUIRE_CMP(pl_cache_load(test2, ref, 5), <, 0, "d"); // truncated header + REQUIRE_CMP(pl_cache_load(test2, ref, 64), ==, 1, "d"); // truncated object data + data[sizeof(ref) - 2] = 'X'; // corrupt data + REQUIRE_CMP(pl_cache_load(test2, data, sizeof(ref)), ==, 1, "d"); // bad checksum + pl_cache_destroy(&test2); + + // Inserting too large object should fail + uint8_t zero[32] = {0}; + pl_cache_obj obj4 = { .key = KEY4, .data = zero, .size = 32 }; + REQUIRE(!pl_cache_try_set(test, &obj4)); + REQUIRE(!pl_cache_get(test, &obj4)); + REQUIRE_CMP(pl_cache_size(test), ==, 7, "zu"); + REQUIRE_CMP(pl_cache_objects(test), ==, 2, "d"); + + // Inserting 16-byte object should succeed, and not purge old entries + obj4 = (pl_cache_obj) { .key = KEY4, .data = zero, .size = 16 }; + REQUIRE(pl_cache_try_set(test, &obj4)); + REQUIRE_CMP(pl_cache_size(test), ==, 23, "zu"); + REQUIRE_CMP(pl_cache_objects(test), ==, 3, "d"); + REQUIRE(pl_cache_get(test, &obj1)); + REQUIRE(pl_cache_get(test, &obj3)); + REQUIRE(pl_cache_get(test, &obj4)); + pl_cache_set(test, &obj1); + pl_cache_set(test, &obj3); + pl_cache_set(test, &obj4); + REQUIRE_CMP(pl_cache_size(test), ==, 23, "zu"); + REQUIRE_CMP(pl_cache_objects(test), ==, 3, "d"); + + // Inserting another 10-byte object should purge entry KEY1 + pl_cache_obj obj5 = { .key = KEY5, .data = zero, .size = 10 }; + REQUIRE(pl_cache_try_set(test, &obj5)); + REQUIRE_CMP(pl_cache_size(test), ==, 30, "zu"); + REQUIRE_CMP(pl_cache_objects(test), ==, 3, "d"); + REQUIRE(!pl_cache_get(test, &obj1)); + REQUIRE(pl_cache_get(test, &obj3)); + REQUIRE(pl_cache_get(test, &obj4)); + REQUIRE(pl_cache_get(test, &obj5)); + pl_cache_set(test, &obj3); + pl_cache_set(test, &obj4); + pl_cache_set(test, &obj5); + REQUIRE_CMP(pl_cache_size(test), ==, 30, "zu"); + REQUIRE_CMP(pl_cache_objects(test), ==, 3, "d"); + + // Inserting final 6-byte object should purge entry KEY3 + pl_cache_obj obj6 = { .key = KEY6, .data = zero, .size = 6 }; + REQUIRE(pl_cache_try_set(test, &obj6)); + REQUIRE_CMP(pl_cache_size(test), ==, 32, "zu"); + REQUIRE_CMP(pl_cache_objects(test), ==, 3, "d"); + REQUIRE(!pl_cache_get(test, &obj3)); + REQUIRE(pl_cache_get(test, &obj4)); + REQUIRE(pl_cache_get(test, &obj5)); + REQUIRE(pl_cache_get(test, &obj6)); + REQUIRE_CMP(pl_cache_size(test), ==, 0, "zu"); + REQUIRE_CMP(pl_cache_objects(test), ==, 0, "d"); + pl_cache_obj_free(&obj4); + pl_cache_obj_free(&obj5); + pl_cache_obj_free(&obj6); + + // Test callback API + int num_objects = 0; + test2 = pl_cache_create(pl_cache_params( + .get = lookup_foobar, + .set = update_count, + .priv = &num_objects, + )); + + REQUIRE(pl_cache_get(test2, &obj1)); + REQUIRE_CMP(obj1.key, ==, KEY1, PRIu64); + REQUIRE_CMP(obj1.size, ==, 3, "zu"); + REQUIRE_MEMEQ(obj1.data, "bar", 3); + REQUIRE(pl_cache_get(test2, &obj2)); + REQUIRE_CMP(obj2.key, ==, KEY2, PRIu64); + REQUIRE_CMP(obj2.size, ==, 3, "zu"); + REQUIRE_MEMEQ(obj2.data, "foo", 3); + REQUIRE_CMP(pl_cache_objects(test2), ==, 0, "d"); + REQUIRE_CMP(num_objects, ==, 0, "d"); + REQUIRE(pl_cache_try_set(test2, &obj1)); + REQUIRE(pl_cache_try_set(test2, &obj2)); + REQUIRE(pl_cache_try_set(test2, &(pl_cache_obj) { .key = KEY7, .data = "abcde", .size = 5 })); + REQUIRE_CMP(pl_cache_objects(test2), ==, 3, "d"); + REQUIRE_CMP(num_objects, ==, 3, "d"); + REQUIRE(pl_cache_try_set(test2, &obj1)); + REQUIRE(pl_cache_try_set(test2, &obj2)); + REQUIRE_CMP(pl_cache_objects(test2), ==, 1, "d"); + REQUIRE_CMP(num_objects, ==, 1, "d"); + pl_cache_destroy(&test2); + + pl_cache_destroy(&test); + pl_log_destroy(&log); + return 0; +} |