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