1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <unittest/unittest.h>
6#include <kvstore/kvstore.h>
7#include <pretty/hexdump.h>
8#include <lib/cksum.h>
9
10static bool kvs_bad_args(void) {
11    BEGIN_TEST;
12
13    kvstore_t kvs;
14    uint8_t buffer[1024];
15
16    char str[300];
17    memset(str, 'a', 299);
18    str[299] = 0;
19
20    // kvstore too small for even the header
21    kvs_init(&kvs, buffer, 3);
22    ASSERT_EQ(kvs_save(&kvs), KVS_ERR_OUT_OF_SPACE, "");
23
24    ASSERT_EQ(kvs_add(&kvs, "key", "value"), KVS_ERR_OUT_OF_SPACE, "");
25
26    // too-large keys or values
27    ASSERT_EQ(kvs_add(&kvs, str, "value"), KVS_ERR_BAD_PARAM, "");
28    ASSERT_EQ(kvs_add(&kvs, "key", str), KVS_ERR_BAD_PARAM, "");
29
30    str[256] = 0;
31    // just one byte too large
32    ASSERT_EQ(kvs_add(&kvs, str, "value"), KVS_ERR_BAD_PARAM, "");
33    ASSERT_EQ(kvs_add(&kvs, "key", str), KVS_ERR_BAD_PARAM, "");
34
35    // empty keys are invalid
36    ASSERT_EQ(kvs_add(&kvs, "", "value"), KVS_ERR_BAD_PARAM, "");
37
38    END_TEST;
39}
40
41static int kvs_check(kvstore_t* kvs, const char* key, const char* val) {
42    const char* out = kvs_get(kvs, key, NULL);
43    if (out == NULL) {
44        return KVS_ERR_NOT_FOUND;
45    }
46    if (strcmp(val, out)) {
47        return KVS_ERR_INTERNAL;
48    }
49    return KVS_OK;
50}
51
52static int kvs_verify(kvstore_t* kvs, const void* data, size_t dlen, size_t count) {
53    if (memcmp(kvs->data + sizeof(kvshdr_t), data, dlen)) {
54        printf("\ndata mismatch between kvs (first) and expected (second):\n");
55        hexdump8(kvs->data + sizeof(kvshdr_t), dlen);
56        hexdump8(data, dlen);
57        return KVS_ERR_INTERNAL;
58    }
59    if ((kvs->datalen - sizeof(kvshdr_t)) != dlen) {
60        return KVS_ERR_BAD_PARAM;
61    }
62    if (kvs->kvcount != count) {
63        return KVS_ERR_BAD_PARAM;
64    }
65    return KVS_OK;
66}
67
68static bool kvs_get_put(void) {
69    BEGIN_TEST;
70
71    kvstore_t kvs;
72    uint8_t buffer[2048];
73    memset(buffer, '@', sizeof(buffer));
74
75    char str[256];
76    memset(str, 'a', 255);
77    str[255] = 0;
78
79    kvs_init(&kvs, buffer, sizeof(buffer));
80
81    // simple
82    ASSERT_EQ(kvs_add(&kvs, "key1", "val1"), KVS_OK, "");
83    ASSERT_EQ(kvs_verify(&kvs, "\x04\x04key1\0val1\0", 12, 1), KVS_OK, "");
84    ASSERT_EQ(kvs_check(&kvs, "key1", "val1"), KVS_OK, "");
85    ASSERT_EQ(kvs_add(&kvs, "key2", "val2"), KVS_OK, "");
86    ASSERT_EQ(kvs_verify(&kvs, "\x04\x04key1\0val1\0\x04\x04key2\0val2\0", 24, 2), KVS_OK, "");
87    ASSERT_EQ(kvs_check(&kvs, "key1", "val1"), KVS_OK, "");
88    ASSERT_EQ(kvs_check(&kvs, "key2", "val2"), KVS_OK, "");
89
90    // max allowable key/value
91    ASSERT_EQ(kvs_add(&kvs, str, "value"), KVS_OK, "");
92    ASSERT_EQ(kvs_check(&kvs, str, "value"), KVS_OK, "");
93    ASSERT_EQ(kvs_add(&kvs, "key", str), KVS_OK, "");
94    ASSERT_EQ(kvs_check(&kvs, "key", str), KVS_OK, "");
95    ASSERT_EQ(kvs_add(&kvs, str, str), KVS_OK, "");
96
97    END_TEST;
98}
99
100static bool kvs_wire_format(void) {
101    BEGIN_TEST;
102
103    const uint8_t content[] =
104        "\x04\x04key1\0aaaa\0\x04\x08key2\0abcdefgh\0\x06\x00keykey\0\0\x04\x04key4\0bbbb";
105    kvshdr_t hdr = {
106        .version = KVSTORE_VERSION,
107        .flags = 0,
108        .length = sizeof(kvshdr_t) + sizeof(content),
109        .crc = 0,
110        .reserved = 0,
111    };
112
113    kvstore_t kvs;
114    uint8_t buffer[1024];
115
116    hdr.crc = crc32(0, (const void*) &hdr, sizeof(hdr) - sizeof(uint32_t));
117    hdr.crc = crc32(hdr.crc, content, sizeof(content));
118    memcpy(buffer, &hdr, sizeof(hdr));
119    memcpy(buffer + sizeof(hdr), content, sizeof(content));
120
121    // Create a new kvs with the same content, save it, compare raw data
122    uint8_t buffer2[1024];
123    kvs_init(&kvs, buffer2, sizeof(buffer2));
124    ASSERT_EQ(kvs_add(&kvs, "key1", "aaaa"), KVS_OK, "");
125    ASSERT_EQ(kvs_add(&kvs, "key2", "abcdefgh"), KVS_OK, "");
126    ASSERT_EQ(kvs_add(&kvs, "keykey", ""), KVS_OK, "");
127    ASSERT_EQ(kvs_add(&kvs, "key4", "bbbb"), KVS_OK, "");
128    ASSERT_EQ(kvs_save(&kvs), KVS_OK, "");
129    ASSERT_EQ(kvs.datalen, sizeof(hdr) + sizeof(content), "");
130    ASSERT_EQ(memcmp(buffer, buffer2, kvs.datalen), 0, "");
131
132    // mutated data should fail due to crc check
133    buffer[sizeof(hdr) + 8] = 0x42;
134    ASSERT_EQ(kvs_load(&kvs, buffer, sizeof(hdr) + sizeof(content)), KVS_ERR_PARSE_CRC, "");
135
136    // exactly sized should parse
137    memcpy(buffer, &hdr, sizeof(hdr));
138    memcpy(buffer + sizeof(hdr), content, sizeof(content));
139    ASSERT_EQ(kvs_load(&kvs, buffer, sizeof(hdr) + sizeof(content)), KVS_OK, "");
140
141    // verify we can find all the keys
142    ASSERT_EQ(kvs_check(&kvs, "key1", "aaaa"), KVS_OK, "");
143    ASSERT_EQ(kvs_check(&kvs, "key2", "abcdefgh"), KVS_OK, "");
144    ASSERT_EQ(kvs_check(&kvs, "keykey", ""), KVS_OK, "");
145    ASSERT_EQ(kvs_check(&kvs, "key4", "bbbb"), KVS_OK, "");
146
147    // but there's no space left
148    ASSERT_EQ(kvs_add(&kvs, "newkey", "newval"), KVS_ERR_OUT_OF_SPACE, "");
149
150    // larger buffer should allow keys to be added
151    memcpy(buffer, &hdr, sizeof(hdr));
152    memcpy(buffer + sizeof(hdr), content, sizeof(content));
153    ASSERT_EQ(kvs_load(&kvs, buffer, sizeof(buffer)), KVS_OK, "");
154
155    // add additional keys
156    ASSERT_EQ(kvs_add(&kvs, "key000000", "val000000"), KVS_OK, "");
157    ASSERT_EQ(kvs_add(&kvs, "key000001", "val000001"), KVS_OK, "");
158
159    const uint8_t newcontent[] =
160        "\x09\x09key000000\0val000000\0\x09\x09key000001\0val000001";
161    hdr.crc = crc32(0, (const void*) &hdr, sizeof(hdr) - sizeof(uint32_t));
162    hdr.crc = crc32(hdr.crc, content, sizeof(content));
163
164    uint8_t checkbuf[sizeof(content) + sizeof(newcontent)];
165    memcpy(checkbuf, content, sizeof(content));
166    memcpy(checkbuf + sizeof(content), newcontent, sizeof(newcontent));
167    ASSERT_EQ(kvs_verify(&kvs, checkbuf, sizeof(checkbuf), 6), KVS_OK, "");
168    ASSERT_EQ(kvs_check(&kvs, "key000000", "val000000"), KVS_OK, "");
169    ASSERT_EQ(kvs_check(&kvs, "key000001", "val000001"), KVS_OK, "");
170
171    // truncated buffer should fail
172    memcpy(buffer, &hdr, sizeof(hdr));
173    memcpy(buffer + sizeof(hdr), content, sizeof(content));
174    ASSERT_EQ(kvs_load(&kvs, buffer, sizeof(hdr) + sizeof(content) - 1), KVS_ERR_PARSE_HDR, "");
175
176    // truncated records should fail
177    hdr.length -= 3;
178    hdr.crc = crc32(0, (const void*) &hdr, sizeof(hdr) - sizeof(uint32_t));
179    hdr.crc = crc32(hdr.crc, content, sizeof(content) - 3);
180    memcpy(buffer, &hdr, sizeof(hdr));
181    memcpy(buffer + sizeof(hdr), content, sizeof(content));
182    ASSERT_EQ(kvs_load(&kvs, buffer, sizeof(hdr) + sizeof(content) - 3), KVS_ERR_PARSE_REC, "");
183
184    END_TEST;
185}
186
187BEGIN_TEST_CASE(kvstore_tests)
188RUN_TEST(kvs_bad_args)
189RUN_TEST(kvs_get_put)
190RUN_TEST(kvs_wire_format)
191END_TEST_CASE(kvstore_tests)
192
193int main(int argc, char** argv) {
194    return unittest_run_all_tests(argc, argv) ? 0 : -1;
195}
196