Add Google's LevelDB support
[novacoin.git] / src / leveldb / db / c_test.c
1 /* Copyright (c) 2011 The LevelDB 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. See the AUTHORS file for names of contributors. */
4
5 #include "leveldb/c.h"
6
7 #include <stddef.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13
14 const char* phase = "";
15 static char dbname[200];
16
17 static void StartPhase(const char* name) {
18   fprintf(stderr, "=== Test %s\n", name);
19   phase = name;
20 }
21
22 static const char* GetTempDir(void) {
23     const char* ret = getenv("TEST_TMPDIR");
24     if (ret == NULL || ret[0] == '\0')
25         ret = "/tmp";
26     return ret;
27 }
28
29 #define CheckNoError(err)                                               \
30   if ((err) != NULL) {                                                  \
31     fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \
32     abort();                                                            \
33   }
34
35 #define CheckCondition(cond)                                            \
36   if (!(cond)) {                                                        \
37     fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, #cond); \
38     abort();                                                            \
39   }
40
41 static void CheckEqual(const char* expected, const char* v, size_t n) {
42   if (expected == NULL && v == NULL) {
43     // ok
44   } else if (expected != NULL && v != NULL && n == strlen(expected) &&
45              memcmp(expected, v, n) == 0) {
46     // ok
47     return;
48   } else {
49     fprintf(stderr, "%s: expected '%s', got '%s'\n",
50             phase,
51             (expected ? expected : "(null)"),
52             (v ? v : "(null"));
53     abort();
54   }
55 }
56
57 static void Free(char** ptr) {
58   if (*ptr) {
59     free(*ptr);
60     *ptr = NULL;
61   }
62 }
63
64 static void CheckGet(
65     leveldb_t* db,
66     const leveldb_readoptions_t* options,
67     const char* key,
68     const char* expected) {
69   char* err = NULL;
70   size_t val_len;
71   char* val;
72   val = leveldb_get(db, options, key, strlen(key), &val_len, &err);
73   CheckNoError(err);
74   CheckEqual(expected, val, val_len);
75   Free(&val);
76 }
77
78 static void CheckIter(leveldb_iterator_t* iter,
79                       const char* key, const char* val) {
80   size_t len;
81   const char* str;
82   str = leveldb_iter_key(iter, &len);
83   CheckEqual(key, str, len);
84   str = leveldb_iter_value(iter, &len);
85   CheckEqual(val, str, len);
86 }
87
88 // Callback from leveldb_writebatch_iterate()
89 static void CheckPut(void* ptr,
90                      const char* k, size_t klen,
91                      const char* v, size_t vlen) {
92   int* state = (int*) ptr;
93   CheckCondition(*state < 2);
94   switch (*state) {
95     case 0:
96       CheckEqual("bar", k, klen);
97       CheckEqual("b", v, vlen);
98       break;
99     case 1:
100       CheckEqual("box", k, klen);
101       CheckEqual("c", v, vlen);
102       break;
103   }
104   (*state)++;
105 }
106
107 // Callback from leveldb_writebatch_iterate()
108 static void CheckDel(void* ptr, const char* k, size_t klen) {
109   int* state = (int*) ptr;
110   CheckCondition(*state == 2);
111   CheckEqual("bar", k, klen);
112   (*state)++;
113 }
114
115 static void CmpDestroy(void* arg) { }
116
117 static int CmpCompare(void* arg, const char* a, size_t alen,
118                       const char* b, size_t blen) {
119   int n = (alen < blen) ? alen : blen;
120   int r = memcmp(a, b, n);
121   if (r == 0) {
122     if (alen < blen) r = -1;
123     else if (alen > blen) r = +1;
124   }
125   return r;
126 }
127
128 static const char* CmpName(void* arg) {
129   return "foo";
130 }
131
132 // Custom filter policy
133 static unsigned char fake_filter_result = 1;
134 static void FilterDestroy(void* arg) { }
135 static const char* FilterName(void* arg) {
136   return "TestFilter";
137 }
138 static char* FilterCreate(
139     void* arg,
140     const char* const* key_array, const size_t* key_length_array,
141     int num_keys,
142     size_t* filter_length) {
143   *filter_length = 4;
144   char* result = malloc(4);
145   memcpy(result, "fake", 4);
146   return result;
147 }
148 unsigned char FilterKeyMatch(
149     void* arg,
150     const char* key, size_t length,
151     const char* filter, size_t filter_length) {
152   CheckCondition(filter_length == 4);
153   CheckCondition(memcmp(filter, "fake", 4) == 0);
154   return fake_filter_result;
155 }
156
157 int main(int argc, char** argv) {
158   leveldb_t* db;
159   leveldb_comparator_t* cmp;
160   leveldb_cache_t* cache;
161   leveldb_env_t* env;
162   leveldb_options_t* options;
163   leveldb_readoptions_t* roptions;
164   leveldb_writeoptions_t* woptions;
165   char* err = NULL;
166   int run = -1;
167
168   CheckCondition(leveldb_major_version() >= 1);
169   CheckCondition(leveldb_minor_version() >= 1);
170
171   snprintf(dbname, sizeof(dbname),
172            "%s/leveldb_c_test-%d",
173            GetTempDir(),
174            ((int) geteuid()));
175
176   StartPhase("create_objects");
177   cmp = leveldb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName);
178   env = leveldb_create_default_env();
179   cache = leveldb_cache_create_lru(100000);
180
181   options = leveldb_options_create();
182   leveldb_options_set_comparator(options, cmp);
183   leveldb_options_set_error_if_exists(options, 1);
184   leveldb_options_set_cache(options, cache);
185   leveldb_options_set_env(options, env);
186   leveldb_options_set_info_log(options, NULL);
187   leveldb_options_set_write_buffer_size(options, 100000);
188   leveldb_options_set_paranoid_checks(options, 1);
189   leveldb_options_set_max_open_files(options, 10);
190   leveldb_options_set_block_size(options, 1024);
191   leveldb_options_set_block_restart_interval(options, 8);
192   leveldb_options_set_compression(options, leveldb_no_compression);
193
194   roptions = leveldb_readoptions_create();
195   leveldb_readoptions_set_verify_checksums(roptions, 1);
196   leveldb_readoptions_set_fill_cache(roptions, 0);
197
198   woptions = leveldb_writeoptions_create();
199   leveldb_writeoptions_set_sync(woptions, 1);
200
201   StartPhase("destroy");
202   leveldb_destroy_db(options, dbname, &err);
203   Free(&err);
204
205   StartPhase("open_error");
206   db = leveldb_open(options, dbname, &err);
207   CheckCondition(err != NULL);
208   Free(&err);
209
210   StartPhase("leveldb_free");
211   db = leveldb_open(options, dbname, &err);
212   CheckCondition(err != NULL);
213   leveldb_free(err);
214   err = NULL;
215
216   StartPhase("open");
217   leveldb_options_set_create_if_missing(options, 1);
218   db = leveldb_open(options, dbname, &err);
219   CheckNoError(err);
220   CheckGet(db, roptions, "foo", NULL);
221
222   StartPhase("put");
223   leveldb_put(db, woptions, "foo", 3, "hello", 5, &err);
224   CheckNoError(err);
225   CheckGet(db, roptions, "foo", "hello");
226
227   StartPhase("compactall");
228   leveldb_compact_range(db, NULL, 0, NULL, 0);
229   CheckGet(db, roptions, "foo", "hello");
230
231   StartPhase("compactrange");
232   leveldb_compact_range(db, "a", 1, "z", 1);
233   CheckGet(db, roptions, "foo", "hello");
234
235   StartPhase("writebatch");
236   {
237     leveldb_writebatch_t* wb = leveldb_writebatch_create();
238     leveldb_writebatch_put(wb, "foo", 3, "a", 1);
239     leveldb_writebatch_clear(wb);
240     leveldb_writebatch_put(wb, "bar", 3, "b", 1);
241     leveldb_writebatch_put(wb, "box", 3, "c", 1);
242     leveldb_writebatch_delete(wb, "bar", 3);
243     leveldb_write(db, woptions, wb, &err);
244     CheckNoError(err);
245     CheckGet(db, roptions, "foo", "hello");
246     CheckGet(db, roptions, "bar", NULL);
247     CheckGet(db, roptions, "box", "c");
248     int pos = 0;
249     leveldb_writebatch_iterate(wb, &pos, CheckPut, CheckDel);
250     CheckCondition(pos == 3);
251     leveldb_writebatch_destroy(wb);
252   }
253
254   StartPhase("iter");
255   {
256     leveldb_iterator_t* iter = leveldb_create_iterator(db, roptions);
257     CheckCondition(!leveldb_iter_valid(iter));
258     leveldb_iter_seek_to_first(iter);
259     CheckCondition(leveldb_iter_valid(iter));
260     CheckIter(iter, "box", "c");
261     leveldb_iter_next(iter);
262     CheckIter(iter, "foo", "hello");
263     leveldb_iter_prev(iter);
264     CheckIter(iter, "box", "c");
265     leveldb_iter_prev(iter);
266     CheckCondition(!leveldb_iter_valid(iter));
267     leveldb_iter_seek_to_last(iter);
268     CheckIter(iter, "foo", "hello");
269     leveldb_iter_seek(iter, "b", 1);
270     CheckIter(iter, "box", "c");
271     leveldb_iter_get_error(iter, &err);
272     CheckNoError(err);
273     leveldb_iter_destroy(iter);
274   }
275
276   StartPhase("approximate_sizes");
277   {
278     int i;
279     int n = 20000;
280     char keybuf[100];
281     char valbuf[100];
282     uint64_t sizes[2];
283     const char* start[2] = { "a", "k00000000000000010000" };
284     size_t start_len[2] = { 1, 21 };
285     const char* limit[2] = { "k00000000000000010000", "z" };
286     size_t limit_len[2] = { 21, 1 };
287     leveldb_writeoptions_set_sync(woptions, 0);
288     for (i = 0; i < n; i++) {
289       snprintf(keybuf, sizeof(keybuf), "k%020d", i);
290       snprintf(valbuf, sizeof(valbuf), "v%020d", i);
291       leveldb_put(db, woptions, keybuf, strlen(keybuf), valbuf, strlen(valbuf),
292                   &err);
293       CheckNoError(err);
294     }
295     leveldb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes);
296     CheckCondition(sizes[0] > 0);
297     CheckCondition(sizes[1] > 0);
298   }
299
300   StartPhase("property");
301   {
302     char* prop = leveldb_property_value(db, "nosuchprop");
303     CheckCondition(prop == NULL);
304     prop = leveldb_property_value(db, "leveldb.stats");
305     CheckCondition(prop != NULL);
306     Free(&prop);
307   }
308
309   StartPhase("snapshot");
310   {
311     const leveldb_snapshot_t* snap;
312     snap = leveldb_create_snapshot(db);
313     leveldb_delete(db, woptions, "foo", 3, &err);
314     CheckNoError(err);
315     leveldb_readoptions_set_snapshot(roptions, snap);
316     CheckGet(db, roptions, "foo", "hello");
317     leveldb_readoptions_set_snapshot(roptions, NULL);
318     CheckGet(db, roptions, "foo", NULL);
319     leveldb_release_snapshot(db, snap);
320   }
321
322   StartPhase("repair");
323   {
324     leveldb_close(db);
325     leveldb_options_set_create_if_missing(options, 0);
326     leveldb_options_set_error_if_exists(options, 0);
327     leveldb_repair_db(options, dbname, &err);
328     CheckNoError(err);
329     db = leveldb_open(options, dbname, &err);
330     CheckNoError(err);
331     CheckGet(db, roptions, "foo", NULL);
332     CheckGet(db, roptions, "bar", NULL);
333     CheckGet(db, roptions, "box", "c");
334     leveldb_options_set_create_if_missing(options, 1);
335     leveldb_options_set_error_if_exists(options, 1);
336   }
337
338   StartPhase("filter");
339   for (run = 0; run < 2; run++) {
340     // First run uses custom filter, second run uses bloom filter
341     CheckNoError(err);
342     leveldb_filterpolicy_t* policy;
343     if (run == 0) {
344       policy = leveldb_filterpolicy_create(
345           NULL, FilterDestroy, FilterCreate, FilterKeyMatch, FilterName);
346     } else {
347       policy = leveldb_filterpolicy_create_bloom(10);
348     }
349
350     // Create new database
351     leveldb_close(db);
352     leveldb_destroy_db(options, dbname, &err);
353     leveldb_options_set_filter_policy(options, policy);
354     db = leveldb_open(options, dbname, &err);
355     CheckNoError(err);
356     leveldb_put(db, woptions, "foo", 3, "foovalue", 8, &err);
357     CheckNoError(err);
358     leveldb_put(db, woptions, "bar", 3, "barvalue", 8, &err);
359     CheckNoError(err);
360     leveldb_compact_range(db, NULL, 0, NULL, 0);
361
362     fake_filter_result = 1;
363     CheckGet(db, roptions, "foo", "foovalue");
364     CheckGet(db, roptions, "bar", "barvalue");
365     if (phase == 0) {
366       // Must not find value when custom filter returns false
367       fake_filter_result = 0;
368       CheckGet(db, roptions, "foo", NULL);
369       CheckGet(db, roptions, "bar", NULL);
370       fake_filter_result = 1;
371
372       CheckGet(db, roptions, "foo", "foovalue");
373       CheckGet(db, roptions, "bar", "barvalue");
374     }
375     leveldb_options_set_filter_policy(options, NULL);
376     leveldb_filterpolicy_destroy(policy);
377   }
378
379   StartPhase("cleanup");
380   leveldb_close(db);
381   leveldb_options_destroy(options);
382   leveldb_readoptions_destroy(roptions);
383   leveldb_writeoptions_destroy(woptions);
384   leveldb_cache_destroy(cache);
385   leveldb_comparator_destroy(cmp);
386   leveldb_env_destroy(env);
387
388   fprintf(stderr, "PASS\n");
389   return 0;
390 }