1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
5 //
6 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
9
10 #include "options/configurable_test.h"
11
12 #include <cctype>
13 #include <cinttypes>
14 #include <cstring>
15 #include <unordered_map>
16
17 #include "options/configurable_helper.h"
18 #include "options/options_helper.h"
19 #include "options/options_parser.h"
20 #include "rocksdb/configurable.h"
21 #include "test_util/testharness.h"
22 #include "test_util/testutil.h"
23
24 #ifndef GFLAGS
25 bool FLAGS_enable_print = false;
26 #else
27 #include "util/gflags_compat.h"
28 using GFLAGS_NAMESPACE::ParseCommandLineFlags;
29 DEFINE_bool(enable_print, false, "Print options generated to console.");
30 #endif // GFLAGS
31
32 namespace ROCKSDB_NAMESPACE {
33 namespace test {
34 class StringLogger : public Logger {
35 public:
36 using Logger::Logv;
Logv(const char * format,va_list ap)37 void Logv(const char* format, va_list ap) override {
38 char buffer[1000];
39 vsnprintf(buffer, sizeof(buffer), format, ap);
40 string_.append(buffer);
41 }
str() const42 const std::string& str() const { return string_; }
clear()43 void clear() { string_.clear(); }
44
45 private:
46 std::string string_;
47 };
48 static std::unordered_map<std::string, OptionTypeInfo> struct_option_info = {
49 #ifndef ROCKSDB_LITE
50 {"struct", OptionTypeInfo::Struct("struct", &simple_option_info, 0,
51 OptionVerificationType::kNormal,
52 OptionTypeFlags::kMutable)},
53 #endif // ROCKSDB_LITE
54 };
55
56 static std::unordered_map<std::string, OptionTypeInfo> imm_struct_option_info =
57 {
58 #ifndef ROCKSDB_LITE
59 {"struct", OptionTypeInfo::Struct("struct", &simple_option_info, 0,
60 OptionVerificationType::kNormal,
61 OptionTypeFlags::kNone)},
62 #endif // ROCKSDB_LITE
63 };
64
65 class SimpleConfigurable : public TestConfigurable<Configurable> {
66 public:
Create(const std::string & name="simple",int mode=TestConfigMode::kDefaultMode,const std::unordered_map<std::string,OptionTypeInfo> * map=& simple_option_info)67 static SimpleConfigurable* Create(
68 const std::string& name = "simple",
69 int mode = TestConfigMode::kDefaultMode,
70 const std::unordered_map<std::string, OptionTypeInfo>* map =
71 &simple_option_info) {
72 return new SimpleConfigurable(name, mode, map);
73 }
74
SimpleConfigurable(const std::string & name,int mode,const std::unordered_map<std::string,OptionTypeInfo> * map=& simple_option_info)75 SimpleConfigurable(const std::string& name, int mode,
76 const std::unordered_map<std::string, OptionTypeInfo>*
77 map = &simple_option_info)
78 : TestConfigurable(name, mode, map) {
79 if ((mode & TestConfigMode::kUniqueMode) != 0) {
80 unique_.reset(SimpleConfigurable::Create("Unique" + name_));
81 RegisterOptions(name_ + "Unique", &unique_, &unique_option_info);
82 }
83 if ((mode & TestConfigMode::kSharedMode) != 0) {
84 shared_.reset(SimpleConfigurable::Create("Shared" + name_));
85 RegisterOptions(name_ + "Shared", &shared_, &shared_option_info);
86 }
87 if ((mode & TestConfigMode::kRawPtrMode) != 0) {
88 pointer_ = SimpleConfigurable::Create("Pointer" + name_);
89 RegisterOptions(name_ + "Pointer", &pointer_, &pointer_option_info);
90 }
91 }
92
93 }; // End class SimpleConfigurable
94
95 using ConfigTestFactoryFunc = std::function<Configurable*()>;
96
97 class ConfigurableTest : public testing::Test {
98 public:
ConfigurableTest()99 ConfigurableTest() { config_options_.invoke_prepare_options = false; }
100
101 ConfigOptions config_options_;
102 };
103
TEST_F(ConfigurableTest,GetOptionsPtrTest)104 TEST_F(ConfigurableTest, GetOptionsPtrTest) {
105 std::string opt_str;
106 std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create());
107 ASSERT_NE(configurable->GetOptions<TestOptions>("simple"), nullptr);
108 ASSERT_EQ(configurable->GetOptions<TestOptions>("bad-opt"), nullptr);
109 }
110
TEST_F(ConfigurableTest,ConfigureFromMapTest)111 TEST_F(ConfigurableTest, ConfigureFromMapTest) {
112 std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create());
113 auto* opts = configurable->GetOptions<TestOptions>("simple");
114 ASSERT_OK(configurable->ConfigureFromMap(config_options_, {}));
115 ASSERT_NE(opts, nullptr);
116 #ifndef ROCKSDB_LITE
117 std::unordered_map<std::string, std::string> options_map = {
118 {"int", "1"}, {"bool", "true"}, {"string", "string"}};
119 ASSERT_OK(configurable->ConfigureFromMap(config_options_, options_map));
120 ASSERT_EQ(opts->i, 1);
121 ASSERT_EQ(opts->b, true);
122 ASSERT_EQ(opts->s, "string");
123 #endif
124 }
125
TEST_F(ConfigurableTest,ConfigureFromStringTest)126 TEST_F(ConfigurableTest, ConfigureFromStringTest) {
127 std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create());
128 auto* opts = configurable->GetOptions<TestOptions>("simple");
129 ASSERT_OK(configurable->ConfigureFromString(config_options_, ""));
130 ASSERT_NE(opts, nullptr);
131 #ifndef ROCKSDB_LITE // GetOptionsFromMap is not supported in ROCKSDB_LITE
132 ASSERT_OK(configurable->ConfigureFromString(config_options_,
133 "int=1;bool=true;string=s"));
134 ASSERT_EQ(opts->i, 1);
135 ASSERT_EQ(opts->b, true);
136 ASSERT_EQ(opts->s, "s");
137 #endif
138 }
139
140 #ifndef ROCKSDB_LITE // GetOptionsFromMap is not supported in ROCKSDB_LITE
TEST_F(ConfigurableTest,ConfigureIgnoreTest)141 TEST_F(ConfigurableTest, ConfigureIgnoreTest) {
142 std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create());
143 std::unordered_map<std::string, std::string> options_map = {{"unused", "u"}};
144 ConfigOptions ignore = config_options_;
145 ignore.ignore_unknown_options = true;
146 ASSERT_NOK(configurable->ConfigureFromMap(config_options_, options_map));
147 ASSERT_OK(configurable->ConfigureFromMap(ignore, options_map));
148 ASSERT_NOK(configurable->ConfigureFromString(config_options_, "unused=u"));
149 ASSERT_OK(configurable->ConfigureFromString(ignore, "unused=u"));
150 }
151
TEST_F(ConfigurableTest,ConfigureNestedOptionsTest)152 TEST_F(ConfigurableTest, ConfigureNestedOptionsTest) {
153 std::unique_ptr<Configurable> base, copy;
154 std::string opt_str;
155 std::string mismatch;
156
157 base.reset(SimpleConfigurable::Create("simple", TestConfigMode::kAllOptMode));
158 copy.reset(SimpleConfigurable::Create("simple", TestConfigMode::kAllOptMode));
159 ASSERT_OK(base->ConfigureFromString(config_options_,
160 "shared={int=10; string=10};"
161 "unique={int=20; string=20};"
162 "pointer={int=30; string=30};"));
163 ASSERT_OK(base->GetOptionString(config_options_, &opt_str));
164 ASSERT_OK(copy->ConfigureFromString(config_options_, opt_str));
165 ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
166 }
167
TEST_F(ConfigurableTest,GetOptionsTest)168 TEST_F(ConfigurableTest, GetOptionsTest) {
169 std::unique_ptr<Configurable> simple;
170
171 simple.reset(
172 SimpleConfigurable::Create("simple", TestConfigMode::kAllOptMode));
173 int i = 11;
174 for (auto opt : {"", "shared.", "unique.", "pointer."}) {
175 std::string value;
176 std::string expected = ToString(i);
177 std::string opt_name = opt;
178 ASSERT_OK(
179 simple->ConfigureOption(config_options_, opt_name + "int", expected));
180 ASSERT_OK(simple->GetOption(config_options_, opt_name + "int", &value));
181 ASSERT_EQ(expected, value);
182 ASSERT_OK(simple->ConfigureOption(config_options_, opt_name + "string",
183 expected));
184 ASSERT_OK(simple->GetOption(config_options_, opt_name + "string", &value));
185 ASSERT_EQ(expected, value);
186
187 ASSERT_NOK(
188 simple->ConfigureOption(config_options_, opt_name + "bad", expected));
189 ASSERT_NOK(simple->GetOption(config_options_, "bad option", &value));
190 ASSERT_TRUE(value.empty());
191 i += 11;
192 }
193 }
194
TEST_F(ConfigurableTest,ConfigureBadOptionsTest)195 TEST_F(ConfigurableTest, ConfigureBadOptionsTest) {
196 std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create());
197 auto* opts = configurable->GetOptions<TestOptions>("simple");
198 ASSERT_NE(opts, nullptr);
199 ASSERT_OK(configurable->ConfigureOption(config_options_, "int", "42"));
200 ASSERT_EQ(opts->i, 42);
201 ASSERT_NOK(configurable->ConfigureOption(config_options_, "int", "fred"));
202 ASSERT_NOK(configurable->ConfigureOption(config_options_, "bool", "fred"));
203 ASSERT_NOK(
204 configurable->ConfigureFromString(config_options_, "int=33;unused=u"));
205 ASSERT_EQ(opts->i, 42);
206 }
207
TEST_F(ConfigurableTest,InvalidOptionTest)208 TEST_F(ConfigurableTest, InvalidOptionTest) {
209 std::unique_ptr<Configurable> configurable(SimpleConfigurable::Create());
210 std::unordered_map<std::string, std::string> options_map = {
211 {"bad-option", "bad"}};
212 ASSERT_NOK(configurable->ConfigureFromMap(config_options_, options_map));
213 ASSERT_NOK(
214 configurable->ConfigureFromString(config_options_, "bad-option=bad"));
215 ASSERT_NOK(
216 configurable->ConfigureOption(config_options_, "bad-option", "bad"));
217 }
218
219 static std::unordered_map<std::string, OptionTypeInfo> validated_option_info = {
220 #ifndef ROCKSDB_LITE
221 {"validated",
222 {0, OptionType::kBoolean, OptionVerificationType::kNormal,
223 OptionTypeFlags::kNone}},
224 #endif // ROCKSDB_LITE
225 };
226 static std::unordered_map<std::string, OptionTypeInfo> prepared_option_info = {
227 #ifndef ROCKSDB_LITE
228 {"prepared",
229 {0, OptionType::kInt, OptionVerificationType::kNormal,
230 OptionTypeFlags::kMutable}},
231 #endif // ROCKSDB_LITE
232 };
233 static std::unordered_map<std::string, OptionTypeInfo>
234 dont_prepare_option_info = {
235 #ifndef ROCKSDB_LITE
236 {"unique",
237 {0, OptionType::kConfigurable, OptionVerificationType::kNormal,
238 (OptionTypeFlags::kUnique | OptionTypeFlags::kDontPrepare)}},
239
240 #endif // ROCKSDB_LITE
241 };
242
243 class ValidatedConfigurable : public SimpleConfigurable {
244 public:
ValidatedConfigurable(const std::string & name,unsigned char mode,bool dont_prepare=false)245 ValidatedConfigurable(const std::string& name, unsigned char mode,
246 bool dont_prepare = false)
247 : SimpleConfigurable(name, TestConfigMode::kDefaultMode),
248 validated(false),
249 prepared(0) {
250 RegisterOptions("Validated", &validated, &validated_option_info);
251 RegisterOptions("Prepared", &prepared, &prepared_option_info);
252 if ((mode & TestConfigMode::kUniqueMode) != 0) {
253 unique_.reset(new ValidatedConfigurable(
254 "Unique" + name_, TestConfigMode::kDefaultMode, false));
255 if (dont_prepare) {
256 RegisterOptions(name_ + "Unique", &unique_, &dont_prepare_option_info);
257 } else {
258 RegisterOptions(name_ + "Unique", &unique_, &unique_option_info);
259 }
260 }
261 }
262
PrepareOptions(const ConfigOptions & config_options)263 Status PrepareOptions(const ConfigOptions& config_options) override {
264 if (++prepared <= 0) {
265 return Status::InvalidArgument("Cannot prepare option");
266 } else {
267 return SimpleConfigurable::PrepareOptions(config_options);
268 }
269 }
270
ValidateOptions(const DBOptions & db_opts,const ColumnFamilyOptions & cf_opts) const271 Status ValidateOptions(const DBOptions& db_opts,
272 const ColumnFamilyOptions& cf_opts) const override {
273 if (!validated) {
274 return Status::InvalidArgument("Not Validated");
275 } else {
276 return SimpleConfigurable::ValidateOptions(db_opts, cf_opts);
277 }
278 }
279
280 private:
281 bool validated;
282 int prepared;
283 };
284
TEST_F(ConfigurableTest,ValidateOptionsTest)285 TEST_F(ConfigurableTest, ValidateOptionsTest) {
286 std::unique_ptr<Configurable> configurable(
287 new ValidatedConfigurable("validated", TestConfigMode::kDefaultMode));
288 ColumnFamilyOptions cf_opts;
289 DBOptions db_opts;
290 ASSERT_OK(
291 configurable->ConfigureOption(config_options_, "validated", "false"));
292 ASSERT_NOK(configurable->ValidateOptions(db_opts, cf_opts));
293 ASSERT_OK(
294 configurable->ConfigureOption(config_options_, "validated", "true"));
295 ASSERT_OK(configurable->ValidateOptions(db_opts, cf_opts));
296 }
297
TEST_F(ConfigurableTest,PrepareOptionsTest)298 TEST_F(ConfigurableTest, PrepareOptionsTest) {
299 std::unique_ptr<Configurable> c(
300 new ValidatedConfigurable("Simple", TestConfigMode::kUniqueMode, false));
301 auto cp = c->GetOptions<int>("Prepared");
302 auto u = c->GetOptions<std::unique_ptr<Configurable>>("SimpleUnique");
303 auto up = u->get()->GetOptions<int>("Prepared");
304 config_options_.invoke_prepare_options = false;
305
306 ASSERT_NE(cp, nullptr);
307 ASSERT_NE(up, nullptr);
308 ASSERT_EQ(*cp, 0);
309 ASSERT_EQ(*up, 0);
310 ASSERT_OK(c->ConfigureFromMap(config_options_, {}));
311 ASSERT_EQ(*cp, 0);
312 ASSERT_EQ(*up, 0);
313 config_options_.invoke_prepare_options = true;
314 ASSERT_OK(c->ConfigureFromMap(config_options_, {}));
315 ASSERT_EQ(*cp, 1);
316 ASSERT_EQ(*up, 1);
317 ASSERT_OK(c->ConfigureFromString(config_options_, "prepared=0"));
318 ASSERT_EQ(*up, 2);
319 ASSERT_EQ(*cp, 1);
320
321 ASSERT_NOK(c->ConfigureFromString(config_options_, "prepared=-2"));
322
323 c.reset(
324 new ValidatedConfigurable("Simple", TestConfigMode::kUniqueMode, true));
325 cp = c->GetOptions<int>("Prepared");
326 u = c->GetOptions<std::unique_ptr<Configurable>>("SimpleUnique");
327 up = u->get()->GetOptions<int>("Prepared");
328
329 ASSERT_OK(c->ConfigureFromString(config_options_, "prepared=0"));
330 ASSERT_EQ(*cp, 1);
331 ASSERT_EQ(*up, 0);
332 }
333
TEST_F(ConfigurableTest,CopyObjectTest)334 TEST_F(ConfigurableTest, CopyObjectTest) {
335 class CopyConfigurable : public Configurable {
336 public:
337 CopyConfigurable() : prepared_(0), validated_(0) {}
338 Status PrepareOptions(const ConfigOptions& options) override {
339 prepared_++;
340 return Configurable::PrepareOptions(options);
341 }
342 Status ValidateOptions(const DBOptions& db_opts,
343 const ColumnFamilyOptions& cf_opts) const override {
344 validated_++;
345 return Configurable::ValidateOptions(db_opts, cf_opts);
346 }
347 int prepared_;
348 mutable int validated_;
349 };
350
351 CopyConfigurable c1;
352 ConfigOptions config_options;
353 Options options;
354
355 ASSERT_OK(c1.PrepareOptions(config_options));
356 ASSERT_OK(c1.ValidateOptions(options, options));
357 ASSERT_EQ(c1.prepared_, 1);
358 ASSERT_EQ(c1.validated_, 1);
359 CopyConfigurable c2 = c1;
360 ASSERT_OK(c1.PrepareOptions(config_options));
361 ASSERT_OK(c1.ValidateOptions(options, options));
362 ASSERT_EQ(c2.prepared_, 1);
363 ASSERT_EQ(c2.validated_, 1);
364 ASSERT_EQ(c1.prepared_, 2);
365 ASSERT_EQ(c1.validated_, 2);
366 }
367
TEST_F(ConfigurableTest,MutableOptionsTest)368 TEST_F(ConfigurableTest, MutableOptionsTest) {
369 static std::unordered_map<std::string, OptionTypeInfo> imm_option_info = {
370 #ifndef ROCKSDB_LITE
371 {"imm", OptionTypeInfo::Struct("imm", &simple_option_info, 0,
372 OptionVerificationType::kNormal,
373 OptionTypeFlags::kNone)},
374 #endif // ROCKSDB_LITE
375 };
376
377 class MutableConfigurable : public SimpleConfigurable {
378 public:
379 MutableConfigurable()
380 : SimpleConfigurable("mutable", TestConfigMode::kDefaultMode |
381 TestConfigMode::kUniqueMode |
382 TestConfigMode::kSharedMode) {
383 RegisterOptions("struct", &options_, &struct_option_info);
384 RegisterOptions("imm", &options_, &imm_option_info);
385 }
386 };
387 MutableConfigurable mc;
388 ConfigOptions options = config_options_;
389
390 ASSERT_OK(mc.ConfigureOption(options, "bool", "true"));
391 ASSERT_OK(mc.ConfigureOption(options, "int", "42"));
392 auto* opts = mc.GetOptions<TestOptions>("mutable");
393 ASSERT_NE(opts, nullptr);
394 ASSERT_EQ(opts->i, 42);
395 ASSERT_EQ(opts->b, true);
396 ASSERT_OK(mc.ConfigureOption(options, "struct", "{bool=false;}"));
397 ASSERT_OK(mc.ConfigureOption(options, "imm", "{int=55;}"));
398
399 options.mutable_options_only = true;
400
401 // Now only mutable options should be settable.
402 ASSERT_NOK(mc.ConfigureOption(options, "bool", "true"));
403 ASSERT_OK(mc.ConfigureOption(options, "int", "24"));
404 ASSERT_EQ(opts->i, 24);
405 ASSERT_EQ(opts->b, false);
406 ASSERT_NOK(mc.ConfigureFromString(options, "bool=false;int=33;"));
407 ASSERT_EQ(opts->i, 24);
408 ASSERT_EQ(opts->b, false);
409
410 // Setting options through an immutable struct fails
411 ASSERT_NOK(mc.ConfigureOption(options, "imm", "{int=55;}"));
412 ASSERT_NOK(mc.ConfigureOption(options, "imm.int", "55"));
413 ASSERT_EQ(opts->i, 24);
414 ASSERT_EQ(opts->b, false);
415
416 // Setting options through an mutable struct succeeds
417 ASSERT_OK(mc.ConfigureOption(options, "struct", "{int=44;}"));
418 ASSERT_EQ(opts->i, 44);
419 ASSERT_OK(mc.ConfigureOption(options, "struct.int", "55"));
420 ASSERT_EQ(opts->i, 55);
421
422 // Setting nested immutable configurable options fail
423 ASSERT_NOK(mc.ConfigureOption(options, "shared", "{bool=true;}"));
424 ASSERT_NOK(mc.ConfigureOption(options, "shared.bool", "true"));
425
426 // Setting nested mutable configurable options succeeds
427 ASSERT_OK(mc.ConfigureOption(options, "unique", "{bool=true}"));
428 ASSERT_OK(mc.ConfigureOption(options, "unique.bool", "true"));
429 }
430
TEST_F(ConfigurableTest,DeprecatedOptionsTest)431 TEST_F(ConfigurableTest, DeprecatedOptionsTest) {
432 static std::unordered_map<std::string, OptionTypeInfo>
433 deprecated_option_info = {
434 {"deprecated",
435 {offsetof(struct TestOptions, b), OptionType::kBoolean,
436 OptionVerificationType::kDeprecated, OptionTypeFlags::kNone}}};
437 std::unique_ptr<Configurable> orig;
438 orig.reset(SimpleConfigurable::Create("simple", TestConfigMode::kDefaultMode,
439 &deprecated_option_info));
440 auto* opts = orig->GetOptions<TestOptions>("simple");
441 ASSERT_NE(opts, nullptr);
442 opts->d = true;
443 ASSERT_OK(orig->ConfigureOption(config_options_, "deprecated", "false"));
444 ASSERT_TRUE(opts->d);
445 ASSERT_OK(orig->ConfigureFromString(config_options_, "deprecated=false"));
446 ASSERT_TRUE(opts->d);
447 }
448
TEST_F(ConfigurableTest,AliasOptionsTest)449 TEST_F(ConfigurableTest, AliasOptionsTest) {
450 static std::unordered_map<std::string, OptionTypeInfo> alias_option_info = {
451 {"bool",
452 {offsetof(struct TestOptions, b), OptionType::kBoolean,
453 OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
454 {"alias",
455 {offsetof(struct TestOptions, b), OptionType::kBoolean,
456 OptionVerificationType::kAlias, OptionTypeFlags::kNone, 0}}};
457 std::unique_ptr<Configurable> orig;
458 orig.reset(SimpleConfigurable::Create("simple", TestConfigMode::kDefaultMode,
459 &alias_option_info));
460 auto* opts = orig->GetOptions<TestOptions>("simple");
461 ASSERT_NE(opts, nullptr);
462 ASSERT_OK(orig->ConfigureOption(config_options_, "bool", "false"));
463 ASSERT_FALSE(opts->b);
464 ASSERT_OK(orig->ConfigureOption(config_options_, "alias", "true"));
465 ASSERT_TRUE(opts->b);
466 std::string opts_str;
467 ASSERT_OK(orig->GetOptionString(config_options_, &opts_str));
468 ASSERT_EQ(opts_str.find("alias"), std::string::npos);
469
470 ASSERT_OK(orig->ConfigureOption(config_options_, "bool", "false"));
471 ASSERT_FALSE(opts->b);
472 ASSERT_OK(orig->GetOption(config_options_, "alias", &opts_str));
473 ASSERT_EQ(opts_str, "false");
474 }
475
TEST_F(ConfigurableTest,NestedUniqueConfigTest)476 TEST_F(ConfigurableTest, NestedUniqueConfigTest) {
477 std::unique_ptr<Configurable> simple;
478 simple.reset(
479 SimpleConfigurable::Create("Outer", TestConfigMode::kAllOptMode));
480 const auto outer = simple->GetOptions<TestOptions>("Outer");
481 const auto unique =
482 simple->GetOptions<std::unique_ptr<Configurable>>("OuterUnique");
483 ASSERT_NE(outer, nullptr);
484 ASSERT_NE(unique, nullptr);
485 ASSERT_OK(
486 simple->ConfigureFromString(config_options_, "int=24;string=outer"));
487 ASSERT_OK(simple->ConfigureFromString(config_options_,
488 "unique={int=42;string=nested}"));
489 const auto inner = unique->get()->GetOptions<TestOptions>("UniqueOuter");
490 ASSERT_NE(inner, nullptr);
491 ASSERT_EQ(outer->i, 24);
492 ASSERT_EQ(outer->s, "outer");
493 ASSERT_EQ(inner->i, 42);
494 ASSERT_EQ(inner->s, "nested");
495 }
496
TEST_F(ConfigurableTest,NestedSharedConfigTest)497 TEST_F(ConfigurableTest, NestedSharedConfigTest) {
498 std::unique_ptr<Configurable> simple;
499 simple.reset(SimpleConfigurable::Create(
500 "Outer", TestConfigMode::kDefaultMode | TestConfigMode::kSharedMode));
501 ASSERT_OK(
502 simple->ConfigureFromString(config_options_, "int=24;string=outer"));
503 ASSERT_OK(simple->ConfigureFromString(config_options_,
504 "shared={int=42;string=nested}"));
505 const auto outer = simple->GetOptions<TestOptions>("Outer");
506 const auto shared =
507 simple->GetOptions<std::shared_ptr<Configurable>>("OuterShared");
508 ASSERT_NE(outer, nullptr);
509 ASSERT_NE(shared, nullptr);
510 const auto inner = shared->get()->GetOptions<TestOptions>("SharedOuter");
511 ASSERT_NE(inner, nullptr);
512 ASSERT_EQ(outer->i, 24);
513 ASSERT_EQ(outer->s, "outer");
514 ASSERT_EQ(inner->i, 42);
515 ASSERT_EQ(inner->s, "nested");
516 }
517
TEST_F(ConfigurableTest,NestedRawConfigTest)518 TEST_F(ConfigurableTest, NestedRawConfigTest) {
519 std::unique_ptr<Configurable> simple;
520 simple.reset(SimpleConfigurable::Create(
521 "Outer", TestConfigMode::kDefaultMode | TestConfigMode::kRawPtrMode));
522 ASSERT_OK(
523 simple->ConfigureFromString(config_options_, "int=24;string=outer"));
524 ASSERT_OK(simple->ConfigureFromString(config_options_,
525 "pointer={int=42;string=nested}"));
526 const auto outer = simple->GetOptions<TestOptions>("Outer");
527 const auto pointer = simple->GetOptions<Configurable*>("OuterPointer");
528 ASSERT_NE(outer, nullptr);
529 ASSERT_NE(pointer, nullptr);
530 const auto inner = (*pointer)->GetOptions<TestOptions>("PointerOuter");
531 ASSERT_NE(inner, nullptr);
532 ASSERT_EQ(outer->i, 24);
533 ASSERT_EQ(outer->s, "outer");
534 ASSERT_EQ(inner->i, 42);
535 ASSERT_EQ(inner->s, "nested");
536 }
537
TEST_F(ConfigurableTest,MatchesTest)538 TEST_F(ConfigurableTest, MatchesTest) {
539 std::string mismatch;
540 std::unique_ptr<Configurable> base, copy;
541 base.reset(SimpleConfigurable::Create(
542 "simple", TestConfigMode::kDefaultMode | TestConfigMode::kNestedMode));
543 copy.reset(SimpleConfigurable::Create(
544 "simple", TestConfigMode::kDefaultMode | TestConfigMode::kNestedMode));
545 ASSERT_OK(base->ConfigureFromString(
546 config_options_,
547 "int=11;string=outer;unique={int=22;string=u};shared={int=33;string=s}"));
548 ASSERT_OK(copy->ConfigureFromString(
549 config_options_,
550 "int=11;string=outer;unique={int=22;string=u};shared={int=33;string=s}"));
551 ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
552 ASSERT_OK(base->ConfigureOption(config_options_, "shared", "int=44"));
553 ASSERT_FALSE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
554 ASSERT_EQ(mismatch, "shared.int");
555 std::string c1value, c2value;
556 ASSERT_OK(base->GetOption(config_options_, mismatch, &c1value));
557 ASSERT_OK(copy->GetOption(config_options_, mismatch, &c2value));
558 ASSERT_NE(c1value, c2value);
559 }
560
SimpleStructFactory()561 static Configurable* SimpleStructFactory() {
562 return SimpleConfigurable::Create(
563 "simple-struct", TestConfigMode::kDefaultMode, &struct_option_info);
564 }
565
TEST_F(ConfigurableTest,ConfigureStructTest)566 TEST_F(ConfigurableTest, ConfigureStructTest) {
567 std::unique_ptr<Configurable> base(SimpleStructFactory());
568 std::unique_ptr<Configurable> copy(SimpleStructFactory());
569 std::string opt_str, value;
570 std::string mismatch;
571 std::unordered_set<std::string> names;
572
573 ASSERT_OK(
574 base->ConfigureFromString(config_options_, "struct={int=10; string=10}"));
575 ASSERT_OK(base->GetOptionString(config_options_, &opt_str));
576 ASSERT_OK(copy->ConfigureFromString(config_options_, opt_str));
577 ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
578 ASSERT_OK(base->GetOptionNames(config_options_, &names));
579 ASSERT_EQ(names.size(), 1);
580 ASSERT_EQ(*(names.begin()), "struct");
581 ASSERT_OK(
582 base->ConfigureFromString(config_options_, "struct={int=20; string=20}"));
583 ASSERT_OK(base->GetOption(config_options_, "struct", &value));
584 ASSERT_OK(copy->ConfigureOption(config_options_, "struct", value));
585 ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
586
587 ASSERT_NOK(base->ConfigureFromString(config_options_,
588 "struct={int=10; string=10; bad=11}"));
589 ASSERT_OK(base->ConfigureOption(config_options_, "struct.int", "42"));
590 ASSERT_NOK(base->ConfigureOption(config_options_, "struct.bad", "42"));
591 ASSERT_NOK(base->GetOption(config_options_, "struct.bad", &value));
592 ASSERT_OK(base->GetOption(config_options_, "struct.int", &value));
593 ASSERT_EQ(value, "42");
594 }
595
TEST_F(ConfigurableTest,ConfigurableEnumTest)596 TEST_F(ConfigurableTest, ConfigurableEnumTest) {
597 std::unique_ptr<Configurable> base, copy;
598 base.reset(SimpleConfigurable::Create("e", TestConfigMode::kEnumMode));
599 copy.reset(SimpleConfigurable::Create("e", TestConfigMode::kEnumMode));
600
601 std::string opts_str;
602 std::string mismatch;
603
604 ASSERT_OK(base->ConfigureFromString(config_options_, "enum=B"));
605 ASSERT_FALSE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
606 ASSERT_OK(base->GetOptionString(config_options_, &opts_str));
607 ASSERT_OK(copy->ConfigureFromString(config_options_, opts_str));
608 ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
609 ASSERT_NOK(base->ConfigureOption(config_options_, "enum", "bad"));
610 ASSERT_NOK(base->ConfigureOption(config_options_, "unknown", "bad"));
611 }
612
613 #ifndef ROCKSDB_LITE
614 static std::unordered_map<std::string, OptionTypeInfo> noserialize_option_info =
615 {
616 {"int",
617 {offsetof(struct TestOptions, i), OptionType::kInt,
618 OptionVerificationType::kNormal, OptionTypeFlags::kDontSerialize}},
619 };
620
TEST_F(ConfigurableTest,TestNoSerialize)621 TEST_F(ConfigurableTest, TestNoSerialize) {
622 std::unique_ptr<Configurable> base;
623 base.reset(SimpleConfigurable::Create("c", TestConfigMode::kDefaultMode,
624 &noserialize_option_info));
625 std::string opts_str, value;
626 ASSERT_OK(base->ConfigureFromString(config_options_, "int=10"));
627 ASSERT_OK(base->GetOptionString(config_options_, &opts_str));
628 ASSERT_EQ(opts_str, "");
629 ASSERT_NOK(base->GetOption(config_options_, "int", &value));
630 }
631
TEST_F(ConfigurableTest,TestNoCompare)632 TEST_F(ConfigurableTest, TestNoCompare) {
633 std::unordered_map<std::string, OptionTypeInfo> nocomp_option_info = {
634 {"int",
635 {offsetof(struct TestOptions, i), OptionType::kInt,
636 OptionVerificationType::kNormal, OptionTypeFlags::kCompareNever}},
637 };
638 std::unordered_map<std::string, OptionTypeInfo> normal_option_info = {
639 {"int",
640 {offsetof(struct TestOptions, i), OptionType::kInt,
641 OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
642 };
643
644 std::unique_ptr<Configurable> base, copy;
645 base.reset(SimpleConfigurable::Create("c", TestConfigMode::kDefaultMode,
646 &nocomp_option_info));
647 copy.reset(SimpleConfigurable::Create("c", TestConfigMode::kDefaultMode,
648 &normal_option_info));
649 ASSERT_OK(base->ConfigureFromString(config_options_, "int=10"));
650 ASSERT_OK(copy->ConfigureFromString(config_options_, "int=20"));
651 std::string bvalue, cvalue, mismatch;
652 ASSERT_OK(base->GetOption(config_options_, "int", &bvalue));
653 ASSERT_OK(copy->GetOption(config_options_, "int", &cvalue));
654 ASSERT_EQ(bvalue, "10");
655 ASSERT_EQ(cvalue, "20");
656 ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
657 ASSERT_FALSE(copy->AreEquivalent(config_options_, base.get(), &mismatch));
658 }
659 #endif
660
661 static std::unordered_map<std::string, ConfigTestFactoryFunc> TestFactories = {
__anon80bdaae10102() 662 {"Simple", []() { return SimpleConfigurable::Create("simple"); }},
__anon80bdaae10202() 663 {"Struct", []() { return SimpleStructFactory(); }},
664 {"Unique",
__anon80bdaae10302() 665 []() {
666 return SimpleConfigurable::Create(
667 "simple", TestConfigMode::kSimpleMode | TestConfigMode::kUniqueMode);
668 }},
669 {"Shared",
__anon80bdaae10402() 670 []() {
671 return SimpleConfigurable::Create(
672 "simple", TestConfigMode::kSimpleMode | TestConfigMode::kSharedMode);
673 }},
674 {"Nested",
__anon80bdaae10502() 675 []() {
676 return SimpleConfigurable::Create(
677 "simple", TestConfigMode::kSimpleMode | TestConfigMode::kNestedMode);
678 }},
679 {"Mutable",
__anon80bdaae10602() 680 []() {
681 return SimpleConfigurable::Create("simple",
682 TestConfigMode::kMutableMode |
683 TestConfigMode::kSimpleMode |
684 TestConfigMode::kNestedMode);
685 }},
686 {"ThreeDeep",
__anon80bdaae10702() 687 []() {
688 Configurable* simple = SimpleConfigurable::Create(
689 "Simple",
690 TestConfigMode::kUniqueMode | TestConfigMode::kDefaultMode);
691 auto* unique =
692 simple->GetOptions<std::unique_ptr<Configurable>>("SimpleUnique");
693 unique->reset(SimpleConfigurable::Create(
694 "Child",
695 TestConfigMode::kUniqueMode | TestConfigMode::kDefaultMode));
696 unique = unique->get()->GetOptions<std::unique_ptr<Configurable>>(
697 "ChildUnique");
698 unique->reset(
699 SimpleConfigurable::Create("Child", TestConfigMode::kDefaultMode));
700 return simple;
701 }},
702 {"DBOptions",
__anon80bdaae10802() 703 []() {
704 auto config = DBOptionsAsConfigurable(DBOptions());
705 return config.release();
706 }},
707 {"CFOptions",
__anon80bdaae10902() 708 []() {
709 auto config = CFOptionsAsConfigurable(ColumnFamilyOptions());
710 return config.release();
711 }},
__anon80bdaae10a02() 712 {"BlockBased", []() { return NewBlockBasedTableFactory(); }},
713 };
714
715 class ConfigurableParamTest : public ConfigurableTest,
716 virtual public ::testing::WithParamInterface<
717 std::pair<std::string, std::string>> {
718 public:
ConfigurableParamTest()719 ConfigurableParamTest() {
720 type_ = GetParam().first;
721 configuration_ = GetParam().second;
722 assert(TestFactories.find(type_) != TestFactories.end());
723 object_.reset(CreateConfigurable());
724 }
725
CreateConfigurable()726 Configurable* CreateConfigurable() {
727 const auto& iter = TestFactories.find(type_);
728 return (iter->second)();
729 }
730
731 void TestConfigureOptions(const ConfigOptions& opts);
732 std::string type_;
733 std::string configuration_;
734 std::unique_ptr<Configurable> object_;
735 };
736
TestConfigureOptions(const ConfigOptions & config_options)737 void ConfigurableParamTest::TestConfigureOptions(
738 const ConfigOptions& config_options) {
739 std::unique_ptr<Configurable> base, copy;
740 std::unordered_set<std::string> names;
741 std::string opt_str, mismatch;
742
743 base.reset(CreateConfigurable());
744 copy.reset(CreateConfigurable());
745
746 ASSERT_OK(base->ConfigureFromString(config_options, configuration_));
747 ASSERT_OK(base->GetOptionString(config_options, &opt_str));
748 ASSERT_OK(copy->ConfigureFromString(config_options, opt_str));
749 ASSERT_OK(copy->GetOptionString(config_options, &opt_str));
750 ASSERT_TRUE(base->AreEquivalent(config_options, copy.get(), &mismatch));
751
752 copy.reset(CreateConfigurable());
753 ASSERT_OK(base->GetOptionNames(config_options, &names));
754 std::unordered_map<std::string, std::string> unused;
755 bool found_one = false;
756 for (auto name : names) {
757 std::string value;
758 Status s = base->GetOption(config_options, name, &value);
759 if (s.ok()) {
760 s = copy->ConfigureOption(config_options, name, value);
761 if (s.ok() || s.IsNotSupported()) {
762 found_one = true;
763 } else {
764 unused[name] = value;
765 }
766 } else {
767 ASSERT_TRUE(s.IsNotSupported());
768 }
769 }
770 ASSERT_TRUE(found_one || names.empty());
771 while (found_one && !unused.empty()) {
772 found_one = false;
773 for (auto iter = unused.begin(); iter != unused.end();) {
774 if (copy->ConfigureOption(config_options, iter->first, iter->second)
775 .ok()) {
776 found_one = true;
777 iter = unused.erase(iter);
778 } else {
779 ++iter;
780 }
781 }
782 }
783 ASSERT_EQ(0, unused.size());
784 ASSERT_TRUE(base->AreEquivalent(config_options, copy.get(), &mismatch));
785 }
786
TEST_P(ConfigurableParamTest,GetDefaultOptionsTest)787 TEST_P(ConfigurableParamTest, GetDefaultOptionsTest) {
788 TestConfigureOptions(config_options_);
789 }
790
TEST_P(ConfigurableParamTest,ConfigureFromPropsTest)791 TEST_P(ConfigurableParamTest, ConfigureFromPropsTest) {
792 std::string opt_str, mismatch;
793 std::unordered_set<std::string> names;
794 std::unique_ptr<Configurable> copy(CreateConfigurable());
795
796 ASSERT_OK(object_->ConfigureFromString(config_options_, configuration_));
797 config_options_.delimiter = "\n";
798 ASSERT_OK(object_->GetOptionString(config_options_, &opt_str));
799 std::istringstream iss(opt_str);
800 std::unordered_map<std::string, std::string> copy_map;
801 std::string line;
802 for (int line_num = 0; std::getline(iss, line); line_num++) {
803 std::string name;
804 std::string value;
805 ASSERT_OK(
806 RocksDBOptionsParser::ParseStatement(&name, &value, line, line_num));
807 copy_map[name] = value;
808 }
809 ASSERT_OK(copy->ConfigureFromMap(config_options_, copy_map));
810 ASSERT_TRUE(object_->AreEquivalent(config_options_, copy.get(), &mismatch));
811 }
812
813 INSTANTIATE_TEST_CASE_P(
814 ParamTest, ConfigurableParamTest,
815 testing::Values(
816 std::pair<std::string, std::string>("Simple",
817 "int=42;bool=true;string=s"),
818 std::pair<std::string, std::string>(
819 "Mutable", "int=42;unique={int=33;string=unique}"),
820 std::pair<std::string, std::string>(
821 "Struct", "struct={int=33;bool=true;string=s;}"),
822 std::pair<std::string, std::string>("Shared",
823 "int=33;bool=true;string=outer;"
824 "shared={int=42;string=shared}"),
825 std::pair<std::string, std::string>("Unique",
826 "int=33;bool=true;string=outer;"
827 "unique={int=42;string=unique}"),
828 std::pair<std::string, std::string>("Nested",
829 "int=11;bool=true;string=outer;"
830 "pointer={int=22;string=pointer};"
831 "unique={int=33;string=unique};"
832 "shared={int=44;string=shared}"),
833 std::pair<std::string, std::string>("ThreeDeep",
834 "int=11;bool=true;string=outer;"
835 "unique={int=22;string=inner;"
836 "unique={int=33;string=unique}};"),
837 std::pair<std::string, std::string>("DBOptions",
838 "max_background_jobs=100;"
839 "max_open_files=200;"),
840 std::pair<std::string, std::string>("CFOptions",
841 "table_factory=BlockBasedTable;"
842 "disable_auto_compactions=true;"),
843 std::pair<std::string, std::string>("BlockBased",
844 "block_size=1024;"
845 "no_block_cache=true;")));
846 #endif // ROCKSDB_LITE
847
848 } // namespace test
849 } // namespace ROCKSDB_NAMESPACE
main(int argc,char ** argv)850 int main(int argc, char** argv) {
851 ::testing::InitGoogleTest(&argc, argv);
852 #ifdef GFLAGS
853 ParseCommandLineFlags(&argc, &argv, true);
854 #endif // GFLAGS
855 return RUN_ALL_TESTS();
856 }
857