## Public API change
* Added APIs to decode and replay trace file via Replayer class. Added `DB::NewDefaultReplayer()` to create a default Replayer instance. Added `TraceReader::Reset()` to restart reading a trace file. Created trace_record.h, trace_record_result.h and utilities/replayer.h files to access the decoded Trace records, replay them, and query the actual operation results.
+* Added Configurable::GetOptionsMap to the public API for use in creating new Customizable classes.
### Performance Improvements
* Try to avoid updating DBOptions if `SetDBOptions()` does not change any option value.
// changed.
virtual bool IsPrepared() const { return is_prepared_; }
+ // Splits the input opt_value into the ID field and the remaining options.
+ // The input opt_value can be in the form of "name" or "name=value
+ // [;name=value]". The first form uses the "name" as an id with no options The
+ // latter form converts the input into a map of name=value pairs and sets "id"
+ // to the "id" value from the map.
+ // @param opt_value The value to split into id and options
+ // @param id The id field from the opt_value
+ // @param options The remaining name/value pairs from the opt_value
+ // @param default_id If specified and there is no id field in the map, this
+ // value is returned as the ID
+ // @return OK if the value was converted to a map successfully and an ID was
+ // found.
+ // @return InvalidArgument if the value could not be converted to a map or
+ // there was or there is no id property in the map.
+ static Status GetOptionsMap(
+ const std::string& opt_value, const std::string& default_id,
+ std::string* id, std::unordered_map<std::string, std::string>* options);
+
protected:
// True once the object is prepared. Once the object is prepared, only
// mutable options can be configured.
const ConfigOptions& config_options, const std::string& id,
const std::unordered_map<std::string, std::string>& opt_map,
std::shared_ptr<T>* result) {
- Status status;
if (!id.empty()) {
+ Status status;
#ifndef ROCKSDB_LITE
status = config_options.registry->NewSharedObject(id, result);
#else
status = Status::NotSupported("Cannot load object in LITE mode ", id);
#endif // ROCKSDB_LITE
if (config_options.ignore_unsupported_options && status.IsNotSupported()) {
- return Status::OK();
+ status = Status::OK();
+ } else if (status.ok()) {
+ status = Customizable::ConfigureNewObject(config_options, result->get(),
+ opt_map);
}
- } else {
- status = Status::NotSupported("Cannot reset object ");
- }
- if (!status.ok()) {
return status;
+ } else if (opt_map.empty()) {
+ // There was no ID and no map (everything empty), so reset/clear the result
+ result->reset();
+ return Status::OK();
} else {
- return Customizable::ConfigureNewObject(config_options, result->get(),
- opt_map);
+ return Status::NotSupported("Cannot reset object ");
}
}
return status;
} else if (func == nullptr ||
!func(id, result)) { // No factory, or it failed
- if (value.empty()) { // No Id and no options. Clear the object
- *result = nullptr;
- return Status::OK();
- } else {
- return NewSharedObject(config_options, id, opt_map, result);
- }
+ return NewSharedObject(config_options, id, opt_map, result);
} else {
return Customizable::ConfigureNewObject(config_options, result->get(),
opt_map);
const ConfigOptions& config_options, const std::string& id,
const std::unordered_map<std::string, std::string>& opt_map,
std::unique_ptr<T>* result) {
- Status status;
- if (id.empty()) {
- status = Status::NotSupported("Cannot reset object ");
- } else {
+ if (!id.empty()) {
+ Status status;
#ifndef ROCKSDB_LITE
status = config_options.registry->NewUniqueObject(id, result);
#else
status = Status::NotSupported("Cannot load object in LITE mode ", id);
#endif // ROCKSDB_LITE
if (config_options.ignore_unsupported_options && status.IsNotSupported()) {
- return Status::OK();
+ status = Status::OK();
+ } else if (status.ok()) {
+ status = Customizable::ConfigureNewObject(config_options, result->get(),
+ opt_map);
}
- }
- if (!status.ok()) {
return status;
+ } else if (opt_map.empty()) {
+ // There was no ID and no map (everything empty), so reset/clear the result
+ result->reset();
+ return Status::OK();
} else {
- return Customizable::ConfigureNewObject(config_options, result->get(),
- opt_map);
+ return Status::NotSupported("Cannot reset object ");
}
}
return status;
} else if (func == nullptr ||
!func(id, result)) { // No factory, or it failed
- if (value.empty()) { // No Id and no options. Clear the object
- *result = nullptr;
- return Status::OK();
- } else {
- return NewUniqueObject(config_options, id, opt_map, result);
- }
+ return NewUniqueObject(config_options, id, opt_map, result);
} else {
return Customizable::ConfigureNewObject(config_options, result->get(),
opt_map);
static Status NewStaticObject(
const ConfigOptions& config_options, const std::string& id,
const std::unordered_map<std::string, std::string>& opt_map, T** result) {
- Status status;
- if (id.empty()) {
- status = Status::NotSupported("Cannot reset object ");
- } else {
+ if (!id.empty()) {
+ Status status;
#ifndef ROCKSDB_LITE
status = config_options.registry->NewStaticObject(id, result);
#else
status = Status::NotSupported("Cannot load object in LITE mode ", id);
#endif // ROCKSDB_LITE
if (config_options.ignore_unsupported_options && status.IsNotSupported()) {
- return Status::OK();
+ status = Status::OK();
+ } else if (status.ok()) {
+ status =
+ Customizable::ConfigureNewObject(config_options, *result, opt_map);
}
- }
- if (!status.ok()) {
return status;
+ } else if (opt_map.empty()) {
+ // There was no ID and no map (everything empty), so reset/clear the result
+ *result = nullptr;
+ return Status::OK();
} else {
- return Customizable::ConfigureNewObject(config_options, *result, opt_map);
+ return Status::NotSupported("Cannot reset object ");
}
}
return status;
} else if (func == nullptr ||
!func(id, result)) { // No factory, or it failed
- if (value.empty()) { // No Id and no options. Clear the object
- *result = nullptr;
- return Status::OK();
- } else {
- return NewStaticObject(config_options, id, opt_map, result);
- }
+ return NewStaticObject(config_options, id, opt_map, result);
} else {
return Customizable::ConfigureNewObject(config_options, *result, opt_map);
}
return OptionTypeInfo(
offset, OptionType::kCustomizable, ovt,
flags | OptionTypeFlags::kShared,
- [](const ConfigOptions& opts, const std::string&,
+ [](const ConfigOptions& opts, const std::string& name,
const std::string& value, void* addr) {
auto* shared = static_cast<std::shared_ptr<T>*>(addr);
- return T::CreateFromString(opts, value, shared);
+ if (name == kIdPropName() && value.empty()) {
+ shared->reset();
+ return Status::OK();
+ } else {
+ return T::CreateFromString(opts, value, shared);
+ }
},
serialize_func, equals_func);
}
return OptionTypeInfo(
offset, OptionType::kCustomizable, ovt,
flags | OptionTypeFlags::kUnique,
- [](const ConfigOptions& opts, const std::string&,
+ [](const ConfigOptions& opts, const std::string& name,
const std::string& value, void* addr) {
auto* unique = static_cast<std::unique_ptr<T>*>(addr);
- return T::CreateFromString(opts, value, unique);
+ if (name == kIdPropName() && value.empty()) {
+ unique->reset();
+ return Status::OK();
+ } else {
+ return T::CreateFromString(opts, value, unique);
+ }
},
serialize_func, equals_func);
}
return OptionTypeInfo(
offset, OptionType::kCustomizable, ovt,
flags | OptionTypeFlags::kRawPointer,
- [](const ConfigOptions& opts, const std::string&,
+ [](const ConfigOptions& opts, const std::string& name,
const std::string& value, void* addr) {
auto** pointer = static_cast<T**>(addr);
- return T::CreateFromString(opts, value, pointer);
+ if (name == kIdPropName() && value.empty()) {
+ *pointer = nullptr;
+ return Status::OK();
+ } else {
+ return T::CreateFromString(opts, value, pointer);
+ }
},
serialize_func, equals_func);
}
static Status NextToken(const std::string& opts, char delimiter, size_t start,
size_t* end, std::string* token);
+ constexpr static const char* kIdPropName() { return "id"; }
+ constexpr static const char* kIdPropSuffix() { return ".id"; }
+
private:
int offset_;
std::string result;
ConfigOptions embedded = config_options;
embedded.delimiter = ";";
- for (size_t i = 0; i < vec.size(); ++i) {
+ int printed = 0;
+ for (const auto& elem : vec) {
std::string elem_str;
- Status s = elem_info.Serialize(
- embedded, name, reinterpret_cast<const char*>(&vec[i]), &elem_str);
+ Status s = elem_info.Serialize(embedded, name, &elem, &elem_str);
if (!s.ok()) {
return s;
- } else {
- if (i > 0) {
+ } else if (!elem_str.empty()) {
+ if (printed++ > 0) {
result += separator;
}
// If the element contains embedded separators, put it inside of brackets
- if (result.find(separator) != std::string::npos) {
+ if (elem_str.find(separator) != std::string::npos) {
result += "{" + elem_str + "}";
} else {
result += elem_str;
}
if (result.find("=") != std::string::npos) {
*value = "{" + result + "}";
+ } else if (printed > 1 && result.at(0) == '{') {
+ *value = "{" + result + "}";
} else {
*value = result;
}
if (opt_info.IsMutable() || !config_options.mutable_options_only) {
// Either the option is mutable, or we are processing all of the options
- if (opt_name == name || name == ConfigurableHelper::kIdPropName ||
- EndsWith(opt_name, ConfigurableHelper::kIdPropSuffix)) {
+ if (opt_name == name || name == OptionTypeInfo::kIdPropName() ||
+ EndsWith(opt_name, OptionTypeInfo::kIdPropSuffix())) {
return configurable.ParseOption(copy, opt_info, name, value, opt_ptr);
} else if (value.empty()) {
return Status::OK();
} else {
return Status::InvalidArgument("Option not changeable: " + opt_name);
}
- } else if (EndsWith(opt_name, ConfigurableHelper::kIdPropSuffix) ||
- name == ConfigurableHelper::kIdPropName) {
+ } else if (EndsWith(opt_name, OptionTypeInfo::kIdPropSuffix()) ||
+ name == OptionTypeInfo::kIdPropName()) {
// We have a property of the form "id=value" or "table.id=value"
// This is OK if we ID/value matches the current customizable object
if (custom->GetId() == value) {
// map
std::unordered_map<std::string, std::string> props;
std::string id;
- Status s = GetOptionsMap(value, custom->GetId(), &id, &props);
+ Status s =
+ Configurable::GetOptionsMap(value, custom->GetId(), &id, &props);
if (!s.ok()) {
return s;
} else if (custom->GetId() != id) {
}
#endif // ROCKSDB_LITE
-Status ConfigurableHelper::GetOptionsMap(
+Status Configurable::GetOptionsMap(
const std::string& value, const std::string& default_id, std::string* id,
std::unordered_map<std::string, std::string>* props) {
assert(id);
props->clear(); // Clear the properties
status = Status::OK(); // and ignore the error
} else {
- auto iter = props->find(ConfigurableHelper::kIdPropName);
+ auto iter = props->find(OptionTypeInfo::kIdPropName());
if (iter != props->end()) {
*id = iter->second;
props->erase(iter);
+ if (*id == kNullptrString) {
+ id->clear();
+ }
} else if (!default_id.empty()) {
*id = default_id;
} else { // No id property and no default
// of configuring the objects.
class ConfigurableHelper {
public:
- constexpr static const char* kIdPropName = "id";
- constexpr static const char* kIdPropSuffix = ".id";
// Configures the input Configurable object based on the parameters.
// On successful completion, the Configurable is updated with the settings
// from the opt_map.
const std::unordered_map<std::string, std::string>& options,
std::unordered_map<std::string, std::string>* unused);
- // Splits the input opt_value into the ID field and the remaining options.
- // The input opt_value can be in the form of "name" or "name=value
- // [;name=value]". The first form uses the "name" as an id with no options The
- // latter form converts the input into a map of name=value pairs and sets "id"
- // to the "id" value from the map.
- // @param opt_value The value to split into id and options
- // @param id The id field from the opt_value
- // @param options The remaining name/value pairs from the opt_value
- // @param default_id If specified and there is no id field in the map, this
- // value is returned as the ID
- // @return OK if the value was converted to a map succesfully and an ID was
- // found.
- // @return InvalidArgument if the value could not be converted to a map or
- // there was or there is no id property in the map.
- static Status GetOptionsMap(
- const std::string& opt_value, const std::string& default_id,
- std::string* id, std::unordered_map<std::string, std::string>* options);
-
#ifndef ROCKSDB_LITE
// Internal method to configure a set of options for this object.
// Classes may override this value to change its behavior.
#include "rocksdb/customizable.h"
-#include "options/configurable_helper.h"
#include "options/options_helper.h"
#include "rocksdb/convenience.h"
#include "rocksdb/status.h"
+#include "rocksdb/utilities/options_type.h"
#include "util/string_util.h"
namespace ROCKSDB_NAMESPACE {
Status Customizable::GetOption(const ConfigOptions& config_options,
const std::string& opt_name,
std::string* value) const {
- if (opt_name == ConfigurableHelper::kIdPropName) {
+ if (opt_name == OptionTypeInfo::kIdPropName()) {
*value = GetId();
return Status::OK();
} else {
result = id;
} else {
result.append(prefix);
- result.append(ConfigurableHelper::kIdPropName).append("=");
- result.append(id).append(config_options.delimiter);
+ result.append(OptionTypeInfo::kIdPropName());
+ result.append("=");
+ result.append(id);
+ result.append(config_options.delimiter);
result.append(parent);
}
return result;
this != other) {
const Customizable* custom = reinterpret_cast<const Customizable*>(other);
if (GetId() != custom->GetId()) {
- *mismatch = ConfigurableHelper::kIdPropName;
+ *mismatch = OptionTypeInfo::kIdPropName();
return false;
} else if (config_options.sanity_level >
ConfigOptions::kSanityLevelLooselyCompatible) {
const ConfigOptions& config_options, const Customizable* customizable,
const std::string& value, std::string* id,
std::unordered_map<std::string, std::string>* props) {
- if (customizable != nullptr) {
- Status status = ConfigurableHelper::GetOptionsMap(
- value, customizable->GetId(), id, props);
+ Status status;
+ if (value.empty() || value == kNullptrString) {
+ *id = "";
+ props->clear();
+ } else if (customizable != nullptr) {
+ status =
+ Configurable::GetOptionsMap(value, customizable->GetId(), id, props);
#ifdef ROCKSDB_LITE
(void)config_options;
#else
}
}
#endif // ROCKSDB_LITE
- return status;
} else {
- return ConfigurableHelper::GetOptionsMap(value, "", id, props);
+ status = Configurable::GetOptionsMap(value, "", id, props);
}
+ return status;
}
Status Customizable::ConfigureNewObject(
#endif // GFLAGS
namespace ROCKSDB_NAMESPACE {
+namespace {
class StringLogger : public Logger {
public:
using Logger::Logv;
};
struct AOptions {
+ static const char* kName() { return "A"; }
int i = 0;
bool b = false;
};
OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
#endif // ROCKSDB_LITE
};
+
class ACustomizable : public TestCustomizable {
public:
explicit ACustomizable(const std::string& id)
: TestCustomizable("A"), id_(id) {
- RegisterOptions("A", &opts_, &a_option_info);
+ RegisterOptions(&opts_, &a_option_info);
}
std::string GetId() const override { return id_; }
static const char* kClassName() { return "A"; }
const std::string id_;
};
-#ifndef ROCKSDB_LITE
-static int A_count = 0;
-const FactoryFunc<TestCustomizable>& a_func =
- ObjectLibrary::Default()->Register<TestCustomizable>(
- "A.*",
- [](const std::string& name, std::unique_ptr<TestCustomizable>* guard,
- std::string* /* msg */) {
- guard->reset(new ACustomizable(name));
- A_count++;
- return guard->get();
- });
-#endif // ROCKSDB_LITE
-
struct BOptions {
std::string s;
bool b = false;
return false;
}
}
-Status TestCustomizable::CreateFromString(
- const ConfigOptions& config_options, const std::string& value,
- std::shared_ptr<TestCustomizable>* result) {
- return LoadSharedObject<TestCustomizable>(config_options, value, LoadSharedB,
- result);
-}
-Status TestCustomizable::CreateFromString(
- const ConfigOptions& config_options, const std::string& value,
- std::unique_ptr<TestCustomizable>* result) {
- return LoadUniqueObject<TestCustomizable>(
- config_options, value,
- [](const std::string& id, std::unique_ptr<TestCustomizable>* u) {
- if (id == "B") {
- u->reset(new BCustomizable(id));
- return true;
- } else if (id.empty()) {
- u->reset();
- return true;
- } else {
- return false;
- }
- },
- result);
-}
+#ifndef ROCKSDB_LITE
+static int A_count = 0;
+static int RegisterCustomTestObjects(ObjectLibrary& library,
+ const std::string& /*arg*/) {
+ library.Register<TestCustomizable>(
+ "A.*",
+ [](const std::string& name, std::unique_ptr<TestCustomizable>* guard,
+ std::string* /* msg */) {
+ guard->reset(new ACustomizable(name));
+ A_count++;
+ return guard->get();
+ });
-Status TestCustomizable::CreateFromString(const ConfigOptions& config_options,
- const std::string& value,
- TestCustomizable** result) {
- return LoadStaticObject<TestCustomizable>(
- config_options, value,
- [](const std::string& id, TestCustomizable** ptr) {
- if (id == "B") {
- *ptr = new BCustomizable(id);
- return true;
- } else if (id.empty()) {
- *ptr = nullptr;
- return true;
- } else {
- return false;
- }
- },
- result);
+ library.Register<TestCustomizable>(
+ "S", [](const std::string& name,
+ std::unique_ptr<TestCustomizable>* /* guard */,
+ std::string* /* msg */) { return new BCustomizable(name); });
+ size_t num_types;
+ return static_cast<int>(library.GetFactoryCount(&num_types));
}
-
-#ifndef ROCKSDB_LITE
-const FactoryFunc<TestCustomizable>& s_func =
- ObjectLibrary::Default()->Register<TestCustomizable>(
- "S", [](const std::string& name,
- std::unique_ptr<TestCustomizable>* /* guard */,
- std::string* /* msg */) { return new BCustomizable(name); });
#endif // ROCKSDB_LITE
struct SimpleOptions {
}
};
+#ifndef ROCKSDB_LITE
+static void GetMapFromProperties(
+ const std::string& props,
+ std::unordered_map<std::string, std::string>* map) {
+ std::istringstream iss(props);
+ std::unordered_map<std::string, std::string> copy_map;
+ std::string line;
+ map->clear();
+ for (int line_num = 0; std::getline(iss, line); line_num++) {
+ std::string name;
+ std::string value;
+ ASSERT_OK(
+ RocksDBOptionsParser::ParseStatement(&name, &value, line, line_num));
+ (*map)[name] = value;
+ }
+}
+#endif // ROCKSDB_LITE
+} // namespace
+
+Status TestCustomizable::CreateFromString(
+ const ConfigOptions& config_options, const std::string& value,
+ std::shared_ptr<TestCustomizable>* result) {
+ return LoadSharedObject<TestCustomizable>(config_options, value, LoadSharedB,
+ result);
+}
+
+Status TestCustomizable::CreateFromString(
+ const ConfigOptions& config_options, const std::string& value,
+ std::unique_ptr<TestCustomizable>* result) {
+ return LoadUniqueObject<TestCustomizable>(
+ config_options, value,
+ [](const std::string& id, std::unique_ptr<TestCustomizable>* u) {
+ if (id == "B") {
+ u->reset(new BCustomizable(id));
+ return true;
+ } else if (id.empty()) {
+ u->reset();
+ return true;
+ } else {
+ return false;
+ }
+ },
+ result);
+}
+
+Status TestCustomizable::CreateFromString(const ConfigOptions& config_options,
+ const std::string& value,
+ TestCustomizable** result) {
+ return LoadStaticObject<TestCustomizable>(
+ config_options, value,
+ [](const std::string& id, TestCustomizable** ptr) {
+ if (id == "B") {
+ *ptr = new BCustomizable(id);
+ return true;
+ } else if (id.empty()) {
+ *ptr = nullptr;
+ return true;
+ } else {
+ return false;
+ }
+ },
+ result);
+}
+
class CustomizableTest : public testing::Test {
public:
- CustomizableTest() { config_options_.invoke_prepare_options = false; }
+ CustomizableTest() {
+ config_options_.invoke_prepare_options = false;
+#ifndef ROCKSDB_LITE
+ // GetOptionsFromMap is not supported in ROCKSDB_LITE
+ config_options_.registry->AddLibrary("CustomizableTest",
+ RegisterCustomTestObjects, "");
+#endif // ROCKSDB_LITE
+ }
ConfigOptions config_options_;
};
configurable->AreEquivalent(config_options_, copy.get(), &mismatch));
}
-static void GetMapFromProperties(
- const std::string& props,
- std::unordered_map<std::string, std::string>* map) {
- std::istringstream iss(props);
- std::unordered_map<std::string, std::string> copy_map;
- std::string line;
- map->clear();
- for (int line_num = 0; std::getline(iss, line); line_num++) {
- std::string name;
- std::string value;
- ASSERT_OK(
- RocksDBOptionsParser::ParseStatement(&name, &value, line, line_num));
- (*map)[name] = value;
- }
-}
-
TEST_F(CustomizableTest, ConfigureFromPropsTest) {
std::unordered_map<std::string, std::string> opt_map = {
{"unique.id", "A"}, {"unique.A.int", "1"}, {"unique.A.bool", "true"},
// Tests that we can initialize a customizable from its options
TEST_F(CustomizableTest, ConfigureStandaloneCustomTest) {
std::unique_ptr<TestCustomizable> base, copy;
- auto registry = ObjectRegistry::NewInstance();
+ const auto& registry = config_options_.registry;
ASSERT_OK(registry->NewUniqueObject<TestCustomizable>("A", &base));
ASSERT_OK(registry->NewUniqueObject<TestCustomizable>("A", ©));
ASSERT_OK(base->ConfigureFromString(config_options_, "int=33;bool=true"));
}
TEST_F(CustomizableTest, IsInstanceOfTest) {
- std::shared_ptr<TestCustomizable> tc = std::make_shared<ACustomizable>("A");
+ std::shared_ptr<TestCustomizable> tc = std::make_shared<ACustomizable>("A_1");
+ ASSERT_EQ(tc->GetId(), std::string("A_1"));
ASSERT_TRUE(tc->IsInstanceOf("A"));
ASSERT_TRUE(tc->IsInstanceOf("TestCustomizable"));
ASSERT_FALSE(tc->IsInstanceOf("B"));
+ ASSERT_FALSE(tc->IsInstanceOf("A_1"));
ASSERT_EQ(tc->CheckedCast<ACustomizable>(), tc.get());
ASSERT_EQ(tc->CheckedCast<TestCustomizable>(), tc.get());
ASSERT_EQ(tc->CheckedCast<BCustomizable>(), nullptr);
ASSERT_TRUE(simple->cp->IsPrepared());
delete simple->cp;
base.reset(new SimpleConfigurable());
+ simple = base->GetOptions<SimpleOptions>();
+ ASSERT_NE(simple, nullptr);
ASSERT_NOK(
base->ConfigureFromString(prepared, "unique={id=P; can_prepare=false}"));
- simple = base->GetOptions<SimpleOptions>();
- ASSERT_NE(simple, nullptr);
- ASSERT_NE(simple->cu, nullptr);
- ASSERT_FALSE(simple->cu->IsPrepared());
+ ASSERT_EQ(simple->cu, nullptr);
ASSERT_OK(
base->ConfigureFromString(prepared, "unique={id=P; can_prepare=true}"));
+ ASSERT_NE(simple->cu, nullptr);
ASSERT_TRUE(simple->cu->IsPrepared());
ASSERT_OK(base->ConfigureFromString(config_options_,
ASSERT_FALSE(simple->cu->IsPrepared());
}
+namespace {
static std::unordered_map<std::string, OptionTypeInfo> inner_option_info = {
#ifndef ROCKSDB_LITE
{"inner",
const char* Name() const override { return kClassName(); }
static const char* kClassName() { return "Wrapped2"; }
};
+} // namespace
TEST_F(CustomizableTest, WrappedInnerTest) {
std::shared_ptr<TestCustomizable> ac =
ASSERT_EQ(wc2->CheckedCast<TestCustomizable>(), ac.get());
}
-class ShallowCustomizable : public Customizable {
- public:
- ShallowCustomizable() {
- inner_ = std::make_shared<ACustomizable>("a");
- RegisterOptions("inner", &inner_, &inner_option_info);
- };
- static const char* kClassName() { return "shallow"; }
- const char* Name() const override { return kClassName(); }
-
- private:
- std::shared_ptr<TestCustomizable> inner_;
-};
-
TEST_F(CustomizableTest, TestStringDepth) {
+ class ShallowCustomizable : public Customizable {
+ public:
+ ShallowCustomizable() {
+ inner_ = std::make_shared<ACustomizable>("a");
+ RegisterOptions("inner", &inner_, &inner_option_info);
+ }
+ static const char* kClassName() { return "shallow"; }
+ const char* Name() const override { return kClassName(); }
+
+ private:
+ std::shared_ptr<TestCustomizable> inner_;
+ };
ConfigOptions shallow = config_options_;
std::unique_ptr<Configurable> c(new ShallowCustomizable());
std::string opt_str;
}
// Tests that we only get a new customizable when it changes
-TEST_F(CustomizableTest, NewCustomizableTest) {
+TEST_F(CustomizableTest, NewUniqueCustomizableTest) {
std::unique_ptr<Configurable> base(new SimpleConfigurable());
A_count = 0;
ASSERT_OK(base->ConfigureFromString(config_options_,
ASSERT_EQ(A_count, 3); // Created another A
ASSERT_OK(base->ConfigureFromString(config_options_, "unique.id="));
ASSERT_EQ(simple->cu, nullptr);
+ ASSERT_OK(base->ConfigureFromString(config_options_, "unique=nullptr"));
+ ASSERT_EQ(simple->cu, nullptr);
+ ASSERT_OK(base->ConfigureFromString(config_options_, "unique.id=nullptr"));
+ ASSERT_EQ(simple->cu, nullptr);
ASSERT_EQ(A_count, 3);
}
+TEST_F(CustomizableTest, NewEmptyUniqueTest) {
+ std::unique_ptr<Configurable> base(new SimpleConfigurable());
+ SimpleOptions* simple = base->GetOptions<SimpleOptions>();
+ ASSERT_EQ(simple->cu, nullptr);
+ simple->cu.reset(new BCustomizable("B"));
+
+ ASSERT_OK(base->ConfigureFromString(config_options_, "unique={id=}"));
+ ASSERT_EQ(simple->cu, nullptr);
+ simple->cu.reset(new BCustomizable("B"));
+
+ ASSERT_OK(base->ConfigureFromString(config_options_, "unique={id=nullptr}"));
+ ASSERT_EQ(simple->cu, nullptr);
+ simple->cu.reset(new BCustomizable("B"));
+
+ ASSERT_OK(base->ConfigureFromString(config_options_, "unique.id="));
+ ASSERT_EQ(simple->cu, nullptr);
+ simple->cu.reset(new BCustomizable("B"));
+
+ ASSERT_OK(base->ConfigureFromString(config_options_, "unique=nullptr"));
+ ASSERT_EQ(simple->cu, nullptr);
+ simple->cu.reset(new BCustomizable("B"));
+
+ ASSERT_OK(base->ConfigureFromString(config_options_, "unique.id=nullptr"));
+ ASSERT_EQ(simple->cu, nullptr);
+}
+
+TEST_F(CustomizableTest, NewEmptySharedTest) {
+ std::unique_ptr<Configurable> base(new SimpleConfigurable());
+
+ SimpleOptions* simple = base->GetOptions<SimpleOptions>();
+ ASSERT_NE(simple, nullptr);
+ ASSERT_EQ(simple->cs, nullptr);
+ simple->cs.reset(new BCustomizable("B"));
+
+ ASSERT_OK(base->ConfigureFromString(config_options_, "shared={id=}"));
+ ASSERT_NE(simple, nullptr);
+ ASSERT_EQ(simple->cs, nullptr);
+ simple->cs.reset(new BCustomizable("B"));
+
+ ASSERT_OK(base->ConfigureFromString(config_options_, "shared={id=nullptr}"));
+ ASSERT_EQ(simple->cs, nullptr);
+ simple->cs.reset(new BCustomizable("B"));
+
+ ASSERT_OK(base->ConfigureFromString(config_options_, "shared.id="));
+ ASSERT_EQ(simple->cs, nullptr);
+ simple->cs.reset(new BCustomizable("B"));
+
+ ASSERT_OK(base->ConfigureFromString(config_options_, "shared.id=nullptr"));
+ ASSERT_EQ(simple->cs, nullptr);
+ simple->cs.reset(new BCustomizable("B"));
+
+ ASSERT_OK(base->ConfigureFromString(config_options_, "shared=nullptr"));
+ ASSERT_EQ(simple->cs, nullptr);
+}
+
+TEST_F(CustomizableTest, NewEmptyStaticTest) {
+ std::unique_ptr<Configurable> base(new SimpleConfigurable());
+ ASSERT_OK(base->ConfigureFromString(config_options_, "pointer={id=}"));
+ SimpleOptions* simple = base->GetOptions<SimpleOptions>();
+ ASSERT_NE(simple, nullptr);
+ ASSERT_EQ(simple->cp, nullptr);
+ ASSERT_OK(base->ConfigureFromString(config_options_, "pointer={id=nullptr}"));
+ ASSERT_EQ(simple->cp, nullptr);
+
+ ASSERT_OK(base->ConfigureFromString(config_options_, "pointer="));
+ ASSERT_EQ(simple->cp, nullptr);
+ ASSERT_OK(base->ConfigureFromString(config_options_, "pointer=nullptr"));
+ ASSERT_EQ(simple->cp, nullptr);
+
+ ASSERT_OK(base->ConfigureFromString(config_options_, "pointer.id="));
+ ASSERT_EQ(simple->cp, nullptr);
+ ASSERT_OK(base->ConfigureFromString(config_options_, "pointer.id=nullptr"));
+ ASSERT_EQ(simple->cp, nullptr);
+}
+
+namespace {
+#ifndef ROCKSDB_LITE
+static std::unordered_map<std::string, OptionTypeInfo> vector_option_info = {
+ {"vector",
+ OptionTypeInfo::Vector<std::shared_ptr<TestCustomizable>>(
+ 0, OptionVerificationType::kNormal,
+
+ OptionTypeFlags::kNone,
+
+ OptionTypeInfo::AsCustomSharedPtr<TestCustomizable>(
+ 0, OptionVerificationType::kNormal, OptionTypeFlags::kNone))},
+};
+class VectorConfigurable : public SimpleConfigurable {
+ public:
+ VectorConfigurable() { RegisterOptions("vector", &cv, &vector_option_info); }
+ std::vector<std::shared_ptr<TestCustomizable>> cv;
+};
+} // namespace
+
+TEST_F(CustomizableTest, VectorConfigTest) {
+ VectorConfigurable orig, copy;
+ std::shared_ptr<TestCustomizable> c1, c2;
+ ASSERT_OK(TestCustomizable::CreateFromString(config_options_, "A", &c1));
+ ASSERT_OK(TestCustomizable::CreateFromString(config_options_, "B", &c2));
+ orig.cv.push_back(c1);
+ orig.cv.push_back(c2);
+ ASSERT_OK(orig.ConfigureFromString(config_options_, "unique=A2"));
+ std::string opt_str, mismatch;
+ ASSERT_OK(orig.GetOptionString(config_options_, &opt_str));
+ ASSERT_OK(copy.ConfigureFromString(config_options_, opt_str));
+ ASSERT_TRUE(orig.AreEquivalent(config_options_, ©, &mismatch));
+}
+
+TEST_F(CustomizableTest, NoNameTest) {
+ // If Customizables are created without names, they are not
+ // part of the serialization (since they cannot be recreated)
+ VectorConfigurable orig, copy;
+ auto sopts = orig.GetOptions<SimpleOptions>();
+ auto copts = copy.GetOptions<SimpleOptions>();
+ sopts->cu.reset(new ACustomizable(""));
+ orig.cv.push_back(std::make_shared<ACustomizable>(""));
+ orig.cv.push_back(std::make_shared<ACustomizable>("A1"));
+ std::string opt_str, mismatch;
+ ASSERT_OK(orig.GetOptionString(config_options_, &opt_str));
+ ASSERT_OK(copy.ConfigureFromString(config_options_, opt_str));
+ ASSERT_EQ(copy.cv.size(), 1U);
+ ASSERT_EQ(copy.cv[0]->GetId(), "A1");
+ ASSERT_EQ(copts->cu, nullptr);
+}
+
+#endif // ROCKSDB_LITE
+
TEST_F(CustomizableTest, IgnoreUnknownObjects) {
ConfigOptions ignore = config_options_;
std::shared_ptr<TestCustomizable> shared;
}
const char* Name() const override { return "MutableCustomizable"; }
};
- MutableCustomizable mc;
+ MutableCustomizable mc, mc2;
+ std::string mismatch;
+ std::string opt_str;
ConfigOptions options = config_options_;
ASSERT_FALSE(mc.IsPrepared());
ASSERT_OK(mc.ConfigureOption(options, "mutable", "{id=B;}"));
+ options.mutable_options_only = true;
+ ASSERT_OK(mc.GetOptionString(options, &opt_str));
+ ASSERT_OK(mc2.ConfigureFromString(options, opt_str));
+ ASSERT_TRUE(mc.AreEquivalent(options, &mc2, &mismatch));
+
+ options.mutable_options_only = false;
ASSERT_OK(mc.ConfigureOption(options, "immutable", "{id=A; int=10}"));
auto* mm = mc.GetOptions<std::shared_ptr<TestCustomizable>>("mutable");
auto* im = mc.GetOptions<std::shared_ptr<TestCustomizable>>("immutable");
// Only the mutable options should get serialized
options.mutable_options_only = false;
+ ASSERT_OK(mc.GetOptionString(options, &opt_str));
ASSERT_OK(mc.ConfigureOption(options, "immutable", "{id=B;}"));
options.mutable_options_only = true;
- std::string opt_str;
ASSERT_OK(mc.GetOptionString(options, &opt_str));
- MutableCustomizable mc2;
ASSERT_OK(mc2.ConfigureFromString(options, opt_str));
- std::string mismatch;
ASSERT_TRUE(mc.AreEquivalent(options, &mc2, &mismatch));
options.mutable_options_only = false;
ASSERT_FALSE(mc.AreEquivalent(options, &mc2, &mismatch));
}
#endif // !ROCKSDB_LITE
+namespace {
class TestSecondaryCache : public SecondaryCache {
public:
static const char* kClassName() { return "Test"; }
};
#ifndef ROCKSDB_LITE
-// This method loads existing test classes into the ObjectRegistry
-static int RegisterTestObjects(ObjectLibrary& library,
- const std::string& /*arg*/) {
- size_t num_types;
- library.Register<TableFactory>(
- "MockTable",
- [](const std::string& /*uri*/, std::unique_ptr<TableFactory>* guard,
- std::string* /* errmsg */) {
- guard->reset(new mock::MockTableFactory());
- return guard->get();
- });
- library.Register<EventListener>(
- OnFileDeletionListener::kClassName(),
- [](const std::string& /*uri*/, std::unique_ptr<EventListener>* guard,
- std::string* /* errmsg */) {
- guard->reset(new OnFileDeletionListener());
- return guard->get();
- });
- library.Register<EventListener>(
- FlushCounterListener::kClassName(),
- [](const std::string& /*uri*/, std::unique_ptr<EventListener>* guard,
- std::string* /* errmsg */) {
- guard->reset(new FlushCounterListener());
- return guard->get();
- });
- library.Register<const Comparator>(
- test::SimpleSuffixReverseComparator::kClassName(),
- [](const std::string& /*uri*/,
- std::unique_ptr<const Comparator>* /*guard*/,
- std::string* /* errmsg */) {
- static test::SimpleSuffixReverseComparator ssrc;
- return &ssrc;
- });
- library.Register<MergeOperator>(
- "Changling",
- [](const std::string& uri, std::unique_ptr<MergeOperator>* guard,
- std::string* /* errmsg */) {
- guard->reset(new test::ChanglingMergeOperator(uri));
- return guard->get();
- });
- library.Register<CompactionFilter>(
- "Changling",
- [](const std::string& uri, std::unique_ptr<CompactionFilter>* /*guard*/,
- std::string* /* errmsg */) {
- return new test::ChanglingCompactionFilter(uri);
- });
- library.Register<CompactionFilterFactory>(
- "Changling", [](const std::string& uri,
- std::unique_ptr<CompactionFilterFactory>* guard,
- std::string* /* errmsg */) {
- guard->reset(new test::ChanglingCompactionFilterFactory(uri));
- return guard->get();
- });
-
- return static_cast<int>(library.GetFactoryCount(&num_types));
-}
-
class MockEncryptionProvider : public EncryptionProvider {
public:
explicit MockEncryptionProvider(const std::string& id) : id_(id) {}
Status Encrypt(char* /*data*/) override { return Status::NotSupported(); }
Status Decrypt(char* data) override { return Encrypt(data); }
};
+#endif // ROCKSDB_LITE
class TestFlushBlockPolicyFactory : public FlushBlockPolicyFactory {
public:
}
};
+#ifndef ROCKSDB_LITE
static int RegisterLocalObjects(ObjectLibrary& library,
const std::string& /*arg*/) {
size_t num_types;
+ library.Register<TableFactory>(
+ mock::MockTableFactory::kClassName(),
+ [](const std::string& /*uri*/, std::unique_ptr<TableFactory>* guard,
+ std::string* /* errmsg */) {
+ guard->reset(new mock::MockTableFactory());
+ return guard->get();
+ });
+ library.Register<EventListener>(
+ OnFileDeletionListener::kClassName(),
+ [](const std::string& /*uri*/, std::unique_ptr<EventListener>* guard,
+ std::string* /* errmsg */) {
+ guard->reset(new OnFileDeletionListener());
+ return guard->get();
+ });
+ library.Register<EventListener>(
+ FlushCounterListener::kClassName(),
+ [](const std::string& /*uri*/, std::unique_ptr<EventListener>* guard,
+ std::string* /* errmsg */) {
+ guard->reset(new FlushCounterListener());
+ return guard->get();
+ });
// Load any locally defined objects here
library.Register<EncryptionProvider>(
"Mock(://test)?",
return static_cast<int>(library.GetFactoryCount(&num_types));
}
#endif // !ROCKSDB_LITE
+} // namespace
class LoadCustomizableTest : public testing::Test {
public:
}
bool RegisterTests(const std::string& arg) {
#ifndef ROCKSDB_LITE
- config_options_.registry->AddLibrary("custom-tests", RegisterTestObjects,
- arg);
+ config_options_.registry->AddLibrary("custom-tests",
+ test::RegisterTestObjects, arg);
config_options_.registry->AddLibrary("local-tests", RegisterLocalObjects,
arg);
return true;
TEST_F(LoadCustomizableTest, LoadTableFactoryTest) {
ColumnFamilyOptions cf_opts;
std::shared_ptr<TableFactory> factory;
- ASSERT_NOK(
- TableFactory::CreateFromString(config_options_, "MockTable", &factory));
+ ASSERT_NOK(TableFactory::CreateFromString(
+ config_options_, mock::MockTableFactory::kClassName(), &factory));
ASSERT_OK(TableFactory::CreateFromString(
config_options_, TableFactory::kBlockBasedTableName(), &factory));
ASSERT_NE(factory, nullptr);
TableFactory::kBlockBasedTableName());
#endif // ROCKSDB_LITE
if (RegisterTests("Test")) {
- ASSERT_OK(
- TableFactory::CreateFromString(config_options_, "MockTable", &factory));
+ ASSERT_OK(TableFactory::CreateFromString(
+ config_options_, mock::MockTableFactory::kClassName(), &factory));
ASSERT_NE(factory, nullptr);
- ASSERT_STREQ(factory->Name(), "MockTable");
+ ASSERT_STREQ(factory->Name(), mock::MockTableFactory::kClassName());
#ifndef ROCKSDB_LITE
- ASSERT_OK(
- GetColumnFamilyOptionsFromString(config_options_, ColumnFamilyOptions(),
- opts_str + "MockTable", &cf_opts));
+ ASSERT_OK(GetColumnFamilyOptionsFromString(
+ config_options_, ColumnFamilyOptions(),
+ opts_str + mock::MockTableFactory::kClassName(), &cf_opts));
ASSERT_NE(cf_opts.table_factory.get(), nullptr);
- ASSERT_STREQ(cf_opts.table_factory->Name(), "MockTable");
+ ASSERT_STREQ(cf_opts.table_factory->Name(),
+ mock::MockTableFactory::kClassName());
#endif // ROCKSDB_LITE
}
}
} else if (IsCustomizable()) {
const Customizable* custom = AsRawPointer<Customizable>(opt_ptr);
if (custom == nullptr) {
- *opt_value = kNullptrString;
+ // We do not have a custom object to serialize.
+ // If the option is not mutable and we are doing only mutable options,
+ // we return an empty string (which will cause the option not to be
+ // printed). Otherwise, we return the "nullptr" string, which will result
+ // in "option=nullptr" being printed.
+ if (IsMutable() || !config_options.mutable_options_only) {
+ *opt_value = kNullptrString;
+ } else {
+ *opt_value = "";
+ }
} else if (IsEnabled(OptionTypeFlags::kStringNameOnly) &&
!config_options.IsDetailed()) {
*opt_value = custom->GetId();
static void TestAndCompareOption(const ConfigOptions& config_options,
const OptionTypeInfo& opt_info,
const std::string& opt_name, void* base_ptr,
- void* comp_ptr) {
+ void* comp_ptr, bool strip = false) {
std::string result, mismatch;
ASSERT_OK(opt_info.Serialize(config_options, opt_name, base_ptr, &result));
+ if (strip) {
+ ASSERT_EQ(result.at(0), '{');
+ ASSERT_EQ(result.at(result.size() - 1), '}');
+ result = result.substr(1, result.size() - 2);
+ }
ASSERT_OK(opt_info.Parse(config_options, opt_name, result, comp_ptr));
ASSERT_TRUE(opt_info.AreEqual(config_options, opt_name, base_ptr, comp_ptr,
&mismatch));
}
-static void TestAndCompareOption(const ConfigOptions& config_options,
- const OptionTypeInfo& opt_info,
- const std::string& opt_name,
- const std::string& opt_value, void* base_ptr,
- void* comp_ptr) {
+static void TestParseAndCompareOption(const ConfigOptions& config_options,
+ const OptionTypeInfo& opt_info,
+ const std::string& opt_name,
+ const std::string& opt_value,
+ void* base_ptr, void* comp_ptr,
+ bool strip = false) {
ASSERT_OK(opt_info.Parse(config_options, opt_name, opt_value, base_ptr));
- TestAndCompareOption(config_options, opt_info, opt_name, base_ptr, comp_ptr);
+ TestAndCompareOption(config_options, opt_info, opt_name, base_ptr, comp_ptr,
+ strip);
}
template <typename T>
ASSERT_FALSE(opt_info.AreEqual(config_options, "Enum", &e1, &e2, &mismatch));
ASSERT_EQ(mismatch, "Enum");
- TestAndCompareOption(config_options, opt_info, "", "C", &e1, &e2);
+ TestParseAndCompareOption(config_options, opt_info, "", "C", &e1, &e2);
ASSERT_EQ(e2, TestEnum::kC);
ASSERT_NOK(opt_info.Parse(config_options, "", "D", &e1));
ConfigOptions config_options;
for (auto iter : OptionsHelper::compaction_style_string_map) {
CompactionStyle e1, e2;
- TestAndCompareOption(config_options,
- OptionTypeInfo(0, OptionType::kCompactionStyle),
- "CompactionStyle", iter.first, &e1, &e2);
+ TestParseAndCompareOption(config_options,
+ OptionTypeInfo(0, OptionType::kCompactionStyle),
+ "CompactionStyle", iter.first, &e1, &e2);
ASSERT_EQ(e1, iter.second);
}
for (auto iter : OptionsHelper::compaction_pri_string_map) {
CompactionPri e1, e2;
- TestAndCompareOption(config_options,
- OptionTypeInfo(0, OptionType::kCompactionPri),
- "CompactionPri", iter.first, &e1, &e2);
+ TestParseAndCompareOption(config_options,
+ OptionTypeInfo(0, OptionType::kCompactionPri),
+ "CompactionPri", iter.first, &e1, &e2);
ASSERT_EQ(e1, iter.second);
}
for (auto iter : OptionsHelper::compression_type_string_map) {
CompressionType e1, e2;
- TestAndCompareOption(config_options,
- OptionTypeInfo(0, OptionType::kCompressionType),
- "CompressionType", iter.first, &e1, &e2);
+ TestParseAndCompareOption(config_options,
+ OptionTypeInfo(0, OptionType::kCompressionType),
+ "CompressionType", iter.first, &e1, &e2);
ASSERT_EQ(e1, iter.second);
}
for (auto iter : OptionsHelper::compaction_stop_style_string_map) {
CompactionStopStyle e1, e2;
- TestAndCompareOption(config_options,
- OptionTypeInfo(0, OptionType::kCompactionStopStyle),
- "CompactionStopStyle", iter.first, &e1, &e2);
+ TestParseAndCompareOption(
+ config_options, OptionTypeInfo(0, OptionType::kCompactionStopStyle),
+ "CompactionStopStyle", iter.first, &e1, &e2);
ASSERT_EQ(e1, iter.second);
}
for (auto iter : OptionsHelper::checksum_type_string_map) {
ChecksumType e1, e2;
- TestAndCompareOption(config_options,
- OptionTypeInfo(0, OptionType::kChecksumType),
- "CheckSumType", iter.first, &e1, &e2);
+ TestParseAndCompareOption(config_options,
+ OptionTypeInfo(0, OptionType::kChecksumType),
+ "CheckSumType", iter.first, &e1, &e2);
ASSERT_EQ(e1, iter.second);
}
for (auto iter : OptionsHelper::encoding_type_string_map) {
EncodingType e1, e2;
- TestAndCompareOption(config_options,
- OptionTypeInfo(0, OptionType::kEncodingType),
- "EncodingType", iter.first, &e1, &e2);
+ TestParseAndCompareOption(config_options,
+ OptionTypeInfo(0, OptionType::kEncodingType),
+ "EncodingType", iter.first, &e1, &e2);
ASSERT_EQ(e1, iter.second);
}
}
Extended e1, e2;
ConfigOptions config_options;
std::string mismatch;
- TestAndCompareOption(config_options, basic_info, "b", "{i=33;s=33}", &e1.b,
- &e2.b);
+ TestParseAndCompareOption(config_options, basic_info, "b", "{i=33;s=33}",
+ &e1.b, &e2.b);
ASSERT_EQ(e1.b.i, 33);
ASSERT_EQ(e1.b.s, "33");
- TestAndCompareOption(config_options, basic_info, "b.i", "44", &e1.b, &e2.b);
+ TestParseAndCompareOption(config_options, basic_info, "b.i", "44", &e1.b,
+ &e2.b);
ASSERT_EQ(e1.b.i, 44);
- TestAndCompareOption(config_options, basic_info, "i", "55", &e1.b, &e2.b);
+ TestParseAndCompareOption(config_options, basic_info, "i", "55", &e1.b,
+ &e2.b);
ASSERT_EQ(e1.b.i, 55);
e1.b.i = 0;
ASSERT_NOK(basic_info.Parse(config_options, "b.j", "44", &e1.b));
ASSERT_NOK(basic_info.Parse(config_options, "j", "44", &e1.b));
- TestAndCompareOption(config_options, extended_info, "e",
- "b={i=55;s=55}; j=22;", &e1, &e2);
+ TestParseAndCompareOption(config_options, extended_info, "e",
+ "b={i=55;s=55}; j=22;", &e1, &e2);
ASSERT_EQ(e1.b.i, 55);
ASSERT_EQ(e1.j, 22);
ASSERT_EQ(e1.b.s, "55");
- TestAndCompareOption(config_options, extended_info, "e.b", "{i=66;s=66;}",
- &e1, &e2);
+ TestParseAndCompareOption(config_options, extended_info, "e.b",
+ "{i=66;s=66;}", &e1, &e2);
ASSERT_EQ(e1.b.i, 66);
ASSERT_EQ(e1.j, 22);
ASSERT_EQ(e1.b.s, "66");
- TestAndCompareOption(config_options, extended_info, "e.b.i", "77", &e1, &e2);
+ TestParseAndCompareOption(config_options, extended_info, "e.b.i", "77", &e1,
+ &e2);
ASSERT_EQ(e1.b.i, 77);
ASSERT_EQ(e1.j, 22);
ASSERT_EQ(e1.b.s, "66");
std::string mismatch;
ConfigOptions config_options;
- TestAndCompareOption(config_options, vec_info, "v", "a:b:c:d", &vec1, &vec2);
+ TestParseAndCompareOption(config_options, vec_info, "v", "a:b:c:d", &vec1,
+ &vec2);
ASSERT_EQ(vec1.size(), 4);
ASSERT_EQ(vec1[0], "a");
ASSERT_EQ(vec1[1], "b");
ASSERT_EQ(mismatch, "v");
// Test vectors with inner brackets
- TestAndCompareOption(config_options, vec_info, "v", "a:{b}:c:d", &vec1,
- &vec2);
+ TestParseAndCompareOption(config_options, vec_info, "v", "a:{b}:c:d", &vec1,
+ &vec2);
ASSERT_EQ(vec1.size(), 4);
ASSERT_EQ(vec1[0], "a");
ASSERT_EQ(vec1[1], "b");
OptionTypeInfo bar_info = OptionTypeInfo::Vector<std::string>(
0, OptionVerificationType::kNormal, OptionTypeFlags::kNone,
{0, OptionType::kString}, '|');
- TestAndCompareOption(config_options, vec_info, "v", "x|y|z", &vec1, &vec2);
+ TestParseAndCompareOption(config_options, vec_info, "v", "x|y|z", &vec1,
+ &vec2);
// Test vectors with inner vector
- TestAndCompareOption(config_options, bar_info, "v",
- "a|{b1|b2}|{c1|c2|{d1|d2}}", &vec1, &vec2);
+ TestParseAndCompareOption(config_options, bar_info, "v",
+ "a|{b1|b2}|{c1|c2|{d1|d2}}", &vec1, &vec2, false);
ASSERT_EQ(vec1.size(), 3);
ASSERT_EQ(vec1[0], "a");
ASSERT_EQ(vec1[1], "b1|b2");
ASSERT_EQ(vec1[2], "c1|c2|{d1|d2}");
+
+ TestParseAndCompareOption(config_options, bar_info, "v",
+ "{a1|a2}|{b1|{c1|c2}}|d1", &vec1, &vec2, true);
+ ASSERT_EQ(vec1.size(), 3);
+ ASSERT_EQ(vec1[0], "a1|a2");
+ ASSERT_EQ(vec1[1], "b1|{c1|c2}");
+ ASSERT_EQ(vec1[2], "d1");
+
+ TestParseAndCompareOption(config_options, bar_info, "v", "{a1}", &vec1, &vec2,
+ false);
+ ASSERT_EQ(vec1.size(), 1);
+ ASSERT_EQ(vec1[0], "a1");
+
+ TestParseAndCompareOption(config_options, bar_info, "v", "{a1|a2}|{b1|b2}",
+ &vec1, &vec2, true);
+ ASSERT_EQ(vec1.size(), 2);
+ ASSERT_EQ(vec1[0], "a1|a2");
+ ASSERT_EQ(vec1[1], "b1|b2");
}
TEST_F(OptionTypeInfoTest, TestStaticType) {
};
MockTableFactory();
- const char* Name() const override { return "MockTable"; }
+ static const char* kClassName() { return "MockTable"; }
+ const char* Name() const override { return kClassName(); }
using TableFactory::NewTableReader;
Status NewTableReader(
const ReadOptions& ro, const TableReaderOptions& table_reader_options,
#include "port/port.h"
#include "rocksdb/convenience.h"
#include "rocksdb/system_clock.h"
+#include "rocksdb/utilities/object_registry.h"
#include "test_util/sync_point.h"
#include "util/random.h"
}
}
+#ifndef ROCKSDB_LITE
+// This method loads existing test classes into the ObjectRegistry
+int RegisterTestObjects(ObjectLibrary& library, const std::string& /*arg*/) {
+ size_t num_types;
+ library.Register<const Comparator>(
+ test::SimpleSuffixReverseComparator::kClassName(),
+ [](const std::string& /*uri*/,
+ std::unique_ptr<const Comparator>* /*guard*/,
+ std::string* /* errmsg */) {
+ static test::SimpleSuffixReverseComparator ssrc;
+ return &ssrc;
+ });
+ library.Register<MergeOperator>(
+ "Changling",
+ [](const std::string& uri, std::unique_ptr<MergeOperator>* guard,
+ std::string* /* errmsg */) {
+ guard->reset(new test::ChanglingMergeOperator(uri));
+ return guard->get();
+ });
+ library.Register<CompactionFilter>(
+ "Changling",
+ [](const std::string& uri, std::unique_ptr<CompactionFilter>* /*guard*/,
+ std::string* /* errmsg */) {
+ return new test::ChanglingCompactionFilter(uri);
+ });
+ library.Register<CompactionFilterFactory>(
+ "Changling", [](const std::string& uri,
+ std::unique_ptr<CompactionFilterFactory>* guard,
+ std::string* /* errmsg */) {
+ guard->reset(new test::ChanglingCompactionFilterFactory(uri));
+ return guard->get();
+ });
+
+ return static_cast<int>(library.GetFactoryCount(&num_types));
+}
+#endif // ROCKSDB_LITE
} // namespace test
} // namespace ROCKSDB_NAMESPACE
namespace ROCKSDB_NAMESPACE {
class FileSystem;
+class ObjectLibrary;
class Random;
class SequentialFile;
class SequentialFileReader;
// environment variables.
Status CreateEnvFromSystem(const ConfigOptions& options, Env** result,
std::shared_ptr<Env>* guard);
+
+#ifndef ROCKSDB_LITE
+// Registers the testutil classes with the ObjectLibrary
+int RegisterTestObjects(ObjectLibrary& library, const std::string& /*arg*/);
+#endif // ROCKSDB_LITE
} // namespace test
} // namespace ROCKSDB_NAMESPACE