Summary: This diff replaces BlockBasedTable in flush_job_test with TableMock, making it depend on less things and making it closer to an unit test than integration test. It also introduces a framework to compile mock classes -- Any file named *mock.cc will not be compiled into the build. It will only get compiled into the tests. What way we can mock out most other classes, Version, VersionSet, DBImpl, etc. Test Plan: flush_job_test Reviewers: ljin, rven, yhchiang, sdong Reviewed By: sdong Subscribers: dhruba, leveldb Differential Revision: https://reviews.facebook.net/D27681main
parent
fb3f8ffe5e
commit
abac3d6476
@ -0,0 +1,95 @@ |
|||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||||
|
// Copyright (c) 2013, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under the BSD-style license found in the
|
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
// of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
|
||||||
|
#include "rocksdb/table_properties.h" |
||||||
|
#include "table/mock_table.h" |
||||||
|
#include "table/get_context.h" |
||||||
|
#include "db/dbformat.h" |
||||||
|
#include "port/port.h" |
||||||
|
#include "util/coding.h" |
||||||
|
|
||||||
|
namespace rocksdb { |
||||||
|
|
||||||
|
Iterator* MockTableReader::NewIterator(const ReadOptions&, Arena* arena) { |
||||||
|
return new MockTableIterator(table_); |
||||||
|
} |
||||||
|
|
||||||
|
Status MockTableReader::Get(const ReadOptions&, const Slice& key, |
||||||
|
GetContext* get_context) { |
||||||
|
std::unique_ptr<MockTableIterator> iter(new MockTableIterator(table_)); |
||||||
|
for (iter->Seek(key); iter->Valid(); iter->Next()) { |
||||||
|
ParsedInternalKey parsed_key; |
||||||
|
if (!ParseInternalKey(iter->key(), &parsed_key)) { |
||||||
|
return Status::Corruption(Slice()); |
||||||
|
} |
||||||
|
|
||||||
|
if (!get_context->SaveValue(parsed_key, iter->value())) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return Status::OK(); |
||||||
|
} |
||||||
|
|
||||||
|
std::shared_ptr<const TableProperties> MockTableReader::GetTableProperties() |
||||||
|
const { |
||||||
|
return std::shared_ptr<const TableProperties>(new TableProperties()); |
||||||
|
} |
||||||
|
|
||||||
|
MockTableFactory::MockTableFactory() : next_id_(1) {} |
||||||
|
|
||||||
|
Status MockTableFactory::NewTableReader( |
||||||
|
const ImmutableCFOptions& ioptions, const EnvOptions& env_options, |
||||||
|
const InternalKeyComparator& internal_key, |
||||||
|
unique_ptr<RandomAccessFile>&& file, uint64_t file_size, |
||||||
|
unique_ptr<TableReader>* table_reader) const { |
||||||
|
uint32_t id = GetIDFromFile(file.get()); |
||||||
|
|
||||||
|
MutexLock lock_guard(&file_system_.mutex); |
||||||
|
|
||||||
|
auto it = file_system_.files.find(id); |
||||||
|
if (it == file_system_.files.end()) { |
||||||
|
return Status::IOError("Mock file not found"); |
||||||
|
} |
||||||
|
|
||||||
|
table_reader->reset(new MockTableReader(it->second)); |
||||||
|
|
||||||
|
return Status::OK(); |
||||||
|
} |
||||||
|
|
||||||
|
TableBuilder* MockTableFactory::NewTableBuilder( |
||||||
|
const ImmutableCFOptions& ioptions, |
||||||
|
const InternalKeyComparator& internal_key, WritableFile* file, |
||||||
|
const CompressionType compression_type, |
||||||
|
const CompressionOptions& compression_opts) const { |
||||||
|
uint32_t id = GetAndWriteNextID(file); |
||||||
|
|
||||||
|
return new MockTableBuilder(id, &file_system_); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t MockTableFactory::GetAndWriteNextID(WritableFile* file) const { |
||||||
|
uint32_t next_id = next_id_.fetch_add(1); |
||||||
|
char buf[4]; |
||||||
|
EncodeFixed32(buf, next_id); |
||||||
|
file->Append(Slice(buf, 4)); |
||||||
|
return next_id; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t MockTableFactory::GetIDFromFile(RandomAccessFile* file) const { |
||||||
|
char buf[4]; |
||||||
|
Slice result; |
||||||
|
file->Read(0, 4, &result, buf); |
||||||
|
assert(result.size() == 4); |
||||||
|
return DecodeFixed32(buf); |
||||||
|
} |
||||||
|
|
||||||
|
void MockTableFactory::AssertSingleFile( |
||||||
|
const std::map<std::string, std::string>& file_contents) { |
||||||
|
ASSERT_EQ(file_system_.files.size(), 1U); |
||||||
|
ASSERT_TRUE(file_contents == file_system_.files.begin()->second); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace rocksdb
|
@ -0,0 +1,171 @@ |
|||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||||
|
// Copyright (c) 2013, Facebook, Inc. All rights reserved.
|
||||||
|
// This source code is licensed under the BSD-style license found in the
|
||||||
|
// LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
// of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
#pragma once |
||||||
|
#include <algorithm> |
||||||
|
#include <set> |
||||||
|
#include <memory> |
||||||
|
#include <map> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "rocksdb/table.h" |
||||||
|
#include "table/table_reader.h" |
||||||
|
#include "table/table_builder.h" |
||||||
|
#include "port/port.h" |
||||||
|
#include "util/mutexlock.h" |
||||||
|
#include "util/testharness.h" |
||||||
|
#include "util/testutil.h" |
||||||
|
|
||||||
|
namespace rocksdb { |
||||||
|
|
||||||
|
// NOTE this currently only supports bitwise comparator
|
||||||
|
|
||||||
|
struct MockTableFileSystem { |
||||||
|
port::Mutex mutex; |
||||||
|
std::map<uint32_t, std::map<std::string, std::string>> files; |
||||||
|
}; |
||||||
|
|
||||||
|
class MockTableReader : public TableReader { |
||||||
|
public: |
||||||
|
MockTableReader(const std::map<std::string, std::string>& table) |
||||||
|
: table_(table) {} |
||||||
|
|
||||||
|
Iterator* NewIterator(const ReadOptions&, Arena* arena) override; |
||||||
|
|
||||||
|
Status Get(const ReadOptions&, const Slice& key, |
||||||
|
GetContext* get_context) override; |
||||||
|
|
||||||
|
uint64_t ApproximateOffsetOf(const Slice& key) override { return 0; } |
||||||
|
|
||||||
|
virtual size_t ApproximateMemoryUsage() const override { return 0; } |
||||||
|
|
||||||
|
void SetupForCompaction() override {} |
||||||
|
|
||||||
|
std::shared_ptr<const TableProperties> GetTableProperties() const override; |
||||||
|
|
||||||
|
~MockTableReader() {} |
||||||
|
|
||||||
|
private: |
||||||
|
const std::map<std::string, std::string>& table_; |
||||||
|
}; |
||||||
|
|
||||||
|
class MockTableIterator : public Iterator { |
||||||
|
public: |
||||||
|
explicit MockTableIterator(const std::map<std::string, std::string>& table) |
||||||
|
: table_(table) { |
||||||
|
itr_ = table_.end(); |
||||||
|
} |
||||||
|
|
||||||
|
bool Valid() const { return itr_ == table_.end(); } |
||||||
|
|
||||||
|
void SeekToFirst() { itr_ = table_.begin(); } |
||||||
|
|
||||||
|
void SeekToLast() { |
||||||
|
itr_ = table_.end(); |
||||||
|
--itr_; |
||||||
|
} |
||||||
|
|
||||||
|
void Seek(const Slice& target) { |
||||||
|
std::string str_target(target.data(), target.size()); |
||||||
|
itr_ = table_.lower_bound(str_target); |
||||||
|
} |
||||||
|
|
||||||
|
void Next() { ++itr_; } |
||||||
|
|
||||||
|
void Prev() { |
||||||
|
if (itr_ == table_.begin()) { |
||||||
|
itr_ = table_.end(); |
||||||
|
} else { |
||||||
|
--itr_; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Slice key() const { return Slice(itr_->first); } |
||||||
|
|
||||||
|
Slice value() const { return Slice(itr_->second); } |
||||||
|
|
||||||
|
Status status() const { return Status::OK(); } |
||||||
|
|
||||||
|
private: |
||||||
|
const std::map<std::string, std::string>& table_; |
||||||
|
std::map<std::string, std::string>::const_iterator itr_; |
||||||
|
}; |
||||||
|
|
||||||
|
class MockTableBuilder : public TableBuilder { |
||||||
|
public: |
||||||
|
MockTableBuilder(uint32_t id, MockTableFileSystem* file_system) |
||||||
|
: id_(id), file_system_(file_system) {} |
||||||
|
|
||||||
|
// REQUIRES: Either Finish() or Abandon() has been called.
|
||||||
|
~MockTableBuilder() {} |
||||||
|
|
||||||
|
// Add key,value to the table being constructed.
|
||||||
|
// REQUIRES: key is after any previously added key according to comparator.
|
||||||
|
// REQUIRES: Finish(), Abandon() have not been called
|
||||||
|
void Add(const Slice& key, const Slice& value) override { |
||||||
|
table_.insert({key.ToString(), value.ToString()}); |
||||||
|
} |
||||||
|
|
||||||
|
// Return non-ok iff some error has been detected.
|
||||||
|
Status status() const override { return Status::OK(); } |
||||||
|
|
||||||
|
Status Finish() override { |
||||||
|
MutexLock lock_guard(&file_system_->mutex); |
||||||
|
file_system_->files.insert({id_, table_}); |
||||||
|
return Status::OK(); |
||||||
|
} |
||||||
|
|
||||||
|
void Abandon() override {} |
||||||
|
|
||||||
|
uint64_t NumEntries() const override { return table_.size(); } |
||||||
|
|
||||||
|
uint64_t FileSize() const override { return table_.size(); } |
||||||
|
|
||||||
|
private: |
||||||
|
uint32_t id_; |
||||||
|
MockTableFileSystem* file_system_; |
||||||
|
std::map<std::string, std::string> table_; |
||||||
|
}; |
||||||
|
|
||||||
|
class MockTableFactory : public TableFactory { |
||||||
|
public: |
||||||
|
MockTableFactory(); |
||||||
|
const char* Name() const override { return "MockTable"; } |
||||||
|
Status NewTableReader(const ImmutableCFOptions& ioptions, |
||||||
|
const EnvOptions& env_options, |
||||||
|
const InternalKeyComparator& internal_key, |
||||||
|
unique_ptr<RandomAccessFile>&& file, uint64_t file_size, |
||||||
|
unique_ptr<TableReader>* table_reader) const; |
||||||
|
|
||||||
|
TableBuilder* NewTableBuilder( |
||||||
|
const ImmutableCFOptions& ioptions, |
||||||
|
const InternalKeyComparator& internal_key, WritableFile* file, |
||||||
|
const CompressionType compression_type, |
||||||
|
const CompressionOptions& compression_opts) const; |
||||||
|
|
||||||
|
virtual Status SanitizeOptions(const DBOptions& db_opts, |
||||||
|
const ColumnFamilyOptions& cf_opts) const { |
||||||
|
return Status::OK(); |
||||||
|
} |
||||||
|
|
||||||
|
virtual std::string GetPrintableTableOptions() const override { |
||||||
|
return std::string(); |
||||||
|
} |
||||||
|
|
||||||
|
// This function will assert that only a single file exists and that the
|
||||||
|
// contents are equal to file_contents
|
||||||
|
void AssertSingleFile( |
||||||
|
const std::map<std::string, std::string>& file_contents); |
||||||
|
|
||||||
|
private: |
||||||
|
uint32_t GetAndWriteNextID(WritableFile* file) const; |
||||||
|
uint32_t GetIDFromFile(RandomAccessFile* file) const; |
||||||
|
|
||||||
|
mutable MockTableFileSystem file_system_; |
||||||
|
mutable std::atomic<uint32_t> next_id_; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace rocksdb
|
Loading…
Reference in new issue