| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <unittest/unittest.h> |
| #include <kvstore/kvstore.h> |
| #include <pretty/hexdump.h> |
| #include <lib/cksum.h> |
| |
| static bool kvs_bad_args(void) { |
| BEGIN_TEST; |
| |
| kvstore_t kvs; |
| uint8_t buffer[1024]; |
| |
| char str[300]; |
| memset(str, 'a', 299); |
| str[299] = 0; |
| |
| // kvstore too small for even the header |
| kvs_init(&kvs, buffer, 3); |
| ASSERT_EQ(kvs_save(&kvs), KVS_ERR_OUT_OF_SPACE, ""); |
| |
| ASSERT_EQ(kvs_add(&kvs, "key", "value"), KVS_ERR_OUT_OF_SPACE, ""); |
| |
| // too-large keys or values |
| ASSERT_EQ(kvs_add(&kvs, str, "value"), KVS_ERR_BAD_PARAM, ""); |
| ASSERT_EQ(kvs_add(&kvs, "key", str), KVS_ERR_BAD_PARAM, ""); |
| |
| str[256] = 0; |
| // just one byte too large |
| ASSERT_EQ(kvs_add(&kvs, str, "value"), KVS_ERR_BAD_PARAM, ""); |
| ASSERT_EQ(kvs_add(&kvs, "key", str), KVS_ERR_BAD_PARAM, ""); |
| |
| // empty keys are invalid |
| ASSERT_EQ(kvs_add(&kvs, "", "value"), KVS_ERR_BAD_PARAM, ""); |
| |
| END_TEST; |
| } |
| |
| static int kvs_check(kvstore_t* kvs, const char* key, const char* val) { |
| const char* out = kvs_get(kvs, key, NULL); |
| if (out == NULL) { |
| return KVS_ERR_NOT_FOUND; |
| } |
| if (strcmp(val, out)) { |
| return KVS_ERR_INTERNAL; |
| } |
| return KVS_OK; |
| } |
| |
| static int kvs_verify(kvstore_t* kvs, const void* data, size_t dlen, size_t count) { |
| if (memcmp(kvs->data + sizeof(kvshdr_t), data, dlen)) { |
| printf("\ndata mismatch between kvs (first) and expected (second):\n"); |
| hexdump8(kvs->data + sizeof(kvshdr_t), dlen); |
| hexdump8(data, dlen); |
| return KVS_ERR_INTERNAL; |
| } |
| if ((kvs->datalen - sizeof(kvshdr_t)) != dlen) { |
| return KVS_ERR_BAD_PARAM; |
| } |
| if (kvs->kvcount != count) { |
| return KVS_ERR_BAD_PARAM; |
| } |
| return KVS_OK; |
| } |
| |
| static bool kvs_get_put(void) { |
| BEGIN_TEST; |
| |
| kvstore_t kvs; |
| uint8_t buffer[2048]; |
| memset(buffer, '@', sizeof(buffer)); |
| |
| char str[256]; |
| memset(str, 'a', 255); |
| str[255] = 0; |
| |
| kvs_init(&kvs, buffer, sizeof(buffer)); |
| |
| // simple |
| ASSERT_EQ(kvs_add(&kvs, "key1", "val1"), KVS_OK, ""); |
| ASSERT_EQ(kvs_verify(&kvs, "\x04\x04key1\0val1\0", 12, 1), KVS_OK, ""); |
| ASSERT_EQ(kvs_check(&kvs, "key1", "val1"), KVS_OK, ""); |
| ASSERT_EQ(kvs_add(&kvs, "key2", "val2"), KVS_OK, ""); |
| ASSERT_EQ(kvs_verify(&kvs, "\x04\x04key1\0val1\0\x04\x04key2\0val2\0", 24, 2), KVS_OK, ""); |
| ASSERT_EQ(kvs_check(&kvs, "key1", "val1"), KVS_OK, ""); |
| ASSERT_EQ(kvs_check(&kvs, "key2", "val2"), KVS_OK, ""); |
| |
| // max allowable key/value |
| ASSERT_EQ(kvs_add(&kvs, str, "value"), KVS_OK, ""); |
| ASSERT_EQ(kvs_check(&kvs, str, "value"), KVS_OK, ""); |
| ASSERT_EQ(kvs_add(&kvs, "key", str), KVS_OK, ""); |
| ASSERT_EQ(kvs_check(&kvs, "key", str), KVS_OK, ""); |
| ASSERT_EQ(kvs_add(&kvs, str, str), KVS_OK, ""); |
| |
| END_TEST; |
| } |
| |
| static bool kvs_wire_format(void) { |
| BEGIN_TEST; |
| |
| const uint8_t content[] = |
| "\x04\x04key1\0aaaa\0\x04\x08key2\0abcdefgh\0\x06\x00keykey\0\0\x04\x04key4\0bbbb"; |
| kvshdr_t hdr = { |
| .version = KVSTORE_VERSION, |
| .flags = 0, |
| .length = sizeof(kvshdr_t) + sizeof(content), |
| .crc = 0, |
| .reserved = 0, |
| }; |
| |
| kvstore_t kvs; |
| uint8_t buffer[1024]; |
| |
| hdr.crc = crc32(0, (const void*) &hdr, sizeof(hdr) - sizeof(uint32_t)); |
| hdr.crc = crc32(hdr.crc, content, sizeof(content)); |
| memcpy(buffer, &hdr, sizeof(hdr)); |
| memcpy(buffer + sizeof(hdr), content, sizeof(content)); |
| |
| // Create a new kvs with the same content, save it, compare raw data |
| uint8_t buffer2[1024]; |
| kvs_init(&kvs, buffer2, sizeof(buffer2)); |
| ASSERT_EQ(kvs_add(&kvs, "key1", "aaaa"), KVS_OK, ""); |
| ASSERT_EQ(kvs_add(&kvs, "key2", "abcdefgh"), KVS_OK, ""); |
| ASSERT_EQ(kvs_add(&kvs, "keykey", ""), KVS_OK, ""); |
| ASSERT_EQ(kvs_add(&kvs, "key4", "bbbb"), KVS_OK, ""); |
| ASSERT_EQ(kvs_save(&kvs), KVS_OK, ""); |
| ASSERT_EQ(kvs.datalen, sizeof(hdr) + sizeof(content), ""); |
| ASSERT_EQ(memcmp(buffer, buffer2, kvs.datalen), 0, ""); |
| |
| // mutated data should fail due to crc check |
| buffer[sizeof(hdr) + 8] = 0x42; |
| ASSERT_EQ(kvs_load(&kvs, buffer, sizeof(hdr) + sizeof(content)), KVS_ERR_PARSE_CRC, ""); |
| |
| // exactly sized should parse |
| memcpy(buffer, &hdr, sizeof(hdr)); |
| memcpy(buffer + sizeof(hdr), content, sizeof(content)); |
| ASSERT_EQ(kvs_load(&kvs, buffer, sizeof(hdr) + sizeof(content)), KVS_OK, ""); |
| |
| // verify we can find all the keys |
| ASSERT_EQ(kvs_check(&kvs, "key1", "aaaa"), KVS_OK, ""); |
| ASSERT_EQ(kvs_check(&kvs, "key2", "abcdefgh"), KVS_OK, ""); |
| ASSERT_EQ(kvs_check(&kvs, "keykey", ""), KVS_OK, ""); |
| ASSERT_EQ(kvs_check(&kvs, "key4", "bbbb"), KVS_OK, ""); |
| |
| // but there's no space left |
| ASSERT_EQ(kvs_add(&kvs, "newkey", "newval"), KVS_ERR_OUT_OF_SPACE, ""); |
| |
| // larger buffer should allow keys to be added |
| memcpy(buffer, &hdr, sizeof(hdr)); |
| memcpy(buffer + sizeof(hdr), content, sizeof(content)); |
| ASSERT_EQ(kvs_load(&kvs, buffer, sizeof(buffer)), KVS_OK, ""); |
| |
| // add additional keys |
| ASSERT_EQ(kvs_add(&kvs, "key000000", "val000000"), KVS_OK, ""); |
| ASSERT_EQ(kvs_add(&kvs, "key000001", "val000001"), KVS_OK, ""); |
| |
| const uint8_t newcontent[] = |
| "\x09\x09key000000\0val000000\0\x09\x09key000001\0val000001"; |
| hdr.crc = crc32(0, (const void*) &hdr, sizeof(hdr) - sizeof(uint32_t)); |
| hdr.crc = crc32(hdr.crc, content, sizeof(content)); |
| |
| uint8_t checkbuf[sizeof(content) + sizeof(newcontent)]; |
| memcpy(checkbuf, content, sizeof(content)); |
| memcpy(checkbuf + sizeof(content), newcontent, sizeof(newcontent)); |
| ASSERT_EQ(kvs_verify(&kvs, checkbuf, sizeof(checkbuf), 6), KVS_OK, ""); |
| ASSERT_EQ(kvs_check(&kvs, "key000000", "val000000"), KVS_OK, ""); |
| ASSERT_EQ(kvs_check(&kvs, "key000001", "val000001"), KVS_OK, ""); |
| |
| // truncated buffer should fail |
| memcpy(buffer, &hdr, sizeof(hdr)); |
| memcpy(buffer + sizeof(hdr), content, sizeof(content)); |
| ASSERT_EQ(kvs_load(&kvs, buffer, sizeof(hdr) + sizeof(content) - 1), KVS_ERR_PARSE_HDR, ""); |
| |
| // truncated records should fail |
| hdr.length -= 3; |
| hdr.crc = crc32(0, (const void*) &hdr, sizeof(hdr) - sizeof(uint32_t)); |
| hdr.crc = crc32(hdr.crc, content, sizeof(content) - 3); |
| memcpy(buffer, &hdr, sizeof(hdr)); |
| memcpy(buffer + sizeof(hdr), content, sizeof(content)); |
| ASSERT_EQ(kvs_load(&kvs, buffer, sizeof(hdr) + sizeof(content) - 3), KVS_ERR_PARSE_REC, ""); |
| |
| END_TEST; |
| } |
| |
| BEGIN_TEST_CASE(kvstore_tests) |
| RUN_TEST(kvs_bad_args) |
| RUN_TEST(kvs_get_put) |
| RUN_TEST(kvs_wire_format) |
| END_TEST_CASE(kvstore_tests) |
| |
| int main(int argc, char** argv) { |
| return unittest_run_all_tests(argc, argv) ? 0 : -1; |
| } |