summaryrefslogtreecommitdiffstats
path: root/src/rocksdb/options/configurable_test.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/rocksdb/options/configurable_test.cc881
1 files changed, 881 insertions, 0 deletions
diff --git a/src/rocksdb/options/configurable_test.cc b/src/rocksdb/options/configurable_test.cc
new file mode 100644
index 000000000..6ec02cf3a
--- /dev/null
+++ b/src/rocksdb/options/configurable_test.cc
@@ -0,0 +1,881 @@
+// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
+// This source code is licensed under both the GPLv2 (found in the
+// COPYING file in the root directory) and Apache 2.0 License
+// (found in the LICENSE.Apache file in the root directory).
+//
+// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
+// 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.
+
+#include "options/configurable_test.h"
+
+#include <cctype>
+#include <cinttypes>
+#include <cstring>
+#include <unordered_map>
+
+#include "options/configurable_helper.h"
+#include "options/options_helper.h"
+#include "options/options_parser.h"
+#include "rocksdb/configurable.h"
+#include "test_util/testharness.h"
+#include "test_util/testutil.h"
+
+#ifndef GFLAGS
+bool FLAGS_enable_print = false;
+#else
+#include "util/gflags_compat.h"
+using GFLAGS_NAMESPACE::ParseCommandLineFlags;
+DEFINE_bool(enable_print, false, "Print options generated to console.");
+#endif // GFLAGS
+
+namespace ROCKSDB_NAMESPACE {
+namespace test {
+class StringLogger : public Logger {
+ public:
+ using Logger::Logv;
+ void Logv(const char* format, va_list ap) override {
+ char buffer[1000];
+ vsnprintf(buffer, sizeof(buffer), format, ap);
+ string_.append(buffer);
+ }
+ const std::string& str() const { return string_; }
+ void clear() { string_.clear(); }
+
+ private:
+ std::string string_;
+};
+static std::unordered_map<std::string, OptionTypeInfo> struct_option_info = {
+#ifndef ROCKSDB_LITE
+ {"struct", OptionTypeInfo::Struct("struct", &simple_option_info, 0,
+ OptionVerificationType::kNormal,
+ OptionTypeFlags::kMutable)},
+#endif // ROCKSDB_LITE
+};
+
+static std::unordered_map<std::string, OptionTypeInfo> imm_struct_option_info =
+ {
+#ifndef ROCKSDB_LITE
+ {"struct", OptionTypeInfo::Struct("struct", &simple_option_info, 0,
+ OptionVerificationType::kNormal,
+ OptionTypeFlags::kNone)},
+#endif // ROCKSDB_LITE
+};
+
+class SimpleConfigurable : public TestConfigurable<Configurable> {
+ public:
+ static SimpleConfigurable* Create(
+ const std::string& name = "simple",
+ int mode = TestConfigMode::kDefaultMode,
+ const std::unordered_map<std::string, OptionTypeInfo>* map =
+ &simple_option_info) {
+ return new SimpleConfigurable(name, mode, map);
+ }
+
+ SimpleConfigurable(const std::string& name, int mode,
+ const std::unordered_map<std::string, OptionTypeInfo>*
+ map = &simple_option_info)
+ : TestConfigurable(name, mode, map) {
+ if ((mode & TestConfigMode::kUniqueMode) != 0) {
+ unique_.reset(SimpleConfigurable::Create("Unique" + name_));
+ RegisterOptions(name_ + "Unique", &unique_, &unique_option_info);
+ }
+ if ((mode & TestConfigMode::kSharedMode) != 0) {
+ shared_.reset(SimpleConfigurable::Create("Shared" + name_));
+ RegisterOptions(name_ + "Shared", &shared_, &shared_option_info);
+ }
+ if ((mode & TestConfigMode::kRawPtrMode) != 0) {
+ pointer_ = SimpleConfigurable::Create("Pointer" + name_);
+ RegisterOptions(name_ + "Pointer", &pointer_, &pointer_option_info);
+ }
+ }
+
+}; // End class SimpleConfigurable
+
+using ConfigTestFactoryFunc = std::function<Configurable*()>;
+
+class ConfigurableTest : public testing::Test {
+ public:
+ ConfigurableTest() { config_options_.invoke_prepare_options = false; }
+
+ ConfigOptions config_options_;
+};
+
+TEST_F(ConfigurableTest, GetOptionsPtrTest) {
+ std::string opt_str;
+ std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create());
+ ASSERT_NE(configurable->GetOptions<TestOptions>("simple"), nullptr);
+ ASSERT_EQ(configurable->GetOptions<TestOptions>("bad-opt"), nullptr);
+}
+
+TEST_F(ConfigurableTest, ConfigureFromMapTest) {
+ std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create());
+ auto* opts = configurable->GetOptions<TestOptions>("simple");
+ ASSERT_OK(configurable->ConfigureFromMap(config_options_, {}));
+ ASSERT_NE(opts, nullptr);
+#ifndef ROCKSDB_LITE
+ std::unordered_map<std::string, std::string> options_map = {
+ {"int", "1"}, {"bool", "true"}, {"string", "string"}};
+ ASSERT_OK(configurable->ConfigureFromMap(config_options_, options_map));
+ ASSERT_EQ(opts->i, 1);
+ ASSERT_EQ(opts->b, true);
+ ASSERT_EQ(opts->s, "string");
+#endif
+}
+
+TEST_F(ConfigurableTest, ConfigureFromStringTest) {
+ std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create());
+ auto* opts = configurable->GetOptions<TestOptions>("simple");
+ ASSERT_OK(configurable->ConfigureFromString(config_options_, ""));
+ ASSERT_NE(opts, nullptr);
+#ifndef ROCKSDB_LITE // GetOptionsFromMap is not supported in ROCKSDB_LITE
+ ASSERT_OK(configurable->ConfigureFromString(config_options_,
+ "int=1;bool=true;string=s"));
+ ASSERT_EQ(opts->i, 1);
+ ASSERT_EQ(opts->b, true);
+ ASSERT_EQ(opts->s, "s");
+#endif
+}
+
+#ifndef ROCKSDB_LITE // GetOptionsFromMap is not supported in ROCKSDB_LITE
+TEST_F(ConfigurableTest, ConfigureIgnoreTest) {
+ std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create());
+ std::unordered_map<std::string, std::string> options_map = {{"unused", "u"}};
+ ConfigOptions ignore = config_options_;
+ ignore.ignore_unknown_options = true;
+ ASSERT_NOK(configurable->ConfigureFromMap(config_options_, options_map));
+ ASSERT_OK(configurable->ConfigureFromMap(ignore, options_map));
+ ASSERT_NOK(configurable->ConfigureFromString(config_options_, "unused=u"));
+ ASSERT_OK(configurable->ConfigureFromString(ignore, "unused=u"));
+}
+
+TEST_F(ConfigurableTest, ConfigureNestedOptionsTest) {
+ std::unique_ptr<Configurable> base, copy;
+ std::string opt_str;
+ std::string mismatch;
+
+ base.reset(SimpleConfigurable::Create("simple", TestConfigMode::kAllOptMode));
+ copy.reset(SimpleConfigurable::Create("simple", TestConfigMode::kAllOptMode));
+ ASSERT_OK(base->ConfigureFromString(config_options_,
+ "shared={int=10; string=10};"
+ "unique={int=20; string=20};"
+ "pointer={int=30; string=30};"));
+ ASSERT_OK(base->GetOptionString(config_options_, &opt_str));
+ ASSERT_OK(copy->ConfigureFromString(config_options_, opt_str));
+ ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
+}
+
+TEST_F(ConfigurableTest, GetOptionsTest) {
+ std::unique_ptr<Configurable> simple;
+
+ simple.reset(
+ SimpleConfigurable::Create("simple", TestConfigMode::kAllOptMode));
+ int i = 11;
+ for (auto opt : {"", "shared.", "unique.", "pointer."}) {
+ std::string value;
+ std::string expected = std::to_string(i);
+ std::string opt_name = opt;
+ ASSERT_OK(
+ simple->ConfigureOption(config_options_, opt_name + "int", expected));
+ ASSERT_OK(simple->GetOption(config_options_, opt_name + "int", &value));
+ ASSERT_EQ(expected, value);
+ ASSERT_OK(simple->ConfigureOption(config_options_, opt_name + "string",
+ expected));
+ ASSERT_OK(simple->GetOption(config_options_, opt_name + "string", &value));
+ ASSERT_EQ(expected, value);
+
+ ASSERT_NOK(
+ simple->ConfigureOption(config_options_, opt_name + "bad", expected));
+ ASSERT_NOK(simple->GetOption(config_options_, "bad option", &value));
+ ASSERT_TRUE(value.empty());
+ i += 11;
+ }
+}
+
+TEST_F(ConfigurableTest, ConfigureBadOptionsTest) {
+ std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create());
+ auto* opts = configurable->GetOptions<TestOptions>("simple");
+ ASSERT_NE(opts, nullptr);
+ ASSERT_OK(configurable->ConfigureOption(config_options_, "int", "42"));
+ ASSERT_EQ(opts->i, 42);
+ ASSERT_NOK(configurable->ConfigureOption(config_options_, "int", "fred"));
+ ASSERT_NOK(configurable->ConfigureOption(config_options_, "bool", "fred"));
+ ASSERT_NOK(
+ configurable->ConfigureFromString(config_options_, "int=33;unused=u"));
+ ASSERT_EQ(opts->i, 42);
+}
+
+TEST_F(ConfigurableTest, InvalidOptionTest) {
+ std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create());
+ std::unordered_map<std::string, std::string> options_map = {
+ {"bad-option", "bad"}};
+ ASSERT_NOK(configurable->ConfigureFromMap(config_options_, options_map));
+ ASSERT_NOK(
+ configurable->ConfigureFromString(config_options_, "bad-option=bad"));
+ ASSERT_NOK(
+ configurable->ConfigureOption(config_options_, "bad-option", "bad"));
+}
+
+static std::unordered_map<std::string, OptionTypeInfo> validated_option_info = {
+#ifndef ROCKSDB_LITE
+ {"validated",
+ {0, OptionType::kBoolean, OptionVerificationType::kNormal,
+ OptionTypeFlags::kNone}},
+#endif // ROCKSDB_LITE
+};
+static std::unordered_map<std::string, OptionTypeInfo> prepared_option_info = {
+#ifndef ROCKSDB_LITE
+ {"prepared",
+ {0, OptionType::kInt, OptionVerificationType::kNormal,
+ OptionTypeFlags::kMutable}},
+#endif // ROCKSDB_LITE
+};
+static std::unordered_map<std::string, OptionTypeInfo>
+ dont_prepare_option_info = {
+#ifndef ROCKSDB_LITE
+ {"unique",
+ {0, OptionType::kConfigurable, OptionVerificationType::kNormal,
+ (OptionTypeFlags::kUnique | OptionTypeFlags::kDontPrepare)}},
+
+#endif // ROCKSDB_LITE
+};
+
+class ValidatedConfigurable : public SimpleConfigurable {
+ public:
+ ValidatedConfigurable(const std::string& name, unsigned char mode,
+ bool dont_prepare = false)
+ : SimpleConfigurable(name, TestConfigMode::kDefaultMode),
+ validated(false),
+ prepared(0) {
+ RegisterOptions("Validated", &validated, &validated_option_info);
+ RegisterOptions("Prepared", &prepared, &prepared_option_info);
+ if ((mode & TestConfigMode::kUniqueMode) != 0) {
+ unique_.reset(new ValidatedConfigurable(
+ "Unique" + name_, TestConfigMode::kDefaultMode, false));
+ if (dont_prepare) {
+ RegisterOptions(name_ + "Unique", &unique_, &dont_prepare_option_info);
+ } else {
+ RegisterOptions(name_ + "Unique", &unique_, &unique_option_info);
+ }
+ }
+ }
+
+ Status PrepareOptions(const ConfigOptions& config_options) override {
+ if (++prepared <= 0) {
+ return Status::InvalidArgument("Cannot prepare option");
+ } else {
+ return SimpleConfigurable::PrepareOptions(config_options);
+ }
+ }
+
+ Status ValidateOptions(const DBOptions& db_opts,
+ const ColumnFamilyOptions& cf_opts) const override {
+ if (!validated) {
+ return Status::InvalidArgument("Not Validated");
+ } else {
+ return SimpleConfigurable::ValidateOptions(db_opts, cf_opts);
+ }
+ }
+
+ private:
+ bool validated;
+ int prepared;
+};
+
+TEST_F(ConfigurableTest, ValidateOptionsTest) {
+ std::unique_ptr<Configurable> configurable(
+ new ValidatedConfigurable("validated", TestConfigMode::kDefaultMode));
+ ColumnFamilyOptions cf_opts;
+ DBOptions db_opts;
+ ASSERT_OK(
+ configurable->ConfigureOption(config_options_, "validated", "false"));
+ ASSERT_NOK(configurable->ValidateOptions(db_opts, cf_opts));
+ ASSERT_OK(
+ configurable->ConfigureOption(config_options_, "validated", "true"));
+ ASSERT_OK(configurable->ValidateOptions(db_opts, cf_opts));
+}
+
+TEST_F(ConfigurableTest, PrepareOptionsTest) {
+ std::unique_ptr<Configurable> c(
+ new ValidatedConfigurable("Simple", TestConfigMode::kUniqueMode, false));
+ auto cp = c->GetOptions<int>("Prepared");
+ auto u = c->GetOptions<std::unique_ptr<Configurable>>("SimpleUnique");
+ auto up = u->get()->GetOptions<int>("Prepared");
+ config_options_.invoke_prepare_options = false;
+
+ ASSERT_NE(cp, nullptr);
+ ASSERT_NE(up, nullptr);
+ ASSERT_EQ(*cp, 0);
+ ASSERT_EQ(*up, 0);
+ ASSERT_OK(c->ConfigureFromMap(config_options_, {}));
+ ASSERT_EQ(*cp, 0);
+ ASSERT_EQ(*up, 0);
+ config_options_.invoke_prepare_options = true;
+ ASSERT_OK(c->ConfigureFromMap(config_options_, {}));
+ ASSERT_EQ(*cp, 1);
+ ASSERT_EQ(*up, 1);
+ ASSERT_OK(c->ConfigureFromString(config_options_, "prepared=0"));
+ ASSERT_EQ(*up, 2);
+ ASSERT_EQ(*cp, 1);
+
+ ASSERT_NOK(c->ConfigureFromString(config_options_, "prepared=-2"));
+
+ c.reset(
+ new ValidatedConfigurable("Simple", TestConfigMode::kUniqueMode, true));
+ cp = c->GetOptions<int>("Prepared");
+ u = c->GetOptions<std::unique_ptr<Configurable>>("SimpleUnique");
+ up = u->get()->GetOptions<int>("Prepared");
+
+ ASSERT_OK(c->ConfigureFromString(config_options_, "prepared=0"));
+ ASSERT_EQ(*cp, 1);
+ ASSERT_EQ(*up, 0);
+}
+
+TEST_F(ConfigurableTest, CopyObjectTest) {
+ class CopyConfigurable : public Configurable {
+ public:
+ CopyConfigurable() : prepared_(0), validated_(0) {}
+ Status PrepareOptions(const ConfigOptions& options) override {
+ prepared_++;
+ return Configurable::PrepareOptions(options);
+ }
+ Status ValidateOptions(const DBOptions& db_opts,
+ const ColumnFamilyOptions& cf_opts) const override {
+ validated_++;
+ return Configurable::ValidateOptions(db_opts, cf_opts);
+ }
+ int prepared_;
+ mutable int validated_;
+ };
+
+ CopyConfigurable c1;
+ ConfigOptions config_options;
+ Options options;
+
+ ASSERT_OK(c1.PrepareOptions(config_options));
+ ASSERT_OK(c1.ValidateOptions(options, options));
+ ASSERT_EQ(c1.prepared_, 1);
+ ASSERT_EQ(c1.validated_, 1);
+ CopyConfigurable c2 = c1;
+ ASSERT_OK(c1.PrepareOptions(config_options));
+ ASSERT_OK(c1.ValidateOptions(options, options));
+ ASSERT_EQ(c2.prepared_, 1);
+ ASSERT_EQ(c2.validated_, 1);
+ ASSERT_EQ(c1.prepared_, 2);
+ ASSERT_EQ(c1.validated_, 2);
+}
+
+TEST_F(ConfigurableTest, MutableOptionsTest) {
+ static std::unordered_map<std::string, OptionTypeInfo> imm_option_info = {
+#ifndef ROCKSDB_LITE
+ {"imm", OptionTypeInfo::Struct("imm", &simple_option_info, 0,
+ OptionVerificationType::kNormal,
+ OptionTypeFlags::kNone)},
+#endif // ROCKSDB_LITE
+ };
+
+ class MutableConfigurable : public SimpleConfigurable {
+ public:
+ MutableConfigurable()
+ : SimpleConfigurable("mutable", TestConfigMode::kDefaultMode |
+ TestConfigMode::kUniqueMode |
+ TestConfigMode::kSharedMode) {
+ RegisterOptions("struct", &options_, &struct_option_info);
+ RegisterOptions("imm", &options_, &imm_option_info);
+ }
+ };
+ MutableConfigurable mc;
+ ConfigOptions options = config_options_;
+
+ ASSERT_OK(mc.ConfigureOption(options, "bool", "true"));
+ ASSERT_OK(mc.ConfigureOption(options, "int", "42"));
+ auto* opts = mc.GetOptions<TestOptions>("mutable");
+ ASSERT_NE(opts, nullptr);
+ ASSERT_EQ(opts->i, 42);
+ ASSERT_EQ(opts->b, true);
+ ASSERT_OK(mc.ConfigureOption(options, "struct", "{bool=false;}"));
+ ASSERT_OK(mc.ConfigureOption(options, "imm", "{int=55;}"));
+
+ options.mutable_options_only = true;
+
+ // Now only mutable options should be settable.
+ ASSERT_NOK(mc.ConfigureOption(options, "bool", "true"));
+ ASSERT_OK(mc.ConfigureOption(options, "int", "24"));
+ ASSERT_EQ(opts->i, 24);
+ ASSERT_EQ(opts->b, false);
+ ASSERT_NOK(mc.ConfigureFromString(options, "bool=false;int=33;"));
+ ASSERT_EQ(opts->i, 24);
+ ASSERT_EQ(opts->b, false);
+
+ // Setting options through an immutable struct fails
+ ASSERT_NOK(mc.ConfigureOption(options, "imm", "{int=55;}"));
+ ASSERT_NOK(mc.ConfigureOption(options, "imm.int", "55"));
+ ASSERT_EQ(opts->i, 24);
+ ASSERT_EQ(opts->b, false);
+
+ // Setting options through an mutable struct succeeds
+ ASSERT_OK(mc.ConfigureOption(options, "struct", "{int=44;}"));
+ ASSERT_EQ(opts->i, 44);
+ ASSERT_OK(mc.ConfigureOption(options, "struct.int", "55"));
+ ASSERT_EQ(opts->i, 55);
+
+ // Setting nested immutable configurable options fail
+ ASSERT_NOK(mc.ConfigureOption(options, "shared", "{bool=true;}"));
+ ASSERT_NOK(mc.ConfigureOption(options, "shared.bool", "true"));
+
+ // Setting nested mutable configurable options succeeds
+ ASSERT_OK(mc.ConfigureOption(options, "unique", "{bool=true}"));
+ ASSERT_OK(mc.ConfigureOption(options, "unique.bool", "true"));
+}
+
+TEST_F(ConfigurableTest, DeprecatedOptionsTest) {
+ static std::unordered_map<std::string, OptionTypeInfo>
+ deprecated_option_info = {
+ {"deprecated",
+ {offsetof(struct TestOptions, b), OptionType::kBoolean,
+ OptionVerificationType::kDeprecated, OptionTypeFlags::kNone}}};
+ std::unique_ptr<Configurable> orig;
+ orig.reset(SimpleConfigurable::Create("simple", TestConfigMode::kDefaultMode,
+ &deprecated_option_info));
+ auto* opts = orig->GetOptions<TestOptions>("simple");
+ ASSERT_NE(opts, nullptr);
+ opts->d = true;
+ ASSERT_OK(orig->ConfigureOption(config_options_, "deprecated", "false"));
+ ASSERT_TRUE(opts->d);
+ ASSERT_OK(orig->ConfigureFromString(config_options_, "deprecated=false"));
+ ASSERT_TRUE(opts->d);
+}
+
+TEST_F(ConfigurableTest, AliasOptionsTest) {
+ static std::unordered_map<std::string, OptionTypeInfo> alias_option_info = {
+ {"bool",
+ {offsetof(struct TestOptions, b), OptionType::kBoolean,
+ OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
+ {"alias",
+ {offsetof(struct TestOptions, b), OptionType::kBoolean,
+ OptionVerificationType::kAlias, OptionTypeFlags::kNone, 0}}};
+ std::unique_ptr<Configurable> orig;
+ orig.reset(SimpleConfigurable::Create("simple", TestConfigMode::kDefaultMode,
+ &alias_option_info));
+ auto* opts = orig->GetOptions<TestOptions>("simple");
+ ASSERT_NE(opts, nullptr);
+ ASSERT_OK(orig->ConfigureOption(config_options_, "bool", "false"));
+ ASSERT_FALSE(opts->b);
+ ASSERT_OK(orig->ConfigureOption(config_options_, "alias", "true"));
+ ASSERT_TRUE(opts->b);
+ std::string opts_str;
+ ASSERT_OK(orig->GetOptionString(config_options_, &opts_str));
+ ASSERT_EQ(opts_str.find("alias"), std::string::npos);
+
+ ASSERT_OK(orig->ConfigureOption(config_options_, "bool", "false"));
+ ASSERT_FALSE(opts->b);
+ ASSERT_OK(orig->GetOption(config_options_, "alias", &opts_str));
+ ASSERT_EQ(opts_str, "false");
+}
+
+TEST_F(ConfigurableTest, NestedUniqueConfigTest) {
+ std::unique_ptr<Configurable> simple;
+ simple.reset(
+ SimpleConfigurable::Create("Outer", TestConfigMode::kAllOptMode));
+ const auto outer = simple->GetOptions<TestOptions>("Outer");
+ const auto unique =
+ simple->GetOptions<std::unique_ptr<Configurable>>("OuterUnique");
+ ASSERT_NE(outer, nullptr);
+ ASSERT_NE(unique, nullptr);
+ ASSERT_OK(
+ simple->ConfigureFromString(config_options_, "int=24;string=outer"));
+ ASSERT_OK(simple->ConfigureFromString(config_options_,
+ "unique={int=42;string=nested}"));
+ const auto inner = unique->get()->GetOptions<TestOptions>("UniqueOuter");
+ ASSERT_NE(inner, nullptr);
+ ASSERT_EQ(outer->i, 24);
+ ASSERT_EQ(outer->s, "outer");
+ ASSERT_EQ(inner->i, 42);
+ ASSERT_EQ(inner->s, "nested");
+}
+
+TEST_F(ConfigurableTest, NestedSharedConfigTest) {
+ std::unique_ptr<Configurable> simple;
+ simple.reset(SimpleConfigurable::Create(
+ "Outer", TestConfigMode::kDefaultMode | TestConfigMode::kSharedMode));
+ ASSERT_OK(
+ simple->ConfigureFromString(config_options_, "int=24;string=outer"));
+ ASSERT_OK(simple->ConfigureFromString(config_options_,
+ "shared={int=42;string=nested}"));
+ const auto outer = simple->GetOptions<TestOptions>("Outer");
+ const auto shared =
+ simple->GetOptions<std::shared_ptr<Configurable>>("OuterShared");
+ ASSERT_NE(outer, nullptr);
+ ASSERT_NE(shared, nullptr);
+ const auto inner = shared->get()->GetOptions<TestOptions>("SharedOuter");
+ ASSERT_NE(inner, nullptr);
+ ASSERT_EQ(outer->i, 24);
+ ASSERT_EQ(outer->s, "outer");
+ ASSERT_EQ(inner->i, 42);
+ ASSERT_EQ(inner->s, "nested");
+}
+
+TEST_F(ConfigurableTest, NestedRawConfigTest) {
+ std::unique_ptr<Configurable> simple;
+ simple.reset(SimpleConfigurable::Create(
+ "Outer", TestConfigMode::kDefaultMode | TestConfigMode::kRawPtrMode));
+ ASSERT_OK(
+ simple->ConfigureFromString(config_options_, "int=24;string=outer"));
+ ASSERT_OK(simple->ConfigureFromString(config_options_,
+ "pointer={int=42;string=nested}"));
+ const auto outer = simple->GetOptions<TestOptions>("Outer");
+ const auto pointer = simple->GetOptions<Configurable*>("OuterPointer");
+ ASSERT_NE(outer, nullptr);
+ ASSERT_NE(pointer, nullptr);
+ const auto inner = (*pointer)->GetOptions<TestOptions>("PointerOuter");
+ ASSERT_NE(inner, nullptr);
+ ASSERT_EQ(outer->i, 24);
+ ASSERT_EQ(outer->s, "outer");
+ ASSERT_EQ(inner->i, 42);
+ ASSERT_EQ(inner->s, "nested");
+}
+
+TEST_F(ConfigurableTest, MatchesTest) {
+ std::string mismatch;
+ std::unique_ptr<Configurable> base, copy;
+ base.reset(SimpleConfigurable::Create(
+ "simple", TestConfigMode::kDefaultMode | TestConfigMode::kNestedMode));
+ copy.reset(SimpleConfigurable::Create(
+ "simple", TestConfigMode::kDefaultMode | TestConfigMode::kNestedMode));
+ ASSERT_OK(base->ConfigureFromString(
+ config_options_,
+ "int=11;string=outer;unique={int=22;string=u};shared={int=33;string=s}"));
+ ASSERT_OK(copy->ConfigureFromString(
+ config_options_,
+ "int=11;string=outer;unique={int=22;string=u};shared={int=33;string=s}"));
+ ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
+ ASSERT_OK(base->ConfigureOption(config_options_, "shared", "int=44"));
+ ASSERT_FALSE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
+ ASSERT_EQ(mismatch, "shared.int");
+ std::string c1value, c2value;
+ ASSERT_OK(base->GetOption(config_options_, mismatch, &c1value));
+ ASSERT_OK(copy->GetOption(config_options_, mismatch, &c2value));
+ ASSERT_NE(c1value, c2value);
+}
+
+static Configurable* SimpleStructFactory() {
+ return SimpleConfigurable::Create(
+ "simple-struct", TestConfigMode::kDefaultMode, &struct_option_info);
+}
+
+TEST_F(ConfigurableTest, ConfigureStructTest) {
+ std::unique_ptr<Configurable> base(SimpleStructFactory());
+ std::unique_ptr<Configurable> copy(SimpleStructFactory());
+ std::string opt_str, value;
+ std::string mismatch;
+ std::unordered_set<std::string> names;
+
+ ASSERT_OK(
+ base->ConfigureFromString(config_options_, "struct={int=10; string=10}"));
+ ASSERT_OK(base->GetOptionString(config_options_, &opt_str));
+ ASSERT_OK(copy->ConfigureFromString(config_options_, opt_str));
+ ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
+ ASSERT_OK(base->GetOptionNames(config_options_, &names));
+ ASSERT_EQ(names.size(), 1);
+ ASSERT_EQ(*(names.begin()), "struct");
+ ASSERT_OK(
+ base->ConfigureFromString(config_options_, "struct={int=20; string=20}"));
+ ASSERT_OK(base->GetOption(config_options_, "struct", &value));
+ ASSERT_OK(copy->ConfigureOption(config_options_, "struct", value));
+ ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
+
+ ASSERT_NOK(base->ConfigureFromString(config_options_,
+ "struct={int=10; string=10; bad=11}"));
+ ASSERT_OK(base->ConfigureOption(config_options_, "struct.int", "42"));
+ ASSERT_NOK(base->ConfigureOption(config_options_, "struct.bad", "42"));
+ ASSERT_NOK(base->GetOption(config_options_, "struct.bad", &value));
+ ASSERT_OK(base->GetOption(config_options_, "struct.int", &value));
+ ASSERT_EQ(value, "42");
+}
+
+TEST_F(ConfigurableTest, ConfigurableEnumTest) {
+ std::unique_ptr<Configurable> base, copy;
+ base.reset(SimpleConfigurable::Create("e", TestConfigMode::kEnumMode));
+ copy.reset(SimpleConfigurable::Create("e", TestConfigMode::kEnumMode));
+
+ std::string opts_str;
+ std::string mismatch;
+
+ ASSERT_OK(base->ConfigureFromString(config_options_, "enum=B"));
+ ASSERT_FALSE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
+ ASSERT_OK(base->GetOptionString(config_options_, &opts_str));
+ ASSERT_OK(copy->ConfigureFromString(config_options_, opts_str));
+ ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
+ ASSERT_NOK(base->ConfigureOption(config_options_, "enum", "bad"));
+ ASSERT_NOK(base->ConfigureOption(config_options_, "unknown", "bad"));
+}
+
+#ifndef ROCKSDB_LITE
+static std::unordered_map<std::string, OptionTypeInfo> noserialize_option_info =
+ {
+ {"int",
+ {offsetof(struct TestOptions, i), OptionType::kInt,
+ OptionVerificationType::kNormal, OptionTypeFlags::kDontSerialize}},
+};
+
+TEST_F(ConfigurableTest, TestNoSerialize) {
+ std::unique_ptr<Configurable> base;
+ base.reset(SimpleConfigurable::Create("c", TestConfigMode::kDefaultMode,
+ &noserialize_option_info));
+ std::string opts_str, value;
+ ASSERT_OK(base->ConfigureFromString(config_options_, "int=10"));
+ ASSERT_OK(base->GetOptionString(config_options_, &opts_str));
+ ASSERT_EQ(opts_str, "");
+ ASSERT_NOK(base->GetOption(config_options_, "int", &value));
+}
+
+TEST_F(ConfigurableTest, TestNoCompare) {
+ std::unordered_map<std::string, OptionTypeInfo> nocomp_option_info = {
+ {"int",
+ {offsetof(struct TestOptions, i), OptionType::kInt,
+ OptionVerificationType::kNormal, OptionTypeFlags::kCompareNever}},
+ };
+ std::unordered_map<std::string, OptionTypeInfo> normal_option_info = {
+ {"int",
+ {offsetof(struct TestOptions, i), OptionType::kInt,
+ OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
+ };
+
+ std::unique_ptr<Configurable> base, copy;
+ base.reset(SimpleConfigurable::Create("c", TestConfigMode::kDefaultMode,
+ &nocomp_option_info));
+ copy.reset(SimpleConfigurable::Create("c", TestConfigMode::kDefaultMode,
+ &normal_option_info));
+ ASSERT_OK(base->ConfigureFromString(config_options_, "int=10"));
+ ASSERT_OK(copy->ConfigureFromString(config_options_, "int=20"));
+ std::string bvalue, cvalue, mismatch;
+ ASSERT_OK(base->GetOption(config_options_, "int", &bvalue));
+ ASSERT_OK(copy->GetOption(config_options_, "int", &cvalue));
+ ASSERT_EQ(bvalue, "10");
+ ASSERT_EQ(cvalue, "20");
+ ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
+ ASSERT_FALSE(copy->AreEquivalent(config_options_, base.get(), &mismatch));
+}
+
+TEST_F(ConfigurableTest, NullOptionMapTest) {
+ std::unique_ptr<Configurable> base;
+ std::unordered_set<std::string> names;
+ std::string str;
+
+ base.reset(
+ SimpleConfigurable::Create("c", TestConfigMode::kDefaultMode, nullptr));
+ ASSERT_NOK(base->ConfigureFromString(config_options_, "int=10"));
+ ASSERT_NOK(base->ConfigureFromString(config_options_, "int=20"));
+ ASSERT_NOK(base->ConfigureOption(config_options_, "int", "20"));
+ ASSERT_NOK(base->GetOption(config_options_, "int", &str));
+ ASSERT_NE(base->GetOptions<TestOptions>("c"), nullptr);
+ ASSERT_OK(base->GetOptionNames(config_options_, &names));
+ ASSERT_EQ(names.size(), 0UL);
+ ASSERT_OK(base->PrepareOptions(config_options_));
+ ASSERT_OK(base->ValidateOptions(DBOptions(), ColumnFamilyOptions()));
+ std::unique_ptr<Configurable> copy;
+ copy.reset(
+ SimpleConfigurable::Create("c", TestConfigMode::kDefaultMode, nullptr));
+ ASSERT_OK(base->GetOptionString(config_options_, &str));
+ ASSERT_OK(copy->ConfigureFromString(config_options_, str));
+ ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &str));
+}
+#endif
+
+static std::unordered_map<std::string, ConfigTestFactoryFunc> TestFactories = {
+ {"Simple", []() { return SimpleConfigurable::Create("simple"); }},
+ {"Struct", []() { return SimpleStructFactory(); }},
+ {"Unique",
+ []() {
+ return SimpleConfigurable::Create(
+ "simple", TestConfigMode::kSimpleMode | TestConfigMode::kUniqueMode);
+ }},
+ {"Shared",
+ []() {
+ return SimpleConfigurable::Create(
+ "simple", TestConfigMode::kSimpleMode | TestConfigMode::kSharedMode);
+ }},
+ {"Nested",
+ []() {
+ return SimpleConfigurable::Create(
+ "simple", TestConfigMode::kSimpleMode | TestConfigMode::kNestedMode);
+ }},
+ {"Mutable",
+ []() {
+ return SimpleConfigurable::Create("simple",
+ TestConfigMode::kMutableMode |
+ TestConfigMode::kSimpleMode |
+ TestConfigMode::kNestedMode);
+ }},
+ {"ThreeDeep",
+ []() {
+ Configurable* simple = SimpleConfigurable::Create(
+ "Simple",
+ TestConfigMode::kUniqueMode | TestConfigMode::kDefaultMode);
+ auto* unique =
+ simple->GetOptions<std::unique_ptr<Configurable>>("SimpleUnique");
+ unique->reset(SimpleConfigurable::Create(
+ "Child",
+ TestConfigMode::kUniqueMode | TestConfigMode::kDefaultMode));
+ unique = unique->get()->GetOptions<std::unique_ptr<Configurable>>(
+ "ChildUnique");
+ unique->reset(
+ SimpleConfigurable::Create("Child", TestConfigMode::kDefaultMode));
+ return simple;
+ }},
+ {"DBOptions",
+ []() {
+ auto config = DBOptionsAsConfigurable(DBOptions());
+ return config.release();
+ }},
+ {"CFOptions",
+ []() {
+ auto config = CFOptionsAsConfigurable(ColumnFamilyOptions());
+ return config.release();
+ }},
+ {"BlockBased", []() { return NewBlockBasedTableFactory(); }},
+};
+
+class ConfigurableParamTest : public ConfigurableTest,
+ virtual public ::testing::WithParamInterface<
+ std::pair<std::string, std::string>> {
+ public:
+ ConfigurableParamTest() {
+ type_ = GetParam().first;
+ configuration_ = GetParam().second;
+ assert(TestFactories.find(type_) != TestFactories.end());
+ object_.reset(CreateConfigurable());
+ }
+
+ Configurable* CreateConfigurable() {
+ const auto& iter = TestFactories.find(type_);
+ return (iter->second)();
+ }
+
+ void TestConfigureOptions(const ConfigOptions& opts);
+ std::string type_;
+ std::string configuration_;
+ std::unique_ptr<Configurable> object_;
+};
+
+void ConfigurableParamTest::TestConfigureOptions(
+ const ConfigOptions& config_options) {
+ std::unique_ptr<Configurable> base, copy;
+ std::unordered_set<std::string> names;
+ std::string opt_str, mismatch;
+
+ base.reset(CreateConfigurable());
+ copy.reset(CreateConfigurable());
+
+ ASSERT_OK(base->ConfigureFromString(config_options, configuration_));
+ ASSERT_OK(base->GetOptionString(config_options, &opt_str));
+ ASSERT_OK(copy->ConfigureFromString(config_options, opt_str));
+ ASSERT_OK(copy->GetOptionString(config_options, &opt_str));
+ ASSERT_TRUE(base->AreEquivalent(config_options, copy.get(), &mismatch));
+
+ copy.reset(CreateConfigurable());
+ ASSERT_OK(base->GetOptionNames(config_options, &names));
+ std::unordered_map<std::string, std::string> unused;
+ bool found_one = false;
+ for (auto name : names) {
+ std::string value;
+ Status s = base->GetOption(config_options, name, &value);
+ if (s.ok()) {
+ s = copy->ConfigureOption(config_options, name, value);
+ if (s.ok() || s.IsNotSupported()) {
+ found_one = true;
+ } else {
+ unused[name] = value;
+ }
+ } else {
+ ASSERT_TRUE(s.IsNotSupported());
+ }
+ }
+ ASSERT_TRUE(found_one || names.empty());
+ while (found_one && !unused.empty()) {
+ found_one = false;
+ for (auto iter = unused.begin(); iter != unused.end();) {
+ if (copy->ConfigureOption(config_options, iter->first, iter->second)
+ .ok()) {
+ found_one = true;
+ iter = unused.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+ }
+ ASSERT_EQ(0, unused.size());
+ ASSERT_TRUE(base->AreEquivalent(config_options, copy.get(), &mismatch));
+}
+
+TEST_P(ConfigurableParamTest, GetDefaultOptionsTest) {
+ TestConfigureOptions(config_options_);
+}
+
+TEST_P(ConfigurableParamTest, ConfigureFromPropsTest) {
+ std::string opt_str, mismatch;
+ std::unordered_set<std::string> names;
+ std::unique_ptr<Configurable> copy(CreateConfigurable());
+
+ ASSERT_OK(object_->ConfigureFromString(config_options_, configuration_));
+ config_options_.delimiter = "\n";
+ ASSERT_OK(object_->GetOptionString(config_options_, &opt_str));
+ std::istringstream iss(opt_str);
+ std::unordered_map<std::string, std::string> copy_map;
+ std::string line;
+ 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));
+ copy_map[name] = value;
+ }
+ ASSERT_OK(copy->ConfigureFromMap(config_options_, copy_map));
+ ASSERT_TRUE(object_->AreEquivalent(config_options_, copy.get(), &mismatch));
+}
+
+INSTANTIATE_TEST_CASE_P(
+ ParamTest, ConfigurableParamTest,
+ testing::Values(
+ std::pair<std::string, std::string>("Simple",
+ "int=42;bool=true;string=s"),
+ std::pair<std::string, std::string>(
+ "Mutable", "int=42;unique={int=33;string=unique}"),
+ std::pair<std::string, std::string>(
+ "Struct", "struct={int=33;bool=true;string=s;}"),
+ std::pair<std::string, std::string>("Shared",
+ "int=33;bool=true;string=outer;"
+ "shared={int=42;string=shared}"),
+ std::pair<std::string, std::string>("Unique",
+ "int=33;bool=true;string=outer;"
+ "unique={int=42;string=unique}"),
+ std::pair<std::string, std::string>("Nested",
+ "int=11;bool=true;string=outer;"
+ "pointer={int=22;string=pointer};"
+ "unique={int=33;string=unique};"
+ "shared={int=44;string=shared}"),
+ std::pair<std::string, std::string>("ThreeDeep",
+ "int=11;bool=true;string=outer;"
+ "unique={int=22;string=inner;"
+ "unique={int=33;string=unique}};"),
+ std::pair<std::string, std::string>("DBOptions",
+ "max_background_jobs=100;"
+ "max_open_files=200;"),
+ std::pair<std::string, std::string>("CFOptions",
+ "table_factory=BlockBasedTable;"
+ "disable_auto_compactions=true;"),
+ std::pair<std::string, std::string>("BlockBased",
+ "block_size=1024;"
+ "no_block_cache=true;")));
+#endif // ROCKSDB_LITE
+
+} // namespace test
+} // namespace ROCKSDB_NAMESPACE
+int main(int argc, char** argv) {
+ ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
+ ::testing::InitGoogleTest(&argc, argv);
+#ifdef GFLAGS
+ ParseCommandLineFlags(&argc, &argv, true);
+#endif // GFLAGS
+ return RUN_ALL_TESTS();
+}