blob: dd7f8e9c94b66797716fc0fb14108e89bc0df1d9 [file] [log] [blame]
// 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)