gabor@google.com | 021ee9c | 2011-08-05 20:40:49 +0000 | [diff] [blame] | 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> |
gabor@google.com | 021ee9c | 2011-08-05 20:40:49 +0000 | [diff] [blame] | 11 | |
| 12 | const char* phase = ""; |
gabor@google.com | 021ee9c | 2011-08-05 20:40:49 +0000 | [diff] [blame] | 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 | |
Sanjay Ghemawat | 85584d4 | 2012-04-17 08:36:46 -0700 | [diff] [blame] | 122 | // Custom filter policy |
| 123 | static unsigned char 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 | unsigned char FilterKeyMatch( |
| 139 | void* arg, |
| 140 | const char* key, size_t length, |
| 141 | const char* filter, size_t filter_length) { |
| 142 | CheckCondition(filter_length == 4); |
| 143 | CheckCondition(memcmp(filter, "fake", 4) == 0); |
| 144 | return fake_filter_result; |
| 145 | } |
| 146 | |
gabor@google.com | 021ee9c | 2011-08-05 20:40:49 +0000 | [diff] [blame] | 147 | int main(int argc, char** argv) { |
| 148 | leveldb_t* db; |
| 149 | leveldb_comparator_t* cmp; |
| 150 | leveldb_cache_t* cache; |
| 151 | leveldb_env_t* env; |
| 152 | leveldb_options_t* options; |
| 153 | leveldb_readoptions_t* roptions; |
| 154 | leveldb_writeoptions_t* woptions; |
costan | 623d014 | 2018-03-09 10:32:55 -0800 | [diff] [blame] | 155 | char* dbname; |
gabor@google.com | 021ee9c | 2011-08-05 20:40:49 +0000 | [diff] [blame] | 156 | char* err = NULL; |
Sanjay Ghemawat | 85584d4 | 2012-04-17 08:36:46 -0700 | [diff] [blame] | 157 | int run = -1; |
gabor@google.com | 021ee9c | 2011-08-05 20:40:49 +0000 | [diff] [blame] | 158 | |
Sanjay Ghemawat | 4076865 | 2012-10-16 16:17:53 -0700 | [diff] [blame] | 159 | CheckCondition(leveldb_major_version() >= 1); |
| 160 | CheckCondition(leveldb_minor_version() >= 1); |
| 161 | |
gabor@google.com | 021ee9c | 2011-08-05 20:40:49 +0000 | [diff] [blame] | 162 | StartPhase("create_objects"); |
| 163 | cmp = leveldb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName); |
| 164 | env = leveldb_create_default_env(); |
| 165 | cache = leveldb_cache_create_lru(100000); |
costan | 623d014 | 2018-03-09 10:32:55 -0800 | [diff] [blame] | 166 | dbname = leveldb_env_get_test_directory(env); |
| 167 | CheckCondition(dbname != NULL); |
gabor@google.com | 021ee9c | 2011-08-05 20:40:49 +0000 | [diff] [blame] | 168 | |
| 169 | options = leveldb_options_create(); |
| 170 | leveldb_options_set_comparator(options, cmp); |
| 171 | leveldb_options_set_error_if_exists(options, 1); |
| 172 | leveldb_options_set_cache(options, cache); |
| 173 | leveldb_options_set_env(options, env); |
| 174 | leveldb_options_set_info_log(options, NULL); |
| 175 | leveldb_options_set_write_buffer_size(options, 100000); |
| 176 | leveldb_options_set_paranoid_checks(options, 1); |
| 177 | leveldb_options_set_max_open_files(options, 10); |
| 178 | leveldb_options_set_block_size(options, 1024); |
| 179 | leveldb_options_set_block_restart_interval(options, 8); |
cmumford | 47cb9e2 | 2017-10-25 16:13:51 -0700 | [diff] [blame] | 180 | leveldb_options_set_max_file_size(options, 3 << 20); |
gabor@google.com | 021ee9c | 2011-08-05 20:40:49 +0000 | [diff] [blame] | 181 | leveldb_options_set_compression(options, leveldb_no_compression); |
| 182 | |
| 183 | roptions = leveldb_readoptions_create(); |
| 184 | leveldb_readoptions_set_verify_checksums(roptions, 1); |
| 185 | leveldb_readoptions_set_fill_cache(roptions, 0); |
| 186 | |
| 187 | woptions = leveldb_writeoptions_create(); |
| 188 | leveldb_writeoptions_set_sync(woptions, 1); |
| 189 | |
| 190 | StartPhase("destroy"); |
| 191 | leveldb_destroy_db(options, dbname, &err); |
| 192 | Free(&err); |
| 193 | |
| 194 | StartPhase("open_error"); |
| 195 | db = leveldb_open(options, dbname, &err); |
| 196 | CheckCondition(err != NULL); |
| 197 | Free(&err); |
| 198 | |
David Grogan | 946e5b5 | 2012-10-12 11:53:12 -0700 | [diff] [blame] | 199 | StartPhase("leveldb_free"); |
| 200 | db = leveldb_open(options, dbname, &err); |
| 201 | CheckCondition(err != NULL); |
| 202 | leveldb_free(err); |
| 203 | err = NULL; |
| 204 | |
gabor@google.com | 021ee9c | 2011-08-05 20:40:49 +0000 | [diff] [blame] | 205 | StartPhase("open"); |
| 206 | leveldb_options_set_create_if_missing(options, 1); |
| 207 | db = leveldb_open(options, dbname, &err); |
| 208 | CheckNoError(err); |
| 209 | CheckGet(db, roptions, "foo", NULL); |
| 210 | |
| 211 | StartPhase("put"); |
| 212 | leveldb_put(db, woptions, "foo", 3, "hello", 5, &err); |
| 213 | CheckNoError(err); |
| 214 | CheckGet(db, roptions, "foo", "hello"); |
| 215 | |
Sanjay Ghemawat | 85584d4 | 2012-04-17 08:36:46 -0700 | [diff] [blame] | 216 | StartPhase("compactall"); |
| 217 | leveldb_compact_range(db, NULL, 0, NULL, 0); |
| 218 | CheckGet(db, roptions, "foo", "hello"); |
| 219 | |
| 220 | StartPhase("compactrange"); |
| 221 | leveldb_compact_range(db, "a", 1, "z", 1); |
| 222 | CheckGet(db, roptions, "foo", "hello"); |
| 223 | |
gabor@google.com | 021ee9c | 2011-08-05 20:40:49 +0000 | [diff] [blame] | 224 | StartPhase("writebatch"); |
| 225 | { |
| 226 | leveldb_writebatch_t* wb = leveldb_writebatch_create(); |
| 227 | leveldb_writebatch_put(wb, "foo", 3, "a", 1); |
| 228 | leveldb_writebatch_clear(wb); |
| 229 | leveldb_writebatch_put(wb, "bar", 3, "b", 1); |
| 230 | leveldb_writebatch_put(wb, "box", 3, "c", 1); |
costan | 16a2b8b | 2018-08-19 15:17:37 -0700 | [diff] [blame] | 231 | |
| 232 | leveldb_writebatch_t* wb2 = leveldb_writebatch_create(); |
| 233 | leveldb_writebatch_delete(wb2, "bar", 3); |
| 234 | leveldb_writebatch_append(wb, wb2); |
| 235 | leveldb_writebatch_destroy(wb2); |
| 236 | |
gabor@google.com | 021ee9c | 2011-08-05 20:40:49 +0000 | [diff] [blame] | 237 | leveldb_write(db, woptions, wb, &err); |
| 238 | CheckNoError(err); |
| 239 | CheckGet(db, roptions, "foo", "hello"); |
| 240 | CheckGet(db, roptions, "bar", NULL); |
| 241 | CheckGet(db, roptions, "box", "c"); |
costan | 16a2b8b | 2018-08-19 15:17:37 -0700 | [diff] [blame] | 242 | |
gabor@google.com | 021ee9c | 2011-08-05 20:40:49 +0000 | [diff] [blame] | 243 | int pos = 0; |
| 244 | leveldb_writebatch_iterate(wb, &pos, CheckPut, CheckDel); |
| 245 | CheckCondition(pos == 3); |
| 246 | leveldb_writebatch_destroy(wb); |
| 247 | } |
| 248 | |
| 249 | StartPhase("iter"); |
| 250 | { |
| 251 | leveldb_iterator_t* iter = leveldb_create_iterator(db, roptions); |
| 252 | CheckCondition(!leveldb_iter_valid(iter)); |
| 253 | leveldb_iter_seek_to_first(iter); |
| 254 | CheckCondition(leveldb_iter_valid(iter)); |
| 255 | CheckIter(iter, "box", "c"); |
| 256 | leveldb_iter_next(iter); |
| 257 | CheckIter(iter, "foo", "hello"); |
| 258 | leveldb_iter_prev(iter); |
| 259 | CheckIter(iter, "box", "c"); |
| 260 | leveldb_iter_prev(iter); |
| 261 | CheckCondition(!leveldb_iter_valid(iter)); |
| 262 | leveldb_iter_seek_to_last(iter); |
| 263 | CheckIter(iter, "foo", "hello"); |
| 264 | leveldb_iter_seek(iter, "b", 1); |
| 265 | CheckIter(iter, "box", "c"); |
| 266 | leveldb_iter_get_error(iter, &err); |
| 267 | CheckNoError(err); |
| 268 | leveldb_iter_destroy(iter); |
| 269 | } |
| 270 | |
| 271 | StartPhase("approximate_sizes"); |
| 272 | { |
| 273 | int i; |
| 274 | int n = 20000; |
| 275 | char keybuf[100]; |
| 276 | char valbuf[100]; |
| 277 | uint64_t sizes[2]; |
| 278 | const char* start[2] = { "a", "k00000000000000010000" }; |
| 279 | size_t start_len[2] = { 1, 21 }; |
| 280 | const char* limit[2] = { "k00000000000000010000", "z" }; |
| 281 | size_t limit_len[2] = { 21, 1 }; |
| 282 | leveldb_writeoptions_set_sync(woptions, 0); |
| 283 | for (i = 0; i < n; i++) { |
| 284 | snprintf(keybuf, sizeof(keybuf), "k%020d", i); |
| 285 | snprintf(valbuf, sizeof(valbuf), "v%020d", i); |
| 286 | leveldb_put(db, woptions, keybuf, strlen(keybuf), valbuf, strlen(valbuf), |
| 287 | &err); |
| 288 | CheckNoError(err); |
| 289 | } |
| 290 | leveldb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes); |
| 291 | CheckCondition(sizes[0] > 0); |
| 292 | CheckCondition(sizes[1] > 0); |
| 293 | } |
| 294 | |
| 295 | StartPhase("property"); |
| 296 | { |
| 297 | char* prop = leveldb_property_value(db, "nosuchprop"); |
| 298 | CheckCondition(prop == NULL); |
| 299 | prop = leveldb_property_value(db, "leveldb.stats"); |
| 300 | CheckCondition(prop != NULL); |
| 301 | Free(&prop); |
| 302 | } |
| 303 | |
| 304 | StartPhase("snapshot"); |
| 305 | { |
| 306 | const leveldb_snapshot_t* snap; |
| 307 | snap = leveldb_create_snapshot(db); |
| 308 | leveldb_delete(db, woptions, "foo", 3, &err); |
| 309 | CheckNoError(err); |
| 310 | leveldb_readoptions_set_snapshot(roptions, snap); |
| 311 | CheckGet(db, roptions, "foo", "hello"); |
| 312 | leveldb_readoptions_set_snapshot(roptions, NULL); |
| 313 | CheckGet(db, roptions, "foo", NULL); |
| 314 | leveldb_release_snapshot(db, snap); |
| 315 | } |
| 316 | |
| 317 | StartPhase("repair"); |
| 318 | { |
| 319 | leveldb_close(db); |
| 320 | leveldb_options_set_create_if_missing(options, 0); |
| 321 | leveldb_options_set_error_if_exists(options, 0); |
| 322 | leveldb_repair_db(options, dbname, &err); |
| 323 | CheckNoError(err); |
| 324 | db = leveldb_open(options, dbname, &err); |
| 325 | CheckNoError(err); |
| 326 | CheckGet(db, roptions, "foo", NULL); |
| 327 | CheckGet(db, roptions, "bar", NULL); |
| 328 | CheckGet(db, roptions, "box", "c"); |
Sanjay Ghemawat | 85584d4 | 2012-04-17 08:36:46 -0700 | [diff] [blame] | 329 | leveldb_options_set_create_if_missing(options, 1); |
| 330 | leveldb_options_set_error_if_exists(options, 1); |
| 331 | } |
| 332 | |
| 333 | StartPhase("filter"); |
| 334 | for (run = 0; run < 2; run++) { |
| 335 | // First run uses custom filter, second run uses bloom filter |
| 336 | CheckNoError(err); |
| 337 | leveldb_filterpolicy_t* policy; |
| 338 | if (run == 0) { |
| 339 | policy = leveldb_filterpolicy_create( |
| 340 | NULL, FilterDestroy, FilterCreate, FilterKeyMatch, FilterName); |
| 341 | } else { |
| 342 | policy = leveldb_filterpolicy_create_bloom(10); |
| 343 | } |
| 344 | |
| 345 | // Create new database |
| 346 | leveldb_close(db); |
| 347 | leveldb_destroy_db(options, dbname, &err); |
| 348 | leveldb_options_set_filter_policy(options, policy); |
| 349 | db = leveldb_open(options, dbname, &err); |
| 350 | CheckNoError(err); |
| 351 | leveldb_put(db, woptions, "foo", 3, "foovalue", 8, &err); |
| 352 | CheckNoError(err); |
| 353 | leveldb_put(db, woptions, "bar", 3, "barvalue", 8, &err); |
| 354 | CheckNoError(err); |
| 355 | leveldb_compact_range(db, NULL, 0, NULL, 0); |
| 356 | |
| 357 | fake_filter_result = 1; |
| 358 | CheckGet(db, roptions, "foo", "foovalue"); |
| 359 | CheckGet(db, roptions, "bar", "barvalue"); |
| 360 | if (phase == 0) { |
| 361 | // Must not find value when custom filter returns false |
| 362 | fake_filter_result = 0; |
| 363 | CheckGet(db, roptions, "foo", NULL); |
| 364 | CheckGet(db, roptions, "bar", NULL); |
| 365 | fake_filter_result = 1; |
| 366 | |
| 367 | CheckGet(db, roptions, "foo", "foovalue"); |
| 368 | CheckGet(db, roptions, "bar", "barvalue"); |
| 369 | } |
| 370 | leveldb_options_set_filter_policy(options, NULL); |
| 371 | leveldb_filterpolicy_destroy(policy); |
gabor@google.com | 021ee9c | 2011-08-05 20:40:49 +0000 | [diff] [blame] | 372 | } |
| 373 | |
| 374 | StartPhase("cleanup"); |
| 375 | leveldb_close(db); |
| 376 | leveldb_options_destroy(options); |
| 377 | leveldb_readoptions_destroy(roptions); |
| 378 | leveldb_writeoptions_destroy(woptions); |
costan | 623d014 | 2018-03-09 10:32:55 -0800 | [diff] [blame] | 379 | leveldb_free(dbname); |
gabor@google.com | 021ee9c | 2011-08-05 20:40:49 +0000 | [diff] [blame] | 380 | leveldb_cache_destroy(cache); |
| 381 | leveldb_comparator_destroy(cmp); |
| 382 | leveldb_env_destroy(env); |
| 383 | |
| 384 | fprintf(stderr, "PASS\n"); |
| 385 | return 0; |
| 386 | } |