c_test.c raw

   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  }
 385