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