gui: fix ban from qt console
[bitcoinplatinum.git] / src / leveldb / table / table_test.cc
blobc723bf84cf5f55fae9b56ab324ebfdc83ba9be52
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.
5 #include "leveldb/table.h"
7 #include <map>
8 #include <string>
9 #include "db/dbformat.h"
10 #include "db/memtable.h"
11 #include "db/write_batch_internal.h"
12 #include "leveldb/db.h"
13 #include "leveldb/env.h"
14 #include "leveldb/iterator.h"
15 #include "leveldb/table_builder.h"
16 #include "table/block.h"
17 #include "table/block_builder.h"
18 #include "table/format.h"
19 #include "util/random.h"
20 #include "util/testharness.h"
21 #include "util/testutil.h"
23 namespace leveldb {
25 // Return reverse of "key".
26 // Used to test non-lexicographic comparators.
27 static std::string Reverse(const Slice& key) {
28 std::string str(key.ToString());
29 std::string rev("");
30 for (std::string::reverse_iterator rit = str.rbegin();
31 rit != str.rend(); ++rit) {
32 rev.push_back(*rit);
34 return rev;
37 namespace {
38 class ReverseKeyComparator : public Comparator {
39 public:
40 virtual const char* Name() const {
41 return "leveldb.ReverseBytewiseComparator";
44 virtual int Compare(const Slice& a, const Slice& b) const {
45 return BytewiseComparator()->Compare(Reverse(a), Reverse(b));
48 virtual void FindShortestSeparator(
49 std::string* start,
50 const Slice& limit) const {
51 std::string s = Reverse(*start);
52 std::string l = Reverse(limit);
53 BytewiseComparator()->FindShortestSeparator(&s, l);
54 *start = Reverse(s);
57 virtual void FindShortSuccessor(std::string* key) const {
58 std::string s = Reverse(*key);
59 BytewiseComparator()->FindShortSuccessor(&s);
60 *key = Reverse(s);
63 } // namespace
64 static ReverseKeyComparator reverse_key_comparator;
66 static void Increment(const Comparator* cmp, std::string* key) {
67 if (cmp == BytewiseComparator()) {
68 key->push_back('\0');
69 } else {
70 assert(cmp == &reverse_key_comparator);
71 std::string rev = Reverse(*key);
72 rev.push_back('\0');
73 *key = Reverse(rev);
77 // An STL comparator that uses a Comparator
78 namespace {
79 struct STLLessThan {
80 const Comparator* cmp;
82 STLLessThan() : cmp(BytewiseComparator()) { }
83 STLLessThan(const Comparator* c) : cmp(c) { }
84 bool operator()(const std::string& a, const std::string& b) const {
85 return cmp->Compare(Slice(a), Slice(b)) < 0;
88 } // namespace
90 class StringSink: public WritableFile {
91 public:
92 ~StringSink() { }
94 const std::string& contents() const { return contents_; }
96 virtual Status Close() { return Status::OK(); }
97 virtual Status Flush() { return Status::OK(); }
98 virtual Status Sync() { return Status::OK(); }
100 virtual Status Append(const Slice& data) {
101 contents_.append(data.data(), data.size());
102 return Status::OK();
105 private:
106 std::string contents_;
110 class StringSource: public RandomAccessFile {
111 public:
112 StringSource(const Slice& contents)
113 : contents_(contents.data(), contents.size()) {
116 virtual ~StringSource() { }
118 uint64_t Size() const { return contents_.size(); }
120 virtual Status Read(uint64_t offset, size_t n, Slice* result,
121 char* scratch) const {
122 if (offset > contents_.size()) {
123 return Status::InvalidArgument("invalid Read offset");
125 if (offset + n > contents_.size()) {
126 n = contents_.size() - offset;
128 memcpy(scratch, &contents_[offset], n);
129 *result = Slice(scratch, n);
130 return Status::OK();
133 private:
134 std::string contents_;
137 typedef std::map<std::string, std::string, STLLessThan> KVMap;
139 // Helper class for tests to unify the interface between
140 // BlockBuilder/TableBuilder and Block/Table.
141 class Constructor {
142 public:
143 explicit Constructor(const Comparator* cmp) : data_(STLLessThan(cmp)) { }
144 virtual ~Constructor() { }
146 void Add(const std::string& key, const Slice& value) {
147 data_[key] = value.ToString();
150 // Finish constructing the data structure with all the keys that have
151 // been added so far. Returns the keys in sorted order in "*keys"
152 // and stores the key/value pairs in "*kvmap"
153 void Finish(const Options& options,
154 std::vector<std::string>* keys,
155 KVMap* kvmap) {
156 *kvmap = data_;
157 keys->clear();
158 for (KVMap::const_iterator it = data_.begin();
159 it != data_.end();
160 ++it) {
161 keys->push_back(it->first);
163 data_.clear();
164 Status s = FinishImpl(options, *kvmap);
165 ASSERT_TRUE(s.ok()) << s.ToString();
168 // Construct the data structure from the data in "data"
169 virtual Status FinishImpl(const Options& options, const KVMap& data) = 0;
171 virtual Iterator* NewIterator() const = 0;
173 virtual const KVMap& data() { return data_; }
175 virtual DB* db() const { return NULL; } // Overridden in DBConstructor
177 private:
178 KVMap data_;
181 class BlockConstructor: public Constructor {
182 public:
183 explicit BlockConstructor(const Comparator* cmp)
184 : Constructor(cmp),
185 comparator_(cmp),
186 block_(NULL) { }
187 ~BlockConstructor() {
188 delete block_;
190 virtual Status FinishImpl(const Options& options, const KVMap& data) {
191 delete block_;
192 block_ = NULL;
193 BlockBuilder builder(&options);
195 for (KVMap::const_iterator it = data.begin();
196 it != data.end();
197 ++it) {
198 builder.Add(it->first, it->second);
200 // Open the block
201 data_ = builder.Finish().ToString();
202 BlockContents contents;
203 contents.data = data_;
204 contents.cachable = false;
205 contents.heap_allocated = false;
206 block_ = new Block(contents);
207 return Status::OK();
209 virtual Iterator* NewIterator() const {
210 return block_->NewIterator(comparator_);
213 private:
214 const Comparator* comparator_;
215 std::string data_;
216 Block* block_;
218 BlockConstructor();
221 class TableConstructor: public Constructor {
222 public:
223 TableConstructor(const Comparator* cmp)
224 : Constructor(cmp),
225 source_(NULL), table_(NULL) {
227 ~TableConstructor() {
228 Reset();
230 virtual Status FinishImpl(const Options& options, const KVMap& data) {
231 Reset();
232 StringSink sink;
233 TableBuilder builder(options, &sink);
235 for (KVMap::const_iterator it = data.begin();
236 it != data.end();
237 ++it) {
238 builder.Add(it->first, it->second);
239 ASSERT_TRUE(builder.status().ok());
241 Status s = builder.Finish();
242 ASSERT_TRUE(s.ok()) << s.ToString();
244 ASSERT_EQ(sink.contents().size(), builder.FileSize());
246 // Open the table
247 source_ = new StringSource(sink.contents());
248 Options table_options;
249 table_options.comparator = options.comparator;
250 return Table::Open(table_options, source_, sink.contents().size(), &table_);
253 virtual Iterator* NewIterator() const {
254 return table_->NewIterator(ReadOptions());
257 uint64_t ApproximateOffsetOf(const Slice& key) const {
258 return table_->ApproximateOffsetOf(key);
261 private:
262 void Reset() {
263 delete table_;
264 delete source_;
265 table_ = NULL;
266 source_ = NULL;
269 StringSource* source_;
270 Table* table_;
272 TableConstructor();
275 // A helper class that converts internal format keys into user keys
276 class KeyConvertingIterator: public Iterator {
277 public:
278 explicit KeyConvertingIterator(Iterator* iter) : iter_(iter) { }
279 virtual ~KeyConvertingIterator() { delete iter_; }
280 virtual bool Valid() const { return iter_->Valid(); }
281 virtual void Seek(const Slice& target) {
282 ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue);
283 std::string encoded;
284 AppendInternalKey(&encoded, ikey);
285 iter_->Seek(encoded);
287 virtual void SeekToFirst() { iter_->SeekToFirst(); }
288 virtual void SeekToLast() { iter_->SeekToLast(); }
289 virtual void Next() { iter_->Next(); }
290 virtual void Prev() { iter_->Prev(); }
292 virtual Slice key() const {
293 assert(Valid());
294 ParsedInternalKey key;
295 if (!ParseInternalKey(iter_->key(), &key)) {
296 status_ = Status::Corruption("malformed internal key");
297 return Slice("corrupted key");
299 return key.user_key;
302 virtual Slice value() const { return iter_->value(); }
303 virtual Status status() const {
304 return status_.ok() ? iter_->status() : status_;
307 private:
308 mutable Status status_;
309 Iterator* iter_;
311 // No copying allowed
312 KeyConvertingIterator(const KeyConvertingIterator&);
313 void operator=(const KeyConvertingIterator&);
316 class MemTableConstructor: public Constructor {
317 public:
318 explicit MemTableConstructor(const Comparator* cmp)
319 : Constructor(cmp),
320 internal_comparator_(cmp) {
321 memtable_ = new MemTable(internal_comparator_);
322 memtable_->Ref();
324 ~MemTableConstructor() {
325 memtable_->Unref();
327 virtual Status FinishImpl(const Options& options, const KVMap& data) {
328 memtable_->Unref();
329 memtable_ = new MemTable(internal_comparator_);
330 memtable_->Ref();
331 int seq = 1;
332 for (KVMap::const_iterator it = data.begin();
333 it != data.end();
334 ++it) {
335 memtable_->Add(seq, kTypeValue, it->first, it->second);
336 seq++;
338 return Status::OK();
340 virtual Iterator* NewIterator() const {
341 return new KeyConvertingIterator(memtable_->NewIterator());
344 private:
345 InternalKeyComparator internal_comparator_;
346 MemTable* memtable_;
349 class DBConstructor: public Constructor {
350 public:
351 explicit DBConstructor(const Comparator* cmp)
352 : Constructor(cmp),
353 comparator_(cmp) {
354 db_ = NULL;
355 NewDB();
357 ~DBConstructor() {
358 delete db_;
360 virtual Status FinishImpl(const Options& options, const KVMap& data) {
361 delete db_;
362 db_ = NULL;
363 NewDB();
364 for (KVMap::const_iterator it = data.begin();
365 it != data.end();
366 ++it) {
367 WriteBatch batch;
368 batch.Put(it->first, it->second);
369 ASSERT_TRUE(db_->Write(WriteOptions(), &batch).ok());
371 return Status::OK();
373 virtual Iterator* NewIterator() const {
374 return db_->NewIterator(ReadOptions());
377 virtual DB* db() const { return db_; }
379 private:
380 void NewDB() {
381 std::string name = test::TmpDir() + "/table_testdb";
383 Options options;
384 options.comparator = comparator_;
385 Status status = DestroyDB(name, options);
386 ASSERT_TRUE(status.ok()) << status.ToString();
388 options.create_if_missing = true;
389 options.error_if_exists = true;
390 options.write_buffer_size = 10000; // Something small to force merging
391 status = DB::Open(options, name, &db_);
392 ASSERT_TRUE(status.ok()) << status.ToString();
395 const Comparator* comparator_;
396 DB* db_;
399 enum TestType {
400 TABLE_TEST,
401 BLOCK_TEST,
402 MEMTABLE_TEST,
403 DB_TEST
406 struct TestArgs {
407 TestType type;
408 bool reverse_compare;
409 int restart_interval;
412 static const TestArgs kTestArgList[] = {
413 { TABLE_TEST, false, 16 },
414 { TABLE_TEST, false, 1 },
415 { TABLE_TEST, false, 1024 },
416 { TABLE_TEST, true, 16 },
417 { TABLE_TEST, true, 1 },
418 { TABLE_TEST, true, 1024 },
420 { BLOCK_TEST, false, 16 },
421 { BLOCK_TEST, false, 1 },
422 { BLOCK_TEST, false, 1024 },
423 { BLOCK_TEST, true, 16 },
424 { BLOCK_TEST, true, 1 },
425 { BLOCK_TEST, true, 1024 },
427 // Restart interval does not matter for memtables
428 { MEMTABLE_TEST, false, 16 },
429 { MEMTABLE_TEST, true, 16 },
431 // Do not bother with restart interval variations for DB
432 { DB_TEST, false, 16 },
433 { DB_TEST, true, 16 },
435 static const int kNumTestArgs = sizeof(kTestArgList) / sizeof(kTestArgList[0]);
437 class Harness {
438 public:
439 Harness() : constructor_(NULL) { }
441 void Init(const TestArgs& args) {
442 delete constructor_;
443 constructor_ = NULL;
444 options_ = Options();
446 options_.block_restart_interval = args.restart_interval;
447 // Use shorter block size for tests to exercise block boundary
448 // conditions more.
449 options_.block_size = 256;
450 if (args.reverse_compare) {
451 options_.comparator = &reverse_key_comparator;
453 switch (args.type) {
454 case TABLE_TEST:
455 constructor_ = new TableConstructor(options_.comparator);
456 break;
457 case BLOCK_TEST:
458 constructor_ = new BlockConstructor(options_.comparator);
459 break;
460 case MEMTABLE_TEST:
461 constructor_ = new MemTableConstructor(options_.comparator);
462 break;
463 case DB_TEST:
464 constructor_ = new DBConstructor(options_.comparator);
465 break;
469 ~Harness() {
470 delete constructor_;
473 void Add(const std::string& key, const std::string& value) {
474 constructor_->Add(key, value);
477 void Test(Random* rnd) {
478 std::vector<std::string> keys;
479 KVMap data;
480 constructor_->Finish(options_, &keys, &data);
482 TestForwardScan(keys, data);
483 TestBackwardScan(keys, data);
484 TestRandomAccess(rnd, keys, data);
487 void TestForwardScan(const std::vector<std::string>& keys,
488 const KVMap& data) {
489 Iterator* iter = constructor_->NewIterator();
490 ASSERT_TRUE(!iter->Valid());
491 iter->SeekToFirst();
492 for (KVMap::const_iterator model_iter = data.begin();
493 model_iter != data.end();
494 ++model_iter) {
495 ASSERT_EQ(ToString(data, model_iter), ToString(iter));
496 iter->Next();
498 ASSERT_TRUE(!iter->Valid());
499 delete iter;
502 void TestBackwardScan(const std::vector<std::string>& keys,
503 const KVMap& data) {
504 Iterator* iter = constructor_->NewIterator();
505 ASSERT_TRUE(!iter->Valid());
506 iter->SeekToLast();
507 for (KVMap::const_reverse_iterator model_iter = data.rbegin();
508 model_iter != data.rend();
509 ++model_iter) {
510 ASSERT_EQ(ToString(data, model_iter), ToString(iter));
511 iter->Prev();
513 ASSERT_TRUE(!iter->Valid());
514 delete iter;
517 void TestRandomAccess(Random* rnd,
518 const std::vector<std::string>& keys,
519 const KVMap& data) {
520 static const bool kVerbose = false;
521 Iterator* iter = constructor_->NewIterator();
522 ASSERT_TRUE(!iter->Valid());
523 KVMap::const_iterator model_iter = data.begin();
524 if (kVerbose) fprintf(stderr, "---\n");
525 for (int i = 0; i < 200; i++) {
526 const int toss = rnd->Uniform(5);
527 switch (toss) {
528 case 0: {
529 if (iter->Valid()) {
530 if (kVerbose) fprintf(stderr, "Next\n");
531 iter->Next();
532 ++model_iter;
533 ASSERT_EQ(ToString(data, model_iter), ToString(iter));
535 break;
538 case 1: {
539 if (kVerbose) fprintf(stderr, "SeekToFirst\n");
540 iter->SeekToFirst();
541 model_iter = data.begin();
542 ASSERT_EQ(ToString(data, model_iter), ToString(iter));
543 break;
546 case 2: {
547 std::string key = PickRandomKey(rnd, keys);
548 model_iter = data.lower_bound(key);
549 if (kVerbose) fprintf(stderr, "Seek '%s'\n",
550 EscapeString(key).c_str());
551 iter->Seek(Slice(key));
552 ASSERT_EQ(ToString(data, model_iter), ToString(iter));
553 break;
556 case 3: {
557 if (iter->Valid()) {
558 if (kVerbose) fprintf(stderr, "Prev\n");
559 iter->Prev();
560 if (model_iter == data.begin()) {
561 model_iter = data.end(); // Wrap around to invalid value
562 } else {
563 --model_iter;
565 ASSERT_EQ(ToString(data, model_iter), ToString(iter));
567 break;
570 case 4: {
571 if (kVerbose) fprintf(stderr, "SeekToLast\n");
572 iter->SeekToLast();
573 if (keys.empty()) {
574 model_iter = data.end();
575 } else {
576 std::string last = data.rbegin()->first;
577 model_iter = data.lower_bound(last);
579 ASSERT_EQ(ToString(data, model_iter), ToString(iter));
580 break;
584 delete iter;
587 std::string ToString(const KVMap& data, const KVMap::const_iterator& it) {
588 if (it == data.end()) {
589 return "END";
590 } else {
591 return "'" + it->first + "->" + it->second + "'";
595 std::string ToString(const KVMap& data,
596 const KVMap::const_reverse_iterator& it) {
597 if (it == data.rend()) {
598 return "END";
599 } else {
600 return "'" + it->first + "->" + it->second + "'";
604 std::string ToString(const Iterator* it) {
605 if (!it->Valid()) {
606 return "END";
607 } else {
608 return "'" + it->key().ToString() + "->" + it->value().ToString() + "'";
612 std::string PickRandomKey(Random* rnd, const std::vector<std::string>& keys) {
613 if (keys.empty()) {
614 return "foo";
615 } else {
616 const int index = rnd->Uniform(keys.size());
617 std::string result = keys[index];
618 switch (rnd->Uniform(3)) {
619 case 0:
620 // Return an existing key
621 break;
622 case 1: {
623 // Attempt to return something smaller than an existing key
624 if (result.size() > 0 && result[result.size()-1] > '\0') {
625 result[result.size()-1]--;
627 break;
629 case 2: {
630 // Return something larger than an existing key
631 Increment(options_.comparator, &result);
632 break;
635 return result;
639 // Returns NULL if not running against a DB
640 DB* db() const { return constructor_->db(); }
642 private:
643 Options options_;
644 Constructor* constructor_;
647 // Test empty table/block.
648 TEST(Harness, Empty) {
649 for (int i = 0; i < kNumTestArgs; i++) {
650 Init(kTestArgList[i]);
651 Random rnd(test::RandomSeed() + 1);
652 Test(&rnd);
656 // Special test for a block with no restart entries. The C++ leveldb
657 // code never generates such blocks, but the Java version of leveldb
658 // seems to.
659 TEST(Harness, ZeroRestartPointsInBlock) {
660 char data[sizeof(uint32_t)];
661 memset(data, 0, sizeof(data));
662 BlockContents contents;
663 contents.data = Slice(data, sizeof(data));
664 contents.cachable = false;
665 contents.heap_allocated = false;
666 Block block(contents);
667 Iterator* iter = block.NewIterator(BytewiseComparator());
668 iter->SeekToFirst();
669 ASSERT_TRUE(!iter->Valid());
670 iter->SeekToLast();
671 ASSERT_TRUE(!iter->Valid());
672 iter->Seek("foo");
673 ASSERT_TRUE(!iter->Valid());
674 delete iter;
677 // Test the empty key
678 TEST(Harness, SimpleEmptyKey) {
679 for (int i = 0; i < kNumTestArgs; i++) {
680 Init(kTestArgList[i]);
681 Random rnd(test::RandomSeed() + 1);
682 Add("", "v");
683 Test(&rnd);
687 TEST(Harness, SimpleSingle) {
688 for (int i = 0; i < kNumTestArgs; i++) {
689 Init(kTestArgList[i]);
690 Random rnd(test::RandomSeed() + 2);
691 Add("abc", "v");
692 Test(&rnd);
696 TEST(Harness, SimpleMulti) {
697 for (int i = 0; i < kNumTestArgs; i++) {
698 Init(kTestArgList[i]);
699 Random rnd(test::RandomSeed() + 3);
700 Add("abc", "v");
701 Add("abcd", "v");
702 Add("ac", "v2");
703 Test(&rnd);
707 TEST(Harness, SimpleSpecialKey) {
708 for (int i = 0; i < kNumTestArgs; i++) {
709 Init(kTestArgList[i]);
710 Random rnd(test::RandomSeed() + 4);
711 Add("\xff\xff", "v3");
712 Test(&rnd);
716 TEST(Harness, Randomized) {
717 for (int i = 0; i < kNumTestArgs; i++) {
718 Init(kTestArgList[i]);
719 Random rnd(test::RandomSeed() + 5);
720 for (int num_entries = 0; num_entries < 2000;
721 num_entries += (num_entries < 50 ? 1 : 200)) {
722 if ((num_entries % 10) == 0) {
723 fprintf(stderr, "case %d of %d: num_entries = %d\n",
724 (i + 1), int(kNumTestArgs), num_entries);
726 for (int e = 0; e < num_entries; e++) {
727 std::string v;
728 Add(test::RandomKey(&rnd, rnd.Skewed(4)),
729 test::RandomString(&rnd, rnd.Skewed(5), &v).ToString());
731 Test(&rnd);
736 TEST(Harness, RandomizedLongDB) {
737 Random rnd(test::RandomSeed());
738 TestArgs args = { DB_TEST, false, 16 };
739 Init(args);
740 int num_entries = 100000;
741 for (int e = 0; e < num_entries; e++) {
742 std::string v;
743 Add(test::RandomKey(&rnd, rnd.Skewed(4)),
744 test::RandomString(&rnd, rnd.Skewed(5), &v).ToString());
746 Test(&rnd);
748 // We must have created enough data to force merging
749 int files = 0;
750 for (int level = 0; level < config::kNumLevels; level++) {
751 std::string value;
752 char name[100];
753 snprintf(name, sizeof(name), "leveldb.num-files-at-level%d", level);
754 ASSERT_TRUE(db()->GetProperty(name, &value));
755 files += atoi(value.c_str());
757 ASSERT_GT(files, 0);
760 class MemTableTest { };
762 TEST(MemTableTest, Simple) {
763 InternalKeyComparator cmp(BytewiseComparator());
764 MemTable* memtable = new MemTable(cmp);
765 memtable->Ref();
766 WriteBatch batch;
767 WriteBatchInternal::SetSequence(&batch, 100);
768 batch.Put(std::string("k1"), std::string("v1"));
769 batch.Put(std::string("k2"), std::string("v2"));
770 batch.Put(std::string("k3"), std::string("v3"));
771 batch.Put(std::string("largekey"), std::string("vlarge"));
772 ASSERT_TRUE(WriteBatchInternal::InsertInto(&batch, memtable).ok());
774 Iterator* iter = memtable->NewIterator();
775 iter->SeekToFirst();
776 while (iter->Valid()) {
777 fprintf(stderr, "key: '%s' -> '%s'\n",
778 iter->key().ToString().c_str(),
779 iter->value().ToString().c_str());
780 iter->Next();
783 delete iter;
784 memtable->Unref();
787 static bool Between(uint64_t val, uint64_t low, uint64_t high) {
788 bool result = (val >= low) && (val <= high);
789 if (!result) {
790 fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n",
791 (unsigned long long)(val),
792 (unsigned long long)(low),
793 (unsigned long long)(high));
795 return result;
798 class TableTest { };
800 TEST(TableTest, ApproximateOffsetOfPlain) {
801 TableConstructor c(BytewiseComparator());
802 c.Add("k01", "hello");
803 c.Add("k02", "hello2");
804 c.Add("k03", std::string(10000, 'x'));
805 c.Add("k04", std::string(200000, 'x'));
806 c.Add("k05", std::string(300000, 'x'));
807 c.Add("k06", "hello3");
808 c.Add("k07", std::string(100000, 'x'));
809 std::vector<std::string> keys;
810 KVMap kvmap;
811 Options options;
812 options.block_size = 1024;
813 options.compression = kNoCompression;
814 c.Finish(options, &keys, &kvmap);
816 ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0));
817 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0));
818 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01a"), 0, 0));
819 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0));
820 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 0, 0));
821 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 10000, 11000));
822 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04a"), 210000, 211000));
823 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k05"), 210000, 211000));
824 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k06"), 510000, 511000));
825 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k07"), 510000, 511000));
826 ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 610000, 612000));
830 static bool SnappyCompressionSupported() {
831 std::string out;
832 Slice in = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
833 return port::Snappy_Compress(in.data(), in.size(), &out);
836 TEST(TableTest, ApproximateOffsetOfCompressed) {
837 if (!SnappyCompressionSupported()) {
838 fprintf(stderr, "skipping compression tests\n");
839 return;
842 Random rnd(301);
843 TableConstructor c(BytewiseComparator());
844 std::string tmp;
845 c.Add("k01", "hello");
846 c.Add("k02", test::CompressibleString(&rnd, 0.25, 10000, &tmp));
847 c.Add("k03", "hello3");
848 c.Add("k04", test::CompressibleString(&rnd, 0.25, 10000, &tmp));
849 std::vector<std::string> keys;
850 KVMap kvmap;
851 Options options;
852 options.block_size = 1024;
853 options.compression = kSnappyCompression;
854 c.Finish(options, &keys, &kvmap);
856 ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0));
857 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0));
858 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0));
859 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 2000, 3000));
860 ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 2000, 3000));
861 ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 4000, 6000));
864 } // namespace leveldb
866 int main(int argc, char** argv) {
867 return leveldb::test::RunAllTests();