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 "rocksdb/customizable.h"
11
12 #include <cctype>
13 #include <cinttypes>
14 #include <cstring>
15 #include <unordered_map>
16
17 #include "db/db_test_util.h"
18 #include "options/options_helper.h"
19 #include "options/options_parser.h"
20 #include "port/stack_trace.h"
21 #include "rocksdb/convenience.h"
22 #include "rocksdb/env_encryption.h"
23 #include "rocksdb/file_checksum.h"
24 #include "rocksdb/flush_block_policy.h"
25 #include "rocksdb/secondary_cache.h"
26 #include "rocksdb/slice_transform.h"
27 #include "rocksdb/sst_partitioner.h"
28 #include "rocksdb/statistics.h"
29 #include "rocksdb/utilities/customizable_util.h"
30 #include "rocksdb/utilities/object_registry.h"
31 #include "rocksdb/utilities/options_type.h"
32 #include "table/block_based/flush_block_policy.h"
33 #include "table/mock_table.h"
34 #include "test_util/mock_time_env.h"
35 #include "test_util/testharness.h"
36 #include "test_util/testutil.h"
37 #include "util/file_checksum_helper.h"
38 #include "util/string_util.h"
39 #include "utilities/compaction_filters/remove_emptyvalue_compactionfilter.h"
40
41 #ifndef GFLAGS
42 bool FLAGS_enable_print = false;
43 #else
44 #include "util/gflags_compat.h"
45 using GFLAGS_NAMESPACE::ParseCommandLineFlags;
46 DEFINE_bool(enable_print, false, "Print options generated to console.");
47 #endif // GFLAGS
48
49 namespace ROCKSDB_NAMESPACE {
50 namespace {
51 class StringLogger : public Logger {
52 public:
53 using Logger::Logv;
Logv(const char * format,va_list ap)54 void Logv(const char* format, va_list ap) override {
55 char buffer[1000];
56 vsnprintf(buffer, sizeof(buffer), format, ap);
57 string_.append(buffer);
58 }
str() const59 const std::string& str() const { return string_; }
clear()60 void clear() { string_.clear(); }
61
62 private:
63 std::string string_;
64 };
65
66 class TestCustomizable : public Customizable {
67 public:
TestCustomizable(const std::string & name)68 TestCustomizable(const std::string& name) : name_(name) {}
69 // Method to allow CheckedCast to work for this class
kClassName()70 static const char* kClassName() {
71 return "TestCustomizable";
72 }
73
Name() const74 const char* Name() const override { return name_.c_str(); }
Type()75 static const char* Type() { return "test.custom"; }
76 #ifndef ROCKSDB_LITE
77 static Status CreateFromString(const ConfigOptions& opts,
78 const std::string& value,
79 std::unique_ptr<TestCustomizable>* result);
80 static Status CreateFromString(const ConfigOptions& opts,
81 const std::string& value,
82 std::shared_ptr<TestCustomizable>* result);
83 static Status CreateFromString(const ConfigOptions& opts,
84 const std::string& value,
85 TestCustomizable** result);
86 #endif // ROCKSDB_LITE
IsInstanceOf(const std::string & name) const87 bool IsInstanceOf(const std::string& name) const override {
88 if (name == kClassName()) {
89 return true;
90 } else {
91 return Customizable::IsInstanceOf(name);
92 }
93 }
94
95 protected:
96 const std::string name_;
97 };
98
99 struct AOptions {
kNameROCKSDB_NAMESPACE::__anon251625620111::AOptions100 static const char* kName() { return "A"; }
101 int i = 0;
102 bool b = false;
103 };
104
105 static std::unordered_map<std::string, OptionTypeInfo> a_option_info = {
106 #ifndef ROCKSDB_LITE
107 {"int",
108 {offsetof(struct AOptions, i), OptionType::kInt,
109 OptionVerificationType::kNormal, OptionTypeFlags::kMutable}},
110 {"bool",
111 {offsetof(struct AOptions, b), OptionType::kBoolean,
112 OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
113 #endif // ROCKSDB_LITE
114 };
115
116 class ACustomizable : public TestCustomizable {
117 public:
ACustomizable(const std::string & id)118 explicit ACustomizable(const std::string& id)
119 : TestCustomizable("A"), id_(id) {
120 RegisterOptions(&opts_, &a_option_info);
121 }
GetId() const122 std::string GetId() const override { return id_; }
kClassName()123 static const char* kClassName() { return "A"; }
124
125 private:
126 AOptions opts_;
127 const std::string id_;
128 };
129
130 struct BOptions {
131 std::string s;
132 bool b = false;
133 };
134
135 static std::unordered_map<std::string, OptionTypeInfo> b_option_info = {
136 #ifndef ROCKSDB_LITE
137 {"string",
138 {offsetof(struct BOptions, s), OptionType::kString,
139 OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
140 {"bool",
141 {offsetof(struct BOptions, b), OptionType::kBoolean,
142 OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
143 #endif // ROCKSDB_LITE
144 };
145
146 class BCustomizable : public TestCustomizable {
147 private:
148 public:
BCustomizable(const std::string & name)149 explicit BCustomizable(const std::string& name) : TestCustomizable(name) {
150 RegisterOptions(name, &opts_, &b_option_info);
151 }
kClassName()152 static const char* kClassName() { return "B"; }
153
154 private:
155 BOptions opts_;
156 };
157
158 #ifndef ROCKSDB_LITE
LoadSharedB(const std::string & id,std::shared_ptr<TestCustomizable> * result)159 static bool LoadSharedB(const std::string& id,
160 std::shared_ptr<TestCustomizable>* result) {
161 if (id == "B") {
162 result->reset(new BCustomizable(id));
163 return true;
164 } else if (id.empty()) {
165 result->reset();
166 return true;
167 } else {
168 return false;
169 }
170 }
171
172 static int A_count = 0;
RegisterCustomTestObjects(ObjectLibrary & library,const std::string &)173 static int RegisterCustomTestObjects(ObjectLibrary& library,
174 const std::string& /*arg*/) {
175 library.Register<TestCustomizable>(
176 "A.*",
177 [](const std::string& name, std::unique_ptr<TestCustomizable>* guard,
178 std::string* /* msg */) {
179 guard->reset(new ACustomizable(name));
180 A_count++;
181 return guard->get();
182 });
183
184 library.Register<TestCustomizable>(
185 "S", [](const std::string& name,
186 std::unique_ptr<TestCustomizable>* /* guard */,
187 std::string* /* msg */) { return new BCustomizable(name); });
188 size_t num_types;
189 return static_cast<int>(library.GetFactoryCount(&num_types));
190 }
191 #endif // ROCKSDB_LITE
192
193 struct SimpleOptions {
kNameROCKSDB_NAMESPACE::__anon251625620111::SimpleOptions194 static const char* kName() { return "simple"; }
195 bool b = true;
196 std::unique_ptr<TestCustomizable> cu;
197 std::shared_ptr<TestCustomizable> cs;
198 TestCustomizable* cp = nullptr;
199 };
200
201 static std::unordered_map<std::string, OptionTypeInfo> simple_option_info = {
202 #ifndef ROCKSDB_LITE
203 {"bool",
204 {offsetof(struct SimpleOptions, b), OptionType::kBoolean,
205 OptionVerificationType::kNormal, OptionTypeFlags::kNone}},
206 {"unique",
207 OptionTypeInfo::AsCustomUniquePtr<TestCustomizable>(
208 offsetof(struct SimpleOptions, cu), OptionVerificationType::kNormal,
209 OptionTypeFlags::kAllowNull)},
210 {"shared",
211 OptionTypeInfo::AsCustomSharedPtr<TestCustomizable>(
212 offsetof(struct SimpleOptions, cs), OptionVerificationType::kNormal,
213 OptionTypeFlags::kAllowNull)},
214 {"pointer",
215 OptionTypeInfo::AsCustomRawPtr<TestCustomizable>(
216 offsetof(struct SimpleOptions, cp), OptionVerificationType::kNormal,
217 OptionTypeFlags::kAllowNull)},
218 #endif // ROCKSDB_LITE
219 };
220
221 class SimpleConfigurable : public Configurable {
222 private:
223 SimpleOptions simple_;
224
225 public:
SimpleConfigurable()226 SimpleConfigurable() { RegisterOptions(&simple_, &simple_option_info); }
227
SimpleConfigurable(const std::unordered_map<std::string,OptionTypeInfo> * map)228 explicit SimpleConfigurable(
229 const std::unordered_map<std::string, OptionTypeInfo>* map) {
230 RegisterOptions(&simple_, map);
231 }
232 };
233
234 #ifndef ROCKSDB_LITE
GetMapFromProperties(const std::string & props,std::unordered_map<std::string,std::string> * map)235 static void GetMapFromProperties(
236 const std::string& props,
237 std::unordered_map<std::string, std::string>* map) {
238 std::istringstream iss(props);
239 std::unordered_map<std::string, std::string> copy_map;
240 std::string line;
241 map->clear();
242 for (int line_num = 0; std::getline(iss, line); line_num++) {
243 std::string name;
244 std::string value;
245 ASSERT_OK(
246 RocksDBOptionsParser::ParseStatement(&name, &value, line, line_num));
247 (*map)[name] = value;
248 }
249 }
250 #endif // ROCKSDB_LITE
251 } // namespace
252
253 #ifndef ROCKSDB_LITE
CreateFromString(const ConfigOptions & config_options,const std::string & value,std::shared_ptr<TestCustomizable> * result)254 Status TestCustomizable::CreateFromString(
255 const ConfigOptions& config_options, const std::string& value,
256 std::shared_ptr<TestCustomizable>* result) {
257 return LoadSharedObject<TestCustomizable>(config_options, value, LoadSharedB,
258 result);
259 }
260
CreateFromString(const ConfigOptions & config_options,const std::string & value,std::unique_ptr<TestCustomizable> * result)261 Status TestCustomizable::CreateFromString(
262 const ConfigOptions& config_options, const std::string& value,
263 std::unique_ptr<TestCustomizable>* result) {
264 return LoadUniqueObject<TestCustomizable>(
265 config_options, value,
266 [](const std::string& id, std::unique_ptr<TestCustomizable>* u) {
267 if (id == "B") {
268 u->reset(new BCustomizable(id));
269 return true;
270 } else if (id.empty()) {
271 u->reset();
272 return true;
273 } else {
274 return false;
275 }
276 },
277 result);
278 }
279
CreateFromString(const ConfigOptions & config_options,const std::string & value,TestCustomizable ** result)280 Status TestCustomizable::CreateFromString(const ConfigOptions& config_options,
281 const std::string& value,
282 TestCustomizable** result) {
283 return LoadStaticObject<TestCustomizable>(
284 config_options, value,
285 [](const std::string& id, TestCustomizable** ptr) {
286 if (id == "B") {
287 *ptr = new BCustomizable(id);
288 return true;
289 } else if (id.empty()) {
290 *ptr = nullptr;
291 return true;
292 } else {
293 return false;
294 }
295 },
296 result);
297 }
298 #endif // ROCKSDB_LITE
299
300 class CustomizableTest : public testing::Test {
301 public:
CustomizableTest()302 CustomizableTest() {
303 config_options_.invoke_prepare_options = false;
304 #ifndef ROCKSDB_LITE
305 // GetOptionsFromMap is not supported in ROCKSDB_LITE
306 config_options_.registry->AddLibrary("CustomizableTest",
307 RegisterCustomTestObjects, "");
308 #endif // ROCKSDB_LITE
309 }
310
311 ConfigOptions config_options_;
312 };
313
314 #ifndef ROCKSDB_LITE // GetOptionsFromMap is not supported in ROCKSDB_LITE
315 // Tests that a Customizable can be created by:
316 // - a simple name
317 // - a XXX.id option
318 // - a property with a name
TEST_F(CustomizableTest,CreateByNameTest)319 TEST_F(CustomizableTest, CreateByNameTest) {
320 ObjectLibrary::Default()->Register<TestCustomizable>(
321 "TEST.*",
322 [](const std::string& name, std::unique_ptr<TestCustomizable>* guard,
323 std::string* /* msg */) {
324 guard->reset(new TestCustomizable(name));
325 return guard->get();
326 });
327 std::unique_ptr<Configurable> configurable(new SimpleConfigurable());
328 SimpleOptions* simple = configurable->GetOptions<SimpleOptions>();
329 ASSERT_NE(simple, nullptr);
330 ASSERT_OK(
331 configurable->ConfigureFromString(config_options_, "unique={id=TEST_1}"));
332 ASSERT_NE(simple->cu, nullptr);
333 ASSERT_EQ(simple->cu->GetId(), "TEST_1");
334 ASSERT_OK(
335 configurable->ConfigureFromString(config_options_, "unique.id=TEST_2"));
336 ASSERT_NE(simple->cu, nullptr);
337 ASSERT_EQ(simple->cu->GetId(), "TEST_2");
338 ASSERT_OK(
339 configurable->ConfigureFromString(config_options_, "unique=TEST_3"));
340 ASSERT_NE(simple->cu, nullptr);
341 ASSERT_EQ(simple->cu->GetId(), "TEST_3");
342 }
343
TEST_F(CustomizableTest,ToStringTest)344 TEST_F(CustomizableTest, ToStringTest) {
345 std::unique_ptr<TestCustomizable> custom(new TestCustomizable("test"));
346 ASSERT_EQ(custom->ToString(config_options_), "test");
347 }
348
TEST_F(CustomizableTest,SimpleConfigureTest)349 TEST_F(CustomizableTest, SimpleConfigureTest) {
350 std::unordered_map<std::string, std::string> opt_map = {
351 {"unique", "id=A;int=1;bool=true"},
352 {"shared", "id=B;string=s"},
353 };
354 std::unique_ptr<Configurable> configurable(new SimpleConfigurable());
355 ASSERT_OK(configurable->ConfigureFromMap(config_options_, opt_map));
356 SimpleOptions* simple = configurable->GetOptions<SimpleOptions>();
357 ASSERT_NE(simple, nullptr);
358 ASSERT_NE(simple->cu, nullptr);
359 ASSERT_EQ(simple->cu->GetId(), "A");
360 std::string opt_str;
361 std::string mismatch;
362 ASSERT_OK(configurable->GetOptionString(config_options_, &opt_str));
363 std::unique_ptr<Configurable> copy(new SimpleConfigurable());
364 ASSERT_OK(copy->ConfigureFromString(config_options_, opt_str));
365 ASSERT_TRUE(
366 configurable->AreEquivalent(config_options_, copy.get(), &mismatch));
367 }
368
TEST_F(CustomizableTest,ConfigureFromPropsTest)369 TEST_F(CustomizableTest, ConfigureFromPropsTest) {
370 std::unordered_map<std::string, std::string> opt_map = {
371 {"unique.id", "A"}, {"unique.A.int", "1"}, {"unique.A.bool", "true"},
372 {"shared.id", "B"}, {"shared.B.string", "s"},
373 };
374 std::unique_ptr<Configurable> configurable(new SimpleConfigurable());
375 ASSERT_OK(configurable->ConfigureFromMap(config_options_, opt_map));
376 SimpleOptions* simple = configurable->GetOptions<SimpleOptions>();
377 ASSERT_NE(simple, nullptr);
378 ASSERT_NE(simple->cu, nullptr);
379 ASSERT_EQ(simple->cu->GetId(), "A");
380 std::string opt_str;
381 std::string mismatch;
382 config_options_.delimiter = "\n";
383 std::unordered_map<std::string, std::string> props;
384 ASSERT_OK(configurable->GetOptionString(config_options_, &opt_str));
385 GetMapFromProperties(opt_str, &props);
386 std::unique_ptr<Configurable> copy(new SimpleConfigurable());
387 ASSERT_OK(copy->ConfigureFromMap(config_options_, props));
388 ASSERT_TRUE(
389 configurable->AreEquivalent(config_options_, copy.get(), &mismatch));
390 }
391
TEST_F(CustomizableTest,ConfigureFromShortTest)392 TEST_F(CustomizableTest, ConfigureFromShortTest) {
393 std::unordered_map<std::string, std::string> opt_map = {
394 {"unique.id", "A"}, {"unique.A.int", "1"}, {"unique.A.bool", "true"},
395 {"shared.id", "B"}, {"shared.B.string", "s"},
396 };
397 std::unique_ptr<Configurable> configurable(new SimpleConfigurable());
398 ASSERT_OK(configurable->ConfigureFromMap(config_options_, opt_map));
399 SimpleOptions* simple = configurable->GetOptions<SimpleOptions>();
400 ASSERT_NE(simple, nullptr);
401 ASSERT_NE(simple->cu, nullptr);
402 ASSERT_EQ(simple->cu->GetId(), "A");
403 }
404
TEST_F(CustomizableTest,AreEquivalentOptionsTest)405 TEST_F(CustomizableTest, AreEquivalentOptionsTest) {
406 std::unordered_map<std::string, std::string> opt_map = {
407 {"unique", "id=A;int=1;bool=true"},
408 {"shared", "id=A;int=1;bool=true"},
409 };
410 std::string mismatch;
411 ConfigOptions config_options = config_options_;
412 std::unique_ptr<Configurable> c1(new SimpleConfigurable());
413 std::unique_ptr<Configurable> c2(new SimpleConfigurable());
414 ASSERT_OK(c1->ConfigureFromMap(config_options, opt_map));
415 ASSERT_OK(c2->ConfigureFromMap(config_options, opt_map));
416 ASSERT_TRUE(c1->AreEquivalent(config_options, c2.get(), &mismatch));
417 SimpleOptions* simple = c1->GetOptions<SimpleOptions>();
418 ASSERT_TRUE(
419 simple->cu->AreEquivalent(config_options, simple->cs.get(), &mismatch));
420 ASSERT_OK(simple->cu->ConfigureOption(config_options, "int", "2"));
421 ASSERT_FALSE(
422 simple->cu->AreEquivalent(config_options, simple->cs.get(), &mismatch));
423 ASSERT_FALSE(c1->AreEquivalent(config_options, c2.get(), &mismatch));
424 ConfigOptions loosely = config_options;
425 loosely.sanity_level = ConfigOptions::kSanityLevelLooselyCompatible;
426 ASSERT_TRUE(c1->AreEquivalent(loosely, c2.get(), &mismatch));
427 ASSERT_TRUE(simple->cu->AreEquivalent(loosely, simple->cs.get(), &mismatch));
428
429 ASSERT_OK(c1->ConfigureOption(config_options, "shared", "id=B;string=3"));
430 ASSERT_TRUE(c1->AreEquivalent(loosely, c2.get(), &mismatch));
431 ASSERT_FALSE(c1->AreEquivalent(config_options, c2.get(), &mismatch));
432 ASSERT_FALSE(simple->cs->AreEquivalent(loosely, simple->cu.get(), &mismatch));
433 simple->cs.reset();
434 ASSERT_TRUE(c1->AreEquivalent(loosely, c2.get(), &mismatch));
435 ASSERT_FALSE(c1->AreEquivalent(config_options, c2.get(), &mismatch));
436 }
437
438 // Tests that we can initialize a customizable from its options
TEST_F(CustomizableTest,ConfigureStandaloneCustomTest)439 TEST_F(CustomizableTest, ConfigureStandaloneCustomTest) {
440 std::unique_ptr<TestCustomizable> base, copy;
441 const auto& registry = config_options_.registry;
442 ASSERT_OK(registry->NewUniqueObject<TestCustomizable>("A", &base));
443 ASSERT_OK(registry->NewUniqueObject<TestCustomizable>("A", ©));
444 ASSERT_OK(base->ConfigureFromString(config_options_, "int=33;bool=true"));
445 std::string opt_str;
446 std::string mismatch;
447 ASSERT_OK(base->GetOptionString(config_options_, &opt_str));
448 ASSERT_OK(copy->ConfigureFromString(config_options_, opt_str));
449 ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
450 }
451
452 // Tests that we fail appropriately if the pattern is not registered
TEST_F(CustomizableTest,BadNameTest)453 TEST_F(CustomizableTest, BadNameTest) {
454 config_options_.ignore_unsupported_options = false;
455 std::unique_ptr<Configurable> c1(new SimpleConfigurable());
456 ASSERT_NOK(
457 c1->ConfigureFromString(config_options_, "unique.shared.id=bad name"));
458 config_options_.ignore_unsupported_options = true;
459 ASSERT_OK(
460 c1->ConfigureFromString(config_options_, "unique.shared.id=bad name"));
461 }
462
463 // Tests that we fail appropriately if a bad option is passed to the underlying
464 // configurable
TEST_F(CustomizableTest,BadOptionTest)465 TEST_F(CustomizableTest, BadOptionTest) {
466 std::unique_ptr<Configurable> c1(new SimpleConfigurable());
467 ConfigOptions ignore = config_options_;
468 ignore.ignore_unknown_options = true;
469
470 ASSERT_NOK(c1->ConfigureFromString(config_options_, "A.int=11"));
471 ASSERT_NOK(c1->ConfigureFromString(config_options_, "shared={id=B;int=1}"));
472 ASSERT_OK(c1->ConfigureFromString(ignore, "shared={id=A;string=s}"));
473 ASSERT_NOK(c1->ConfigureFromString(config_options_, "B.int=11"));
474 ASSERT_OK(c1->ConfigureFromString(ignore, "B.int=11"));
475 ASSERT_NOK(c1->ConfigureFromString(config_options_, "A.string=s"));
476 ASSERT_OK(c1->ConfigureFromString(ignore, "A.string=s"));
477 // Test as detached
478 ASSERT_NOK(
479 c1->ConfigureFromString(config_options_, "shared.id=A;A.string=b}"));
480 ASSERT_OK(c1->ConfigureFromString(ignore, "shared.id=A;A.string=s}"));
481 }
482
483 // Tests that different IDs lead to different objects
TEST_F(CustomizableTest,UniqueIdTest)484 TEST_F(CustomizableTest, UniqueIdTest) {
485 std::unique_ptr<Configurable> base(new SimpleConfigurable());
486 ASSERT_OK(base->ConfigureFromString(config_options_,
487 "unique={id=A_1;int=1;bool=true}"));
488 SimpleOptions* simple = base->GetOptions<SimpleOptions>();
489 ASSERT_NE(simple, nullptr);
490 ASSERT_NE(simple->cu, nullptr);
491 ASSERT_EQ(simple->cu->GetId(), std::string("A_1"));
492 std::string opt_str;
493 std::string mismatch;
494 ASSERT_OK(base->GetOptionString(config_options_, &opt_str));
495 std::unique_ptr<Configurable> copy(new SimpleConfigurable());
496 ASSERT_OK(copy->ConfigureFromString(config_options_, opt_str));
497 ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
498 ASSERT_OK(base->ConfigureFromString(config_options_,
499 "unique={id=A_2;int=1;bool=true}"));
500 ASSERT_FALSE(base->AreEquivalent(config_options_, copy.get(), &mismatch));
501 ASSERT_EQ(simple->cu->GetId(), std::string("A_2"));
502 }
503
TEST_F(CustomizableTest,IsInstanceOfTest)504 TEST_F(CustomizableTest, IsInstanceOfTest) {
505 std::shared_ptr<TestCustomizable> tc = std::make_shared<ACustomizable>("A_1");
506
507 ASSERT_EQ(tc->GetId(), std::string("A_1"));
508 ASSERT_TRUE(tc->IsInstanceOf("A"));
509 ASSERT_TRUE(tc->IsInstanceOf("TestCustomizable"));
510 ASSERT_FALSE(tc->IsInstanceOf("B"));
511 ASSERT_FALSE(tc->IsInstanceOf("A_1"));
512 ASSERT_EQ(tc->CheckedCast<ACustomizable>(), tc.get());
513 ASSERT_EQ(tc->CheckedCast<TestCustomizable>(), tc.get());
514 ASSERT_EQ(tc->CheckedCast<BCustomizable>(), nullptr);
515
516 tc.reset(new BCustomizable("B"));
517 ASSERT_TRUE(tc->IsInstanceOf("B"));
518 ASSERT_TRUE(tc->IsInstanceOf("TestCustomizable"));
519 ASSERT_FALSE(tc->IsInstanceOf("A"));
520 ASSERT_EQ(tc->CheckedCast<BCustomizable>(), tc.get());
521 ASSERT_EQ(tc->CheckedCast<TestCustomizable>(), tc.get());
522 ASSERT_EQ(tc->CheckedCast<ACustomizable>(), nullptr);
523 }
524
TEST_F(CustomizableTest,PrepareOptionsTest)525 TEST_F(CustomizableTest, PrepareOptionsTest) {
526 static std::unordered_map<std::string, OptionTypeInfo> p_option_info = {
527 #ifndef ROCKSDB_LITE
528 {"can_prepare",
529 {0, OptionType::kBoolean, OptionVerificationType::kNormal,
530 OptionTypeFlags::kNone}},
531 #endif // ROCKSDB_LITE
532 };
533
534 class PrepareCustomizable : public TestCustomizable {
535 public:
536 bool can_prepare_ = true;
537
538 PrepareCustomizable() : TestCustomizable("P") {
539 RegisterOptions("Prepare", &can_prepare_, &p_option_info);
540 }
541
542 Status PrepareOptions(const ConfigOptions& opts) override {
543 if (!can_prepare_) {
544 return Status::InvalidArgument("Cannot Prepare");
545 } else {
546 return TestCustomizable::PrepareOptions(opts);
547 }
548 }
549 };
550
551 ObjectLibrary::Default()->Register<TestCustomizable>(
552 "P",
553 [](const std::string& /*name*/, std::unique_ptr<TestCustomizable>* guard,
554 std::string* /* msg */) {
555 guard->reset(new PrepareCustomizable());
556 return guard->get();
557 });
558
559 std::unique_ptr<Configurable> base(new SimpleConfigurable());
560 ConfigOptions prepared(config_options_);
561 prepared.invoke_prepare_options = true;
562
563 ASSERT_OK(base->ConfigureFromString(
564 prepared, "unique=A_1; shared={id=B;string=s}; pointer.id=S"));
565 SimpleOptions* simple = base->GetOptions<SimpleOptions>();
566 ASSERT_NE(simple, nullptr);
567 ASSERT_NE(simple->cu, nullptr);
568 ASSERT_NE(simple->cs, nullptr);
569 ASSERT_NE(simple->cp, nullptr);
570 delete simple->cp;
571 base.reset(new SimpleConfigurable());
572 ASSERT_OK(base->ConfigureFromString(
573 config_options_, "unique=A_1; shared={id=B;string=s}; pointer.id=S"));
574
575 simple = base->GetOptions<SimpleOptions>();
576 ASSERT_NE(simple, nullptr);
577 ASSERT_NE(simple->cu, nullptr);
578 ASSERT_NE(simple->cs, nullptr);
579 ASSERT_NE(simple->cp, nullptr);
580
581 ASSERT_OK(base->PrepareOptions(config_options_));
582 delete simple->cp;
583 base.reset(new SimpleConfigurable());
584 simple = base->GetOptions<SimpleOptions>();
585 ASSERT_NE(simple, nullptr);
586
587 ASSERT_NOK(
588 base->ConfigureFromString(prepared, "unique={id=P; can_prepare=false}"));
589 ASSERT_EQ(simple->cu, nullptr);
590
591 ASSERT_OK(
592 base->ConfigureFromString(prepared, "unique={id=P; can_prepare=true}"));
593 ASSERT_NE(simple->cu, nullptr);
594
595 ASSERT_OK(base->ConfigureFromString(config_options_,
596 "unique={id=P; can_prepare=true}"));
597 ASSERT_NE(simple->cu, nullptr);
598 ASSERT_OK(simple->cu->PrepareOptions(prepared));
599
600 ASSERT_OK(base->ConfigureFromString(config_options_,
601 "unique={id=P; can_prepare=false}"));
602 ASSERT_NE(simple->cu, nullptr);
603 ASSERT_NOK(simple->cu->PrepareOptions(prepared));
604 }
605
606 namespace {
607 static std::unordered_map<std::string, OptionTypeInfo> inner_option_info = {
608 #ifndef ROCKSDB_LITE
609 {"inner",
610 OptionTypeInfo::AsCustomSharedPtr<TestCustomizable>(
611 0, OptionVerificationType::kNormal, OptionTypeFlags::kStringNameOnly)}
612 #endif // ROCKSDB_LITE
613 };
614
615 class InnerCustomizable : public Customizable {
616 public:
InnerCustomizable(const std::shared_ptr<Customizable> & w)617 explicit InnerCustomizable(const std::shared_ptr<Customizable>& w)
618 : inner_(w) {}
kClassName()619 static const char* kClassName() { return "Inner"; }
IsInstanceOf(const std::string & name) const620 bool IsInstanceOf(const std::string& name) const override {
621 if (name == kClassName()) {
622 return true;
623 } else {
624 return Customizable::IsInstanceOf(name);
625 }
626 }
627
628 protected:
Inner() const629 const Customizable* Inner() const override { return inner_.get(); }
630
631 private:
632 std::shared_ptr<Customizable> inner_;
633 };
634
635 class WrappedCustomizable1 : public InnerCustomizable {
636 public:
WrappedCustomizable1(const std::shared_ptr<Customizable> & w)637 explicit WrappedCustomizable1(const std::shared_ptr<Customizable>& w)
638 : InnerCustomizable(w) {}
Name() const639 const char* Name() const override { return kClassName(); }
kClassName()640 static const char* kClassName() { return "Wrapped1"; }
641 };
642
643 class WrappedCustomizable2 : public InnerCustomizable {
644 public:
WrappedCustomizable2(const std::shared_ptr<Customizable> & w)645 explicit WrappedCustomizable2(const std::shared_ptr<Customizable>& w)
646 : InnerCustomizable(w) {}
Name() const647 const char* Name() const override { return kClassName(); }
kClassName()648 static const char* kClassName() { return "Wrapped2"; }
649 };
650 } // namespace
651
TEST_F(CustomizableTest,WrappedInnerTest)652 TEST_F(CustomizableTest, WrappedInnerTest) {
653 std::shared_ptr<TestCustomizable> ac =
654 std::make_shared<TestCustomizable>("A");
655
656 ASSERT_TRUE(ac->IsInstanceOf("A"));
657 ASSERT_TRUE(ac->IsInstanceOf("TestCustomizable"));
658 ASSERT_EQ(ac->CheckedCast<TestCustomizable>(), ac.get());
659 ASSERT_EQ(ac->CheckedCast<InnerCustomizable>(), nullptr);
660 ASSERT_EQ(ac->CheckedCast<WrappedCustomizable1>(), nullptr);
661 ASSERT_EQ(ac->CheckedCast<WrappedCustomizable2>(), nullptr);
662 std::shared_ptr<Customizable> wc1 =
663 std::make_shared<WrappedCustomizable1>(ac);
664
665 ASSERT_TRUE(wc1->IsInstanceOf(WrappedCustomizable1::kClassName()));
666 ASSERT_EQ(wc1->CheckedCast<WrappedCustomizable1>(), wc1.get());
667 ASSERT_EQ(wc1->CheckedCast<WrappedCustomizable2>(), nullptr);
668 ASSERT_EQ(wc1->CheckedCast<InnerCustomizable>(), wc1.get());
669 ASSERT_EQ(wc1->CheckedCast<TestCustomizable>(), ac.get());
670
671 std::shared_ptr<Customizable> wc2 =
672 std::make_shared<WrappedCustomizable2>(wc1);
673 ASSERT_TRUE(wc2->IsInstanceOf(WrappedCustomizable2::kClassName()));
674 ASSERT_EQ(wc2->CheckedCast<WrappedCustomizable2>(), wc2.get());
675 ASSERT_EQ(wc2->CheckedCast<WrappedCustomizable1>(), wc1.get());
676 ASSERT_EQ(wc2->CheckedCast<InnerCustomizable>(), wc2.get());
677 ASSERT_EQ(wc2->CheckedCast<TestCustomizable>(), ac.get());
678 }
679
TEST_F(CustomizableTest,CopyObjectTest)680 TEST_F(CustomizableTest, CopyObjectTest) {
681 class CopyCustomizable : public Customizable {
682 public:
683 CopyCustomizable() : prepared_(0), validated_(0) {}
684 const char* Name() const override { return "CopyCustomizable"; }
685
686 Status PrepareOptions(const ConfigOptions& options) override {
687 prepared_++;
688 return Customizable::PrepareOptions(options);
689 }
690 Status ValidateOptions(const DBOptions& db_opts,
691 const ColumnFamilyOptions& cf_opts) const override {
692 validated_++;
693 return Customizable::ValidateOptions(db_opts, cf_opts);
694 }
695 int prepared_;
696 mutable int validated_;
697 };
698
699 CopyCustomizable c1;
700 ConfigOptions config_options;
701 Options options;
702
703 ASSERT_OK(c1.PrepareOptions(config_options));
704 ASSERT_OK(c1.ValidateOptions(options, options));
705 ASSERT_EQ(c1.prepared_, 1);
706 ASSERT_EQ(c1.validated_, 1);
707 CopyCustomizable c2 = c1;
708 ASSERT_OK(c1.PrepareOptions(config_options));
709 ASSERT_OK(c1.ValidateOptions(options, options));
710 ASSERT_EQ(c2.prepared_, 1);
711 ASSERT_EQ(c2.validated_, 1);
712 ASSERT_EQ(c1.prepared_, 2);
713 ASSERT_EQ(c1.validated_, 2);
714 }
715
TEST_F(CustomizableTest,TestStringDepth)716 TEST_F(CustomizableTest, TestStringDepth) {
717 class ShallowCustomizable : public Customizable {
718 public:
719 ShallowCustomizable() {
720 inner_ = std::make_shared<ACustomizable>("a");
721 RegisterOptions("inner", &inner_, &inner_option_info);
722 }
723 static const char* kClassName() { return "shallow"; }
724 const char* Name() const override { return kClassName(); }
725
726 private:
727 std::shared_ptr<TestCustomizable> inner_;
728 };
729 ConfigOptions shallow = config_options_;
730 std::unique_ptr<Configurable> c(new ShallowCustomizable());
731 std::string opt_str;
732 shallow.depth = ConfigOptions::Depth::kDepthShallow;
733 ASSERT_OK(c->GetOptionString(shallow, &opt_str));
734 ASSERT_EQ(opt_str, "inner=a;");
735 shallow.depth = ConfigOptions::Depth::kDepthDetailed;
736 ASSERT_OK(c->GetOptionString(shallow, &opt_str));
737 ASSERT_NE(opt_str, "inner=a;");
738 }
739
740 // Tests that we only get a new customizable when it changes
TEST_F(CustomizableTest,NewUniqueCustomizableTest)741 TEST_F(CustomizableTest, NewUniqueCustomizableTest) {
742 std::unique_ptr<Configurable> base(new SimpleConfigurable());
743 A_count = 0;
744 ASSERT_OK(base->ConfigureFromString(config_options_,
745 "unique={id=A_1;int=1;bool=true}"));
746 SimpleOptions* simple = base->GetOptions<SimpleOptions>();
747 ASSERT_NE(simple, nullptr);
748 ASSERT_NE(simple->cu, nullptr);
749 ASSERT_EQ(A_count, 1); // Created one A
750 ASSERT_OK(base->ConfigureFromString(config_options_,
751 "unique={id=A_1;int=1;bool=false}"));
752 ASSERT_EQ(A_count, 2); // Create another A_1
753 ASSERT_OK(base->ConfigureFromString(config_options_, "unique={id=}"));
754 ASSERT_EQ(simple->cu, nullptr);
755 ASSERT_EQ(A_count, 2);
756 ASSERT_OK(base->ConfigureFromString(config_options_,
757 "unique={id=A_2;int=1;bool=false}"));
758 ASSERT_EQ(A_count, 3); // Created another A
759 ASSERT_OK(base->ConfigureFromString(config_options_, "unique.id="));
760 ASSERT_EQ(simple->cu, nullptr);
761 ASSERT_OK(base->ConfigureFromString(config_options_, "unique=nullptr"));
762 ASSERT_EQ(simple->cu, nullptr);
763 ASSERT_OK(base->ConfigureFromString(config_options_, "unique.id=nullptr"));
764 ASSERT_EQ(simple->cu, nullptr);
765 ASSERT_EQ(A_count, 3);
766 }
767
TEST_F(CustomizableTest,NewEmptyUniqueTest)768 TEST_F(CustomizableTest, NewEmptyUniqueTest) {
769 std::unique_ptr<Configurable> base(new SimpleConfigurable());
770 SimpleOptions* simple = base->GetOptions<SimpleOptions>();
771 ASSERT_EQ(simple->cu, nullptr);
772 simple->cu.reset(new BCustomizable("B"));
773
774 ASSERT_OK(base->ConfigureFromString(config_options_, "unique={id=}"));
775 ASSERT_EQ(simple->cu, nullptr);
776 simple->cu.reset(new BCustomizable("B"));
777
778 ASSERT_OK(base->ConfigureFromString(config_options_, "unique={id=nullptr}"));
779 ASSERT_EQ(simple->cu, nullptr);
780 simple->cu.reset(new BCustomizable("B"));
781
782 ASSERT_OK(base->ConfigureFromString(config_options_, "unique.id="));
783 ASSERT_EQ(simple->cu, nullptr);
784 simple->cu.reset(new BCustomizable("B"));
785
786 ASSERT_OK(base->ConfigureFromString(config_options_, "unique=nullptr"));
787 ASSERT_EQ(simple->cu, nullptr);
788 simple->cu.reset(new BCustomizable("B"));
789
790 ASSERT_OK(base->ConfigureFromString(config_options_, "unique.id=nullptr"));
791 ASSERT_EQ(simple->cu, nullptr);
792 }
793
TEST_F(CustomizableTest,NewEmptySharedTest)794 TEST_F(CustomizableTest, NewEmptySharedTest) {
795 std::unique_ptr<Configurable> base(new SimpleConfigurable());
796
797 SimpleOptions* simple = base->GetOptions<SimpleOptions>();
798 ASSERT_NE(simple, nullptr);
799 ASSERT_EQ(simple->cs, nullptr);
800 simple->cs.reset(new BCustomizable("B"));
801
802 ASSERT_OK(base->ConfigureFromString(config_options_, "shared={id=}"));
803 ASSERT_NE(simple, nullptr);
804 ASSERT_EQ(simple->cs, nullptr);
805 simple->cs.reset(new BCustomizable("B"));
806
807 ASSERT_OK(base->ConfigureFromString(config_options_, "shared={id=nullptr}"));
808 ASSERT_EQ(simple->cs, nullptr);
809 simple->cs.reset(new BCustomizable("B"));
810
811 ASSERT_OK(base->ConfigureFromString(config_options_, "shared.id="));
812 ASSERT_EQ(simple->cs, nullptr);
813 simple->cs.reset(new BCustomizable("B"));
814
815 ASSERT_OK(base->ConfigureFromString(config_options_, "shared.id=nullptr"));
816 ASSERT_EQ(simple->cs, nullptr);
817 simple->cs.reset(new BCustomizable("B"));
818
819 ASSERT_OK(base->ConfigureFromString(config_options_, "shared=nullptr"));
820 ASSERT_EQ(simple->cs, nullptr);
821 }
822
TEST_F(CustomizableTest,NewEmptyStaticTest)823 TEST_F(CustomizableTest, NewEmptyStaticTest) {
824 std::unique_ptr<Configurable> base(new SimpleConfigurable());
825 ASSERT_OK(base->ConfigureFromString(config_options_, "pointer={id=}"));
826 SimpleOptions* simple = base->GetOptions<SimpleOptions>();
827 ASSERT_NE(simple, nullptr);
828 ASSERT_EQ(simple->cp, nullptr);
829 ASSERT_OK(base->ConfigureFromString(config_options_, "pointer={id=nullptr}"));
830 ASSERT_EQ(simple->cp, nullptr);
831
832 ASSERT_OK(base->ConfigureFromString(config_options_, "pointer="));
833 ASSERT_EQ(simple->cp, nullptr);
834 ASSERT_OK(base->ConfigureFromString(config_options_, "pointer=nullptr"));
835 ASSERT_EQ(simple->cp, nullptr);
836
837 ASSERT_OK(base->ConfigureFromString(config_options_, "pointer.id="));
838 ASSERT_EQ(simple->cp, nullptr);
839 ASSERT_OK(base->ConfigureFromString(config_options_, "pointer.id=nullptr"));
840 ASSERT_EQ(simple->cp, nullptr);
841 }
842
843 namespace {
844 #ifndef ROCKSDB_LITE
845 static std::unordered_map<std::string, OptionTypeInfo> vector_option_info = {
846 {"vector",
847 OptionTypeInfo::Vector<std::shared_ptr<TestCustomizable>>(
848 0, OptionVerificationType::kNormal,
849
850 OptionTypeFlags::kNone,
851
852 OptionTypeInfo::AsCustomSharedPtr<TestCustomizable>(
853 0, OptionVerificationType::kNormal, OptionTypeFlags::kNone))},
854 };
855 class VectorConfigurable : public SimpleConfigurable {
856 public:
VectorConfigurable()857 VectorConfigurable() { RegisterOptions("vector", &cv, &vector_option_info); }
858 std::vector<std::shared_ptr<TestCustomizable>> cv;
859 };
860 } // namespace
861
TEST_F(CustomizableTest,VectorConfigTest)862 TEST_F(CustomizableTest, VectorConfigTest) {
863 VectorConfigurable orig, copy;
864 std::shared_ptr<TestCustomizable> c1, c2;
865 ASSERT_OK(TestCustomizable::CreateFromString(config_options_, "A", &c1));
866 ASSERT_OK(TestCustomizable::CreateFromString(config_options_, "B", &c2));
867 orig.cv.push_back(c1);
868 orig.cv.push_back(c2);
869 ASSERT_OK(orig.ConfigureFromString(config_options_, "unique=A2"));
870 std::string opt_str, mismatch;
871 ASSERT_OK(orig.GetOptionString(config_options_, &opt_str));
872 ASSERT_OK(copy.ConfigureFromString(config_options_, opt_str));
873 ASSERT_TRUE(orig.AreEquivalent(config_options_, ©, &mismatch));
874 }
875
TEST_F(CustomizableTest,NoNameTest)876 TEST_F(CustomizableTest, NoNameTest) {
877 // If Customizables are created without names, they are not
878 // part of the serialization (since they cannot be recreated)
879 VectorConfigurable orig, copy;
880 auto sopts = orig.GetOptions<SimpleOptions>();
881 auto copts = copy.GetOptions<SimpleOptions>();
882 sopts->cu.reset(new ACustomizable(""));
883 orig.cv.push_back(std::make_shared<ACustomizable>(""));
884 orig.cv.push_back(std::make_shared<ACustomizable>("A1"));
885 std::string opt_str, mismatch;
886 ASSERT_OK(orig.GetOptionString(config_options_, &opt_str));
887 ASSERT_OK(copy.ConfigureFromString(config_options_, opt_str));
888 ASSERT_EQ(copy.cv.size(), 1U);
889 ASSERT_EQ(copy.cv[0]->GetId(), "A1");
890 ASSERT_EQ(copts->cu, nullptr);
891 }
892
893 #endif // ROCKSDB_LITE
894
TEST_F(CustomizableTest,IgnoreUnknownObjects)895 TEST_F(CustomizableTest, IgnoreUnknownObjects) {
896 ConfigOptions ignore = config_options_;
897 std::shared_ptr<TestCustomizable> shared;
898 std::unique_ptr<TestCustomizable> unique;
899 TestCustomizable* pointer = nullptr;
900 ignore.ignore_unsupported_options = false;
901 ASSERT_NOK(
902 LoadSharedObject<TestCustomizable>(ignore, "Unknown", nullptr, &shared));
903 ASSERT_NOK(
904 LoadUniqueObject<TestCustomizable>(ignore, "Unknown", nullptr, &unique));
905 ASSERT_NOK(
906 LoadStaticObject<TestCustomizable>(ignore, "Unknown", nullptr, &pointer));
907 ASSERT_EQ(shared.get(), nullptr);
908 ASSERT_EQ(unique.get(), nullptr);
909 ASSERT_EQ(pointer, nullptr);
910 ignore.ignore_unsupported_options = true;
911 ASSERT_OK(
912 LoadSharedObject<TestCustomizable>(ignore, "Unknown", nullptr, &shared));
913 ASSERT_OK(
914 LoadUniqueObject<TestCustomizable>(ignore, "Unknown", nullptr, &unique));
915 ASSERT_OK(
916 LoadStaticObject<TestCustomizable>(ignore, "Unknown", nullptr, &pointer));
917 ASSERT_EQ(shared.get(), nullptr);
918 ASSERT_EQ(unique.get(), nullptr);
919 ASSERT_EQ(pointer, nullptr);
920 ASSERT_OK(LoadSharedObject<TestCustomizable>(ignore, "id=Unknown", nullptr,
921 &shared));
922 ASSERT_OK(LoadUniqueObject<TestCustomizable>(ignore, "id=Unknown", nullptr,
923 &unique));
924 ASSERT_OK(LoadStaticObject<TestCustomizable>(ignore, "id=Unknown", nullptr,
925 &pointer));
926 ASSERT_EQ(shared.get(), nullptr);
927 ASSERT_EQ(unique.get(), nullptr);
928 ASSERT_EQ(pointer, nullptr);
929 ASSERT_OK(LoadSharedObject<TestCustomizable>(ignore, "id=Unknown;option=bad",
930 nullptr, &shared));
931 ASSERT_OK(LoadUniqueObject<TestCustomizable>(ignore, "id=Unknown;option=bad",
932 nullptr, &unique));
933 ASSERT_OK(LoadStaticObject<TestCustomizable>(ignore, "id=Unknown;option=bad",
934 nullptr, &pointer));
935 ASSERT_EQ(shared.get(), nullptr);
936 ASSERT_EQ(unique.get(), nullptr);
937 ASSERT_EQ(pointer, nullptr);
938 }
939
TEST_F(CustomizableTest,FactoryFunctionTest)940 TEST_F(CustomizableTest, FactoryFunctionTest) {
941 std::shared_ptr<TestCustomizable> shared;
942 std::unique_ptr<TestCustomizable> unique;
943 TestCustomizable* pointer = nullptr;
944 ConfigOptions ignore = config_options_;
945 ignore.ignore_unsupported_options = false;
946 ASSERT_OK(TestCustomizable::CreateFromString(ignore, "B", &shared));
947 ASSERT_OK(TestCustomizable::CreateFromString(ignore, "B", &unique));
948 ASSERT_OK(TestCustomizable::CreateFromString(ignore, "B", &pointer));
949 ASSERT_NE(shared.get(), nullptr);
950 ASSERT_NE(unique.get(), nullptr);
951 ASSERT_NE(pointer, nullptr);
952 delete pointer;
953 pointer = nullptr;
954 ASSERT_OK(TestCustomizable::CreateFromString(ignore, "id=", &shared));
955 ASSERT_OK(TestCustomizable::CreateFromString(ignore, "id=", &unique));
956 ASSERT_OK(TestCustomizable::CreateFromString(ignore, "id=", &pointer));
957 ASSERT_EQ(shared.get(), nullptr);
958 ASSERT_EQ(unique.get(), nullptr);
959 ASSERT_EQ(pointer, nullptr);
960 ASSERT_NOK(TestCustomizable::CreateFromString(ignore, "option=bad", &shared));
961 ASSERT_NOK(TestCustomizable::CreateFromString(ignore, "option=bad", &unique));
962 ASSERT_NOK(
963 TestCustomizable::CreateFromString(ignore, "option=bad", &pointer));
964 ASSERT_EQ(pointer, nullptr);
965 }
966
TEST_F(CustomizableTest,URLFactoryTest)967 TEST_F(CustomizableTest, URLFactoryTest) {
968 std::unique_ptr<TestCustomizable> unique;
969 ConfigOptions ignore = config_options_;
970 ignore.ignore_unsupported_options = false;
971 ignore.ignore_unsupported_options = false;
972 ASSERT_OK(TestCustomizable::CreateFromString(ignore, "A=1;x=y", &unique));
973 ASSERT_NE(unique, nullptr);
974 ASSERT_EQ(unique->GetId(), "A=1;x=y");
975 ASSERT_OK(TestCustomizable::CreateFromString(ignore, "A;x=y", &unique));
976 ASSERT_NE(unique, nullptr);
977 ASSERT_EQ(unique->GetId(), "A;x=y");
978 unique.reset();
979 ASSERT_OK(TestCustomizable::CreateFromString(ignore, "A=1?x=y", &unique));
980 ASSERT_NE(unique, nullptr);
981 ASSERT_EQ(unique->GetId(), "A=1?x=y");
982 }
983
TEST_F(CustomizableTest,MutableOptionsTest)984 TEST_F(CustomizableTest, MutableOptionsTest) {
985 static std::unordered_map<std::string, OptionTypeInfo> mutable_option_info = {
986 {"mutable",
987 OptionTypeInfo::AsCustomSharedPtr<TestCustomizable>(
988 0, OptionVerificationType::kNormal, OptionTypeFlags::kMutable)}};
989 static std::unordered_map<std::string, OptionTypeInfo> immutable_option_info =
990 {{"immutable",
991 OptionTypeInfo::AsCustomSharedPtr<TestCustomizable>(
992 0, OptionVerificationType::kNormal, OptionTypeFlags::kAllowNull)}};
993
994 class MutableCustomizable : public Customizable {
995 private:
996 std::shared_ptr<TestCustomizable> mutable_;
997 std::shared_ptr<TestCustomizable> immutable_;
998
999 public:
1000 MutableCustomizable() {
1001 RegisterOptions("mutable", &mutable_, &mutable_option_info);
1002 RegisterOptions("immutable", &immutable_, &immutable_option_info);
1003 }
1004 const char* Name() const override { return "MutableCustomizable"; }
1005 };
1006 MutableCustomizable mc, mc2;
1007 std::string mismatch;
1008 std::string opt_str;
1009
1010 ConfigOptions options = config_options_;
1011 ASSERT_OK(mc.ConfigureOption(options, "mutable", "{id=B;}"));
1012 options.mutable_options_only = true;
1013 ASSERT_OK(mc.GetOptionString(options, &opt_str));
1014 ASSERT_OK(mc2.ConfigureFromString(options, opt_str));
1015 ASSERT_TRUE(mc.AreEquivalent(options, &mc2, &mismatch));
1016
1017 options.mutable_options_only = false;
1018 ASSERT_OK(mc.ConfigureOption(options, "immutable", "{id=A; int=10}"));
1019 auto* mm = mc.GetOptions<std::shared_ptr<TestCustomizable>>("mutable");
1020 auto* im = mc.GetOptions<std::shared_ptr<TestCustomizable>>("immutable");
1021 ASSERT_NE(mm, nullptr);
1022 ASSERT_NE(mm->get(), nullptr);
1023 ASSERT_NE(im, nullptr);
1024 ASSERT_NE(im->get(), nullptr);
1025
1026 // Now only deal with mutable options
1027 options.mutable_options_only = true;
1028
1029 // Setting nested immutable customizable options fails
1030 ASSERT_NOK(mc.ConfigureOption(options, "immutable", "{id=B;}"));
1031 ASSERT_NOK(mc.ConfigureOption(options, "immutable.id", "B"));
1032 ASSERT_NOK(mc.ConfigureOption(options, "immutable.bool", "true"));
1033 ASSERT_NOK(mc.ConfigureOption(options, "immutable", "bool=true"));
1034 ASSERT_NOK(mc.ConfigureOption(options, "immutable", "{int=11;bool=true}"));
1035 auto* im_a = im->get()->GetOptions<AOptions>("A");
1036 ASSERT_NE(im_a, nullptr);
1037 ASSERT_EQ(im_a->i, 10);
1038 ASSERT_EQ(im_a->b, false);
1039
1040 // Setting nested mutable customizable options succeeds but the object did not
1041 // change
1042 ASSERT_OK(mc.ConfigureOption(options, "immutable.int", "11"));
1043 ASSERT_EQ(im_a->i, 11);
1044 ASSERT_EQ(im_a, im->get()->GetOptions<AOptions>("A"));
1045
1046 // The mutable configurable itself can be changed
1047 ASSERT_OK(mc.ConfigureOption(options, "mutable.id", "A"));
1048 ASSERT_OK(mc.ConfigureOption(options, "mutable", "A"));
1049 ASSERT_OK(mc.ConfigureOption(options, "mutable", "{id=A}"));
1050 ASSERT_OK(mc.ConfigureOption(options, "mutable", "{bool=true}"));
1051
1052 // The Nested options in the mutable object can be changed
1053 ASSERT_OK(mc.ConfigureOption(options, "mutable", "{bool=true}"));
1054 auto* mm_a = mm->get()->GetOptions<AOptions>("A");
1055 ASSERT_EQ(mm_a->b, true);
1056 ASSERT_OK(mc.ConfigureOption(options, "mutable", "{int=22;bool=false}"));
1057 mm_a = mm->get()->GetOptions<AOptions>("A");
1058 ASSERT_EQ(mm_a->i, 22);
1059 ASSERT_EQ(mm_a->b, false);
1060
1061 // Only the mutable options should get serialized
1062 options.mutable_options_only = false;
1063 ASSERT_OK(mc.GetOptionString(options, &opt_str));
1064 ASSERT_OK(mc.ConfigureOption(options, "immutable", "{id=B;}"));
1065 options.mutable_options_only = true;
1066
1067 ASSERT_OK(mc.GetOptionString(options, &opt_str));
1068 ASSERT_OK(mc2.ConfigureFromString(options, opt_str));
1069 ASSERT_TRUE(mc.AreEquivalent(options, &mc2, &mismatch));
1070 options.mutable_options_only = false;
1071 ASSERT_FALSE(mc.AreEquivalent(options, &mc2, &mismatch));
1072 ASSERT_EQ(mismatch, "immutable");
1073 }
1074
TEST_F(CustomizableTest,CustomManagedObjects)1075 TEST_F(CustomizableTest, CustomManagedObjects) {
1076 std::shared_ptr<TestCustomizable> object1, object2;
1077 ASSERT_OK(LoadManagedObject<TestCustomizable>(
1078 config_options_, "id=A_1;int=1;bool=true", &object1));
1079 ASSERT_OK(
1080 LoadManagedObject<TestCustomizable>(config_options_, "A_1", &object2));
1081 ASSERT_EQ(object1, object2);
1082 auto* opts = object2->GetOptions<AOptions>("A");
1083 ASSERT_NE(opts, nullptr);
1084 ASSERT_EQ(opts->i, 1);
1085 ASSERT_EQ(opts->b, true);
1086 ASSERT_OK(
1087 LoadManagedObject<TestCustomizable>(config_options_, "A_2", &object2));
1088 ASSERT_NE(object1, object2);
1089 object1.reset();
1090 ASSERT_OK(LoadManagedObject<TestCustomizable>(
1091 config_options_, "id=A_1;int=2;bool=false", &object1));
1092 opts = object1->GetOptions<AOptions>("A");
1093 ASSERT_NE(opts, nullptr);
1094 ASSERT_EQ(opts->i, 2);
1095 ASSERT_EQ(opts->b, false);
1096 }
1097
TEST_F(CustomizableTest,CreateManagedObjects)1098 TEST_F(CustomizableTest, CreateManagedObjects) {
1099 class ManagedCustomizable : public Customizable {
1100 public:
1101 static const char* Type() { return "ManagedCustomizable"; }
1102 static const char* kClassName() { return "Managed"; }
1103 const char* Name() const override { return kClassName(); }
1104 std::string GetId() const override { return id_; }
1105 ManagedCustomizable() { id_ = GenerateIndividualId(); }
1106 static Status CreateFromString(
1107 const ConfigOptions& opts, const std::string& value,
1108 std::shared_ptr<ManagedCustomizable>* result) {
1109 return LoadManagedObject<ManagedCustomizable>(opts, value, result);
1110 }
1111
1112 private:
1113 std::string id_;
1114 };
1115
1116 config_options_.registry->AddLibrary("Managed")
1117 ->Register<ManagedCustomizable>(
1118 "Managed(@.*)?", [](const std::string& /*name*/,
1119 std::unique_ptr<ManagedCustomizable>* guard,
1120 std::string* /* msg */) {
1121 guard->reset(new ManagedCustomizable());
1122 return guard->get();
1123 });
1124
1125 std::shared_ptr<ManagedCustomizable> mc1, mc2, mc3, obj;
1126 // Create a "deadbeef" customizable
1127 std::string deadbeef =
1128 std::string(ManagedCustomizable::kClassName()) + "@0xdeadbeef#0001";
1129 ASSERT_OK(
1130 ManagedCustomizable::CreateFromString(config_options_, deadbeef, &mc1));
1131 // Create an object with the base/class name
1132 ASSERT_OK(ManagedCustomizable::CreateFromString(
1133 config_options_, ManagedCustomizable::kClassName(), &mc2));
1134 // Creating another with the base name returns a different object
1135 ASSERT_OK(ManagedCustomizable::CreateFromString(
1136 config_options_, ManagedCustomizable::kClassName(), &mc3));
1137 // At this point, there should be 4 managed objects (deadbeef, mc1, 2, and 3)
1138 std::vector<std::shared_ptr<ManagedCustomizable>> objects;
1139 ASSERT_OK(config_options_.registry->ListManagedObjects(&objects));
1140 ASSERT_EQ(objects.size(), 4U);
1141 objects.clear();
1142 // Three separate object, none of them equal
1143 ASSERT_NE(mc1, mc2);
1144 ASSERT_NE(mc1, mc3);
1145 ASSERT_NE(mc2, mc3);
1146
1147 // Creating another object with "deadbeef" object
1148 ASSERT_OK(
1149 ManagedCustomizable::CreateFromString(config_options_, deadbeef, &obj));
1150 ASSERT_EQ(mc1, obj);
1151 // Create another with the IDs of the instances
1152 ASSERT_OK(ManagedCustomizable::CreateFromString(config_options_, mc1->GetId(),
1153 &obj));
1154 ASSERT_EQ(mc1, obj);
1155 ASSERT_OK(ManagedCustomizable::CreateFromString(config_options_, mc2->GetId(),
1156 &obj));
1157 ASSERT_EQ(mc2, obj);
1158 ASSERT_OK(ManagedCustomizable::CreateFromString(config_options_, mc3->GetId(),
1159 &obj));
1160 ASSERT_EQ(mc3, obj);
1161
1162 // Now get rid of deadbeef. 2 Objects left (m2+m3)
1163 mc1.reset();
1164 ASSERT_EQ(
1165 config_options_.registry->GetManagedObject<ManagedCustomizable>(deadbeef),
1166 nullptr);
1167 ASSERT_OK(config_options_.registry->ListManagedObjects(&objects));
1168 ASSERT_EQ(objects.size(), 2U);
1169 objects.clear();
1170
1171 // Associate deadbeef with #2
1172 ASSERT_OK(config_options_.registry->SetManagedObject(deadbeef, mc2));
1173 ASSERT_OK(
1174 ManagedCustomizable::CreateFromString(config_options_, deadbeef, &obj));
1175 ASSERT_EQ(mc2, obj);
1176 obj.reset();
1177
1178 // Get the ID of mc2 and then reset it. 1 Object left
1179 std::string mc2id = mc2->GetId();
1180 mc2.reset();
1181 ASSERT_EQ(
1182 config_options_.registry->GetManagedObject<ManagedCustomizable>(mc2id),
1183 nullptr);
1184 ASSERT_OK(config_options_.registry->ListManagedObjects(&objects));
1185 ASSERT_EQ(objects.size(), 1U);
1186 objects.clear();
1187
1188 // Create another object with the old mc2id.
1189 ASSERT_OK(
1190 ManagedCustomizable::CreateFromString(config_options_, mc2id, &mc2));
1191 ASSERT_OK(
1192 ManagedCustomizable::CreateFromString(config_options_, mc2id, &obj));
1193 ASSERT_EQ(mc2, obj);
1194
1195 // For good measure, create another deadbeef object
1196 ASSERT_OK(
1197 ManagedCustomizable::CreateFromString(config_options_, deadbeef, &mc1));
1198 ASSERT_OK(
1199 ManagedCustomizable::CreateFromString(config_options_, deadbeef, &obj));
1200 ASSERT_EQ(mc1, obj);
1201 }
1202
1203 #endif // !ROCKSDB_LITE
1204
1205 namespace {
1206 class TestSecondaryCache : public SecondaryCache {
1207 public:
kClassName()1208 static const char* kClassName() { return "Test"; }
Name() const1209 const char* Name() const override { return kClassName(); }
Insert(const Slice &,void *,const Cache::CacheItemHelper *)1210 Status Insert(const Slice& /*key*/, void* /*value*/,
1211 const Cache::CacheItemHelper* /*helper*/) override {
1212 return Status::NotSupported();
1213 }
Lookup(const Slice &,const Cache::CreateCallback &,bool)1214 std::unique_ptr<SecondaryCacheResultHandle> Lookup(
1215 const Slice& /*key*/, const Cache::CreateCallback& /*create_cb*/,
1216 bool /*wait*/) override {
1217 return nullptr;
1218 }
Erase(const Slice &)1219 void Erase(const Slice& /*key*/) override {}
1220
1221 // Wait for a collection of handles to become ready
WaitAll(std::vector<SecondaryCacheResultHandle * >)1222 void WaitAll(std::vector<SecondaryCacheResultHandle*> /*handles*/) override {}
1223
GetPrintableOptions() const1224 std::string GetPrintableOptions() const override { return ""; }
1225 };
1226
1227 class TestStatistics : public StatisticsImpl {
1228 public:
TestStatistics()1229 TestStatistics() : StatisticsImpl(nullptr) {}
Name() const1230 const char* Name() const override { return kClassName(); }
kClassName()1231 static const char* kClassName() { return "Test"; }
1232 };
1233
1234 class TestFlushBlockPolicyFactory : public FlushBlockPolicyFactory {
1235 public:
TestFlushBlockPolicyFactory()1236 TestFlushBlockPolicyFactory() {}
1237
kClassName()1238 static const char* kClassName() { return "TestFlushBlockPolicyFactory"; }
Name() const1239 const char* Name() const override { return kClassName(); }
1240
NewFlushBlockPolicy(const BlockBasedTableOptions &,const BlockBuilder &) const1241 FlushBlockPolicy* NewFlushBlockPolicy(
1242 const BlockBasedTableOptions& /*table_options*/,
1243 const BlockBuilder& /*data_block_builder*/) const override {
1244 return nullptr;
1245 }
1246 };
1247
1248 class MockSliceTransform : public SliceTransform {
1249 public:
Name() const1250 const char* Name() const override { return kClassName(); }
kClassName()1251 static const char* kClassName() { return "Mock"; }
1252
Transform(const Slice &) const1253 Slice Transform(const Slice& /*key*/) const override { return Slice(); }
1254
InDomain(const Slice &) const1255 bool InDomain(const Slice& /*key*/) const override { return false; }
1256
InRange(const Slice &) const1257 bool InRange(const Slice& /*key*/) const override { return false; }
1258 };
1259
1260 #ifndef ROCKSDB_LITE
1261 class MockEncryptionProvider : public EncryptionProvider {
1262 public:
MockEncryptionProvider(const std::string & id)1263 explicit MockEncryptionProvider(const std::string& id) : id_(id) {}
Name() const1264 const char* Name() const override { return "Mock"; }
GetPrefixLength() const1265 size_t GetPrefixLength() const override { return 0; }
CreateNewPrefix(const std::string &,char *,size_t) const1266 Status CreateNewPrefix(const std::string& /*fname*/, char* /*prefix*/,
1267 size_t /*prefixLength*/) const override {
1268 return Status::NotSupported();
1269 }
1270
AddCipher(const std::string &,const char *,size_t,bool)1271 Status AddCipher(const std::string& /*descriptor*/, const char* /*cipher*/,
1272 size_t /*len*/, bool /*for_write*/) override {
1273 return Status::NotSupported();
1274 }
1275
CreateCipherStream(const std::string &,const EnvOptions &,Slice &,std::unique_ptr<BlockAccessCipherStream> *)1276 Status CreateCipherStream(
1277 const std::string& /*fname*/, const EnvOptions& /*options*/,
1278 Slice& /*prefix*/,
1279 std::unique_ptr<BlockAccessCipherStream>* /*result*/) override {
1280 return Status::NotSupported();
1281 }
ValidateOptions(const DBOptions & db_opts,const ColumnFamilyOptions & cf_opts) const1282 Status ValidateOptions(const DBOptions& db_opts,
1283 const ColumnFamilyOptions& cf_opts) const override {
1284 if (EndsWith(id_, "://test")) {
1285 return EncryptionProvider::ValidateOptions(db_opts, cf_opts);
1286 } else {
1287 return Status::InvalidArgument("MockProvider not initialized");
1288 }
1289 }
1290
1291 private:
1292 std::string id_;
1293 };
1294
1295 class MockCipher : public BlockCipher {
1296 public:
Name() const1297 const char* Name() const override { return "Mock"; }
BlockSize()1298 size_t BlockSize() override { return 0; }
Encrypt(char *)1299 Status Encrypt(char* /*data*/) override { return Status::NotSupported(); }
Decrypt(char * data)1300 Status Decrypt(char* data) override { return Encrypt(data); }
1301 };
1302
1303 #endif // ROCKSDB_LITE
1304
1305 class MockTablePropertiesCollectorFactory
1306 : public TablePropertiesCollectorFactory {
1307 private:
1308 public:
CreateTablePropertiesCollector(TablePropertiesCollectorFactory::Context)1309 TablePropertiesCollector* CreateTablePropertiesCollector(
1310 TablePropertiesCollectorFactory::Context /*context*/) override {
1311 return nullptr;
1312 }
kClassName()1313 static const char* kClassName() { return "Mock"; }
Name() const1314 const char* Name() const override { return kClassName(); }
1315 };
1316
1317 class MockSstPartitionerFactory : public SstPartitionerFactory {
1318 public:
kClassName()1319 static const char* kClassName() { return "Mock"; }
Name() const1320 const char* Name() const override { return kClassName(); }
CreatePartitioner(const SstPartitioner::Context &) const1321 std::unique_ptr<SstPartitioner> CreatePartitioner(
1322 const SstPartitioner::Context& /* context */) const override {
1323 return nullptr;
1324 }
1325 };
1326
1327 class MockFileChecksumGenFactory : public FileChecksumGenFactory {
1328 public:
kClassName()1329 static const char* kClassName() { return "Mock"; }
Name() const1330 const char* Name() const override { return kClassName(); }
CreateFileChecksumGenerator(const FileChecksumGenContext &)1331 std::unique_ptr<FileChecksumGenerator> CreateFileChecksumGenerator(
1332 const FileChecksumGenContext& /*context*/) override {
1333 return nullptr;
1334 }
1335 };
1336
1337 #ifndef ROCKSDB_LITE
RegisterLocalObjects(ObjectLibrary & library,const std::string &)1338 static int RegisterLocalObjects(ObjectLibrary& library,
1339 const std::string& /*arg*/) {
1340 size_t num_types;
1341 library.Register<TableFactory>(
1342 mock::MockTableFactory::kClassName(),
1343 [](const std::string& /*uri*/, std::unique_ptr<TableFactory>* guard,
1344 std::string* /* errmsg */) {
1345 guard->reset(new mock::MockTableFactory());
1346 return guard->get();
1347 });
1348 library.Register<EventListener>(
1349 OnFileDeletionListener::kClassName(),
1350 [](const std::string& /*uri*/, std::unique_ptr<EventListener>* guard,
1351 std::string* /* errmsg */) {
1352 guard->reset(new OnFileDeletionListener());
1353 return guard->get();
1354 });
1355 library.Register<EventListener>(
1356 FlushCounterListener::kClassName(),
1357 [](const std::string& /*uri*/, std::unique_ptr<EventListener>* guard,
1358 std::string* /* errmsg */) {
1359 guard->reset(new FlushCounterListener());
1360 return guard->get();
1361 });
1362 // Load any locally defined objects here
1363 library.Register<const SliceTransform>(
1364 MockSliceTransform::kClassName(),
1365 [](const std::string& /*uri*/,
1366 std::unique_ptr<const SliceTransform>* guard,
1367 std::string* /* errmsg */) {
1368 guard->reset(new MockSliceTransform());
1369 return guard->get();
1370 });
1371 library.Register<Statistics>(
1372 TestStatistics::kClassName(),
1373 [](const std::string& /*uri*/, std::unique_ptr<Statistics>* guard,
1374 std::string* /* errmsg */) {
1375 guard->reset(new TestStatistics());
1376 return guard->get();
1377 });
1378
1379 library.Register<EncryptionProvider>(
1380 "Mock(://test)?",
1381 [](const std::string& uri, std::unique_ptr<EncryptionProvider>* guard,
1382 std::string* /* errmsg */) {
1383 guard->reset(new MockEncryptionProvider(uri));
1384 return guard->get();
1385 });
1386 library.Register<BlockCipher>("Mock", [](const std::string& /*uri*/,
1387 std::unique_ptr<BlockCipher>* guard,
1388 std::string* /* errmsg */) {
1389 guard->reset(new MockCipher());
1390 return guard->get();
1391 });
1392 library.Register<FlushBlockPolicyFactory>(
1393 TestFlushBlockPolicyFactory::kClassName(),
1394 [](const std::string& /*uri*/,
1395 std::unique_ptr<FlushBlockPolicyFactory>* guard,
1396 std::string* /* errmsg */) {
1397 guard->reset(new TestFlushBlockPolicyFactory());
1398 return guard->get();
1399 });
1400
1401 library.Register<SecondaryCache>(
1402 TestSecondaryCache::kClassName(),
1403 [](const std::string& /*uri*/, std::unique_ptr<SecondaryCache>* guard,
1404 std::string* /* errmsg */) {
1405 guard->reset(new TestSecondaryCache());
1406 return guard->get();
1407 });
1408
1409 library.Register<SstPartitionerFactory>(
1410 MockSstPartitionerFactory::kClassName(),
1411 [](const std::string& /*uri*/,
1412 std::unique_ptr<SstPartitionerFactory>* guard,
1413 std::string* /* errmsg */) {
1414 guard->reset(new MockSstPartitionerFactory());
1415 return guard->get();
1416 });
1417
1418 library.Register<FileChecksumGenFactory>(
1419 MockFileChecksumGenFactory::kClassName(),
1420 [](const std::string& /*uri*/,
1421 std::unique_ptr<FileChecksumGenFactory>* guard,
1422 std::string* /* errmsg */) {
1423 guard->reset(new MockFileChecksumGenFactory());
1424 return guard->get();
1425 });
1426
1427 library.Register<TablePropertiesCollectorFactory>(
1428 MockTablePropertiesCollectorFactory::kClassName(),
1429 [](const std::string& /*uri*/,
1430 std::unique_ptr<TablePropertiesCollectorFactory>* guard,
1431 std::string* /* errmsg */) {
1432 guard->reset(new MockTablePropertiesCollectorFactory());
1433 return guard->get();
1434 });
1435 return static_cast<int>(library.GetFactoryCount(&num_types));
1436 }
1437 #endif // !ROCKSDB_LITE
1438 } // namespace
1439
1440 class LoadCustomizableTest : public testing::Test {
1441 public:
LoadCustomizableTest()1442 LoadCustomizableTest() {
1443 config_options_.ignore_unsupported_options = false;
1444 config_options_.invoke_prepare_options = false;
1445 }
RegisterTests(const std::string & arg)1446 bool RegisterTests(const std::string& arg) {
1447 #ifndef ROCKSDB_LITE
1448 config_options_.registry->AddLibrary("custom-tests",
1449 test::RegisterTestObjects, arg);
1450 config_options_.registry->AddLibrary("local-tests", RegisterLocalObjects,
1451 arg);
1452 return true;
1453 #else
1454 (void)arg;
1455 return false;
1456 #endif // !ROCKSDB_LITE
1457 }
1458
1459 protected:
1460 DBOptions db_opts_;
1461 ColumnFamilyOptions cf_opts_;
1462 ConfigOptions config_options_;
1463 };
1464
TEST_F(LoadCustomizableTest,LoadTableFactoryTest)1465 TEST_F(LoadCustomizableTest, LoadTableFactoryTest) {
1466 std::shared_ptr<TableFactory> factory;
1467 ASSERT_NOK(TableFactory::CreateFromString(
1468 config_options_, mock::MockTableFactory::kClassName(), &factory));
1469 ASSERT_OK(TableFactory::CreateFromString(
1470 config_options_, TableFactory::kBlockBasedTableName(), &factory));
1471 ASSERT_NE(factory, nullptr);
1472 ASSERT_STREQ(factory->Name(), TableFactory::kBlockBasedTableName());
1473 #ifndef ROCKSDB_LITE
1474 std::string opts_str = "table_factory=";
1475 ASSERT_OK(GetColumnFamilyOptionsFromString(
1476 config_options_, cf_opts_,
1477 opts_str + TableFactory::kBlockBasedTableName(), &cf_opts_));
1478 ASSERT_NE(cf_opts_.table_factory.get(), nullptr);
1479 ASSERT_STREQ(cf_opts_.table_factory->Name(),
1480 TableFactory::kBlockBasedTableName());
1481 #endif // ROCKSDB_LITE
1482 if (RegisterTests("Test")) {
1483 ASSERT_OK(TableFactory::CreateFromString(
1484 config_options_, mock::MockTableFactory::kClassName(), &factory));
1485 ASSERT_NE(factory, nullptr);
1486 ASSERT_STREQ(factory->Name(), mock::MockTableFactory::kClassName());
1487 #ifndef ROCKSDB_LITE
1488 ASSERT_OK(GetColumnFamilyOptionsFromString(
1489 config_options_, cf_opts_,
1490 opts_str + mock::MockTableFactory::kClassName(), &cf_opts_));
1491 ASSERT_NE(cf_opts_.table_factory.get(), nullptr);
1492 ASSERT_STREQ(cf_opts_.table_factory->Name(),
1493 mock::MockTableFactory::kClassName());
1494 #endif // ROCKSDB_LITE
1495 }
1496 }
1497
TEST_F(LoadCustomizableTest,LoadSecondaryCacheTest)1498 TEST_F(LoadCustomizableTest, LoadSecondaryCacheTest) {
1499 std::shared_ptr<SecondaryCache> result;
1500 ASSERT_NOK(SecondaryCache::CreateFromString(
1501 config_options_, TestSecondaryCache::kClassName(), &result));
1502 if (RegisterTests("Test")) {
1503 ASSERT_OK(SecondaryCache::CreateFromString(
1504 config_options_, TestSecondaryCache::kClassName(), &result));
1505 ASSERT_NE(result, nullptr);
1506 ASSERT_STREQ(result->Name(), TestSecondaryCache::kClassName());
1507 }
1508 }
1509
1510 #ifndef ROCKSDB_LITE
TEST_F(LoadCustomizableTest,LoadSstPartitionerFactoryTest)1511 TEST_F(LoadCustomizableTest, LoadSstPartitionerFactoryTest) {
1512 std::shared_ptr<SstPartitionerFactory> factory;
1513 ASSERT_NOK(SstPartitionerFactory::CreateFromString(config_options_, "Mock",
1514 &factory));
1515 ASSERT_OK(SstPartitionerFactory::CreateFromString(
1516 config_options_, SstPartitionerFixedPrefixFactory::kClassName(),
1517 &factory));
1518 ASSERT_NE(factory, nullptr);
1519 ASSERT_STREQ(factory->Name(), SstPartitionerFixedPrefixFactory::kClassName());
1520
1521 if (RegisterTests("Test")) {
1522 ASSERT_OK(SstPartitionerFactory::CreateFromString(config_options_, "Mock",
1523 &factory));
1524 ASSERT_NE(factory, nullptr);
1525 ASSERT_STREQ(factory->Name(), "Mock");
1526 }
1527 }
1528 #endif // ROCKSDB_LITE
1529
TEST_F(LoadCustomizableTest,LoadChecksumGenFactoryTest)1530 TEST_F(LoadCustomizableTest, LoadChecksumGenFactoryTest) {
1531 std::shared_ptr<FileChecksumGenFactory> factory;
1532 ASSERT_NOK(FileChecksumGenFactory::CreateFromString(config_options_, "Mock",
1533 &factory));
1534 ASSERT_OK(FileChecksumGenFactory::CreateFromString(
1535 config_options_, FileChecksumGenCrc32cFactory::kClassName(), &factory));
1536 ASSERT_NE(factory, nullptr);
1537 ASSERT_STREQ(factory->Name(), FileChecksumGenCrc32cFactory::kClassName());
1538
1539 if (RegisterTests("Test")) {
1540 ASSERT_OK(FileChecksumGenFactory::CreateFromString(config_options_, "Mock",
1541 &factory));
1542 ASSERT_NE(factory, nullptr);
1543 ASSERT_STREQ(factory->Name(), "Mock");
1544 }
1545 }
1546
TEST_F(LoadCustomizableTest,LoadTablePropertiesCollectorFactoryTest)1547 TEST_F(LoadCustomizableTest, LoadTablePropertiesCollectorFactoryTest) {
1548 std::shared_ptr<TablePropertiesCollectorFactory> factory;
1549 ASSERT_NOK(TablePropertiesCollectorFactory::CreateFromString(
1550 config_options_, MockTablePropertiesCollectorFactory::kClassName(),
1551 &factory));
1552 if (RegisterTests("Test")) {
1553 ASSERT_OK(TablePropertiesCollectorFactory::CreateFromString(
1554 config_options_, MockTablePropertiesCollectorFactory::kClassName(),
1555 &factory));
1556 ASSERT_NE(factory, nullptr);
1557 ASSERT_STREQ(factory->Name(),
1558 MockTablePropertiesCollectorFactory::kClassName());
1559 }
1560 }
1561
TEST_F(LoadCustomizableTest,LoadComparatorTest)1562 TEST_F(LoadCustomizableTest, LoadComparatorTest) {
1563 const Comparator* bytewise = BytewiseComparator();
1564 const Comparator* reverse = ReverseBytewiseComparator();
1565
1566 const Comparator* result = nullptr;
1567 ASSERT_NOK(Comparator::CreateFromString(
1568 config_options_, test::SimpleSuffixReverseComparator::kClassName(),
1569 &result));
1570 ASSERT_OK(
1571 Comparator::CreateFromString(config_options_, bytewise->Name(), &result));
1572 ASSERT_EQ(result, bytewise);
1573 ASSERT_OK(
1574 Comparator::CreateFromString(config_options_, reverse->Name(), &result));
1575 ASSERT_EQ(result, reverse);
1576
1577 if (RegisterTests("Test")) {
1578 ASSERT_OK(Comparator::CreateFromString(
1579 config_options_, test::SimpleSuffixReverseComparator::kClassName(),
1580 &result));
1581 ASSERT_NE(result, nullptr);
1582 ASSERT_STREQ(result->Name(),
1583 test::SimpleSuffixReverseComparator::kClassName());
1584 }
1585 }
1586
TEST_F(LoadCustomizableTest,LoadSliceTransformFactoryTest)1587 TEST_F(LoadCustomizableTest, LoadSliceTransformFactoryTest) {
1588 std::shared_ptr<const SliceTransform> result;
1589 ASSERT_NOK(
1590 SliceTransform::CreateFromString(config_options_, "Mock", &result));
1591 ASSERT_OK(
1592 SliceTransform::CreateFromString(config_options_, "fixed:16", &result));
1593 ASSERT_NE(result.get(), nullptr);
1594 ASSERT_TRUE(result->IsInstanceOf("fixed"));
1595 ASSERT_OK(SliceTransform::CreateFromString(
1596 config_options_, "rocksdb.FixedPrefix.22", &result));
1597 ASSERT_NE(result.get(), nullptr);
1598 ASSERT_TRUE(result->IsInstanceOf("fixed"));
1599
1600 ASSERT_OK(
1601 SliceTransform::CreateFromString(config_options_, "capped:16", &result));
1602 ASSERT_NE(result.get(), nullptr);
1603 ASSERT_TRUE(result->IsInstanceOf("capped"));
1604
1605 ASSERT_OK(SliceTransform::CreateFromString(
1606 config_options_, "rocksdb.CappedPrefix.11", &result));
1607 ASSERT_NE(result.get(), nullptr);
1608 ASSERT_TRUE(result->IsInstanceOf("capped"));
1609
1610 if (RegisterTests("Test")) {
1611 ASSERT_OK(
1612 SliceTransform::CreateFromString(config_options_, "Mock", &result));
1613 ASSERT_NE(result, nullptr);
1614 ASSERT_STREQ(result->Name(), "Mock");
1615 }
1616 }
1617
TEST_F(LoadCustomizableTest,LoadStatisticsTest)1618 TEST_F(LoadCustomizableTest, LoadStatisticsTest) {
1619 std::shared_ptr<Statistics> stats;
1620 ASSERT_NOK(Statistics::CreateFromString(
1621 config_options_, TestStatistics::kClassName(), &stats));
1622 ASSERT_OK(
1623 Statistics::CreateFromString(config_options_, "BasicStatistics", &stats));
1624 ASSERT_NE(stats, nullptr);
1625 ASSERT_EQ(stats->Name(), std::string("BasicStatistics"));
1626 #ifndef ROCKSDB_LITE
1627 ASSERT_NOK(GetDBOptionsFromString(config_options_, db_opts_,
1628 "statistics=Test", &db_opts_));
1629 ASSERT_OK(GetDBOptionsFromString(config_options_, db_opts_,
1630 "statistics=BasicStatistics", &db_opts_));
1631 ASSERT_NE(db_opts_.statistics, nullptr);
1632 ASSERT_STREQ(db_opts_.statistics->Name(), "BasicStatistics");
1633
1634 if (RegisterTests("test")) {
1635 ASSERT_OK(Statistics::CreateFromString(
1636 config_options_, TestStatistics::kClassName(), &stats));
1637 ASSERT_NE(stats, nullptr);
1638 ASSERT_STREQ(stats->Name(), TestStatistics::kClassName());
1639
1640 ASSERT_OK(GetDBOptionsFromString(config_options_, db_opts_,
1641 "statistics=Test", &db_opts_));
1642 ASSERT_NE(db_opts_.statistics, nullptr);
1643 ASSERT_STREQ(db_opts_.statistics->Name(), TestStatistics::kClassName());
1644
1645 ASSERT_OK(GetDBOptionsFromString(
1646 config_options_, db_opts_, "statistics={id=Test;inner=BasicStatistics}",
1647 &db_opts_));
1648 ASSERT_NE(db_opts_.statistics, nullptr);
1649 ASSERT_STREQ(db_opts_.statistics->Name(), TestStatistics::kClassName());
1650 auto* inner = db_opts_.statistics->GetOptions<std::shared_ptr<Statistics>>(
1651 "StatisticsOptions");
1652 ASSERT_NE(inner, nullptr);
1653 ASSERT_NE(inner->get(), nullptr);
1654 ASSERT_STREQ(inner->get()->Name(), "BasicStatistics");
1655
1656 ASSERT_OK(Statistics::CreateFromString(
1657 config_options_, "id=BasicStatistics;inner=Test", &stats));
1658 ASSERT_NE(stats, nullptr);
1659 ASSERT_STREQ(stats->Name(), "BasicStatistics");
1660 inner = stats->GetOptions<std::shared_ptr<Statistics>>("StatisticsOptions");
1661 ASSERT_NE(inner, nullptr);
1662 ASSERT_NE(inner->get(), nullptr);
1663 ASSERT_STREQ(inner->get()->Name(), TestStatistics::kClassName());
1664 }
1665 #endif
1666 }
1667
TEST_F(LoadCustomizableTest,LoadMemTableRepFactoryTest)1668 TEST_F(LoadCustomizableTest, LoadMemTableRepFactoryTest) {
1669 std::unique_ptr<MemTableRepFactory> result;
1670 ASSERT_NOK(MemTableRepFactory::CreateFromString(
1671 config_options_, "SpecialSkipListFactory", &result));
1672 ASSERT_OK(MemTableRepFactory::CreateFromString(
1673 config_options_, SkipListFactory::kClassName(), &result));
1674 ASSERT_NE(result.get(), nullptr);
1675 ASSERT_TRUE(result->IsInstanceOf(SkipListFactory::kClassName()));
1676
1677 if (RegisterTests("Test")) {
1678 ASSERT_OK(MemTableRepFactory::CreateFromString(
1679 config_options_, "SpecialSkipListFactory", &result));
1680 ASSERT_NE(result, nullptr);
1681 ASSERT_STREQ(result->Name(), "SpecialSkipListFactory");
1682 }
1683 }
1684
TEST_F(LoadCustomizableTest,LoadMergeOperatorTest)1685 TEST_F(LoadCustomizableTest, LoadMergeOperatorTest) {
1686 std::shared_ptr<MergeOperator> result;
1687
1688 ASSERT_NOK(
1689 MergeOperator::CreateFromString(config_options_, "Changling", &result));
1690 ASSERT_OK(MergeOperator::CreateFromString(config_options_, "put", &result));
1691 ASSERT_NE(result, nullptr);
1692 ASSERT_STREQ(result->Name(), "PutOperator");
1693 if (RegisterTests("Test")) {
1694 ASSERT_OK(
1695 MergeOperator::CreateFromString(config_options_, "Changling", &result));
1696 ASSERT_NE(result, nullptr);
1697 ASSERT_STREQ(result->Name(), "ChanglingMergeOperator");
1698 }
1699 }
1700
TEST_F(LoadCustomizableTest,LoadCompactionFilterFactoryTest)1701 TEST_F(LoadCustomizableTest, LoadCompactionFilterFactoryTest) {
1702 std::shared_ptr<CompactionFilterFactory> result;
1703
1704 ASSERT_NOK(CompactionFilterFactory::CreateFromString(config_options_,
1705 "Changling", &result));
1706 if (RegisterTests("Test")) {
1707 ASSERT_OK(CompactionFilterFactory::CreateFromString(config_options_,
1708 "Changling", &result));
1709 ASSERT_NE(result, nullptr);
1710 ASSERT_STREQ(result->Name(), "ChanglingCompactionFilterFactory");
1711 }
1712 }
1713
TEST_F(LoadCustomizableTest,LoadCompactionFilterTest)1714 TEST_F(LoadCustomizableTest, LoadCompactionFilterTest) {
1715 const CompactionFilter* result = nullptr;
1716
1717 ASSERT_NOK(CompactionFilter::CreateFromString(config_options_, "Changling",
1718 &result));
1719 #ifndef ROCKSDB_LITE
1720 ASSERT_OK(CompactionFilter::CreateFromString(
1721 config_options_, RemoveEmptyValueCompactionFilter::kClassName(),
1722 &result));
1723 ASSERT_NE(result, nullptr);
1724 ASSERT_STREQ(result->Name(), RemoveEmptyValueCompactionFilter::kClassName());
1725 delete result;
1726 result = nullptr;
1727 if (RegisterTests("Test")) {
1728 ASSERT_OK(CompactionFilter::CreateFromString(config_options_, "Changling",
1729 &result));
1730 ASSERT_NE(result, nullptr);
1731 ASSERT_STREQ(result->Name(), "ChanglingCompactionFilter");
1732 delete result;
1733 }
1734 #endif // ROCKSDB_LITE
1735 }
1736
1737 #ifndef ROCKSDB_LITE
TEST_F(LoadCustomizableTest,LoadEventListenerTest)1738 TEST_F(LoadCustomizableTest, LoadEventListenerTest) {
1739 std::shared_ptr<EventListener> result;
1740
1741 ASSERT_NOK(EventListener::CreateFromString(
1742 config_options_, OnFileDeletionListener::kClassName(), &result));
1743 ASSERT_NOK(EventListener::CreateFromString(
1744 config_options_, FlushCounterListener::kClassName(), &result));
1745 if (RegisterTests("Test")) {
1746 ASSERT_OK(EventListener::CreateFromString(
1747 config_options_, OnFileDeletionListener::kClassName(), &result));
1748 ASSERT_NE(result, nullptr);
1749 ASSERT_STREQ(result->Name(), OnFileDeletionListener::kClassName());
1750 ASSERT_OK(EventListener::CreateFromString(
1751 config_options_, FlushCounterListener::kClassName(), &result));
1752 ASSERT_NE(result, nullptr);
1753 ASSERT_STREQ(result->Name(), FlushCounterListener::kClassName());
1754 }
1755 }
1756
TEST_F(LoadCustomizableTest,LoadEncryptionProviderTest)1757 TEST_F(LoadCustomizableTest, LoadEncryptionProviderTest) {
1758 std::shared_ptr<EncryptionProvider> result;
1759 ASSERT_NOK(
1760 EncryptionProvider::CreateFromString(config_options_, "Mock", &result));
1761 ASSERT_OK(
1762 EncryptionProvider::CreateFromString(config_options_, "CTR", &result));
1763 ASSERT_NE(result, nullptr);
1764 ASSERT_STREQ(result->Name(), "CTR");
1765 ASSERT_NOK(result->ValidateOptions(db_opts_, cf_opts_));
1766 ASSERT_OK(EncryptionProvider::CreateFromString(config_options_, "CTR://test",
1767 &result));
1768 ASSERT_NE(result, nullptr);
1769 ASSERT_STREQ(result->Name(), "CTR");
1770 ASSERT_OK(result->ValidateOptions(db_opts_, cf_opts_));
1771
1772 if (RegisterTests("Test")) {
1773 ASSERT_OK(
1774 EncryptionProvider::CreateFromString(config_options_, "Mock", &result));
1775 ASSERT_NE(result, nullptr);
1776 ASSERT_STREQ(result->Name(), "Mock");
1777 ASSERT_OK(EncryptionProvider::CreateFromString(config_options_,
1778 "Mock://test", &result));
1779 ASSERT_NE(result, nullptr);
1780 ASSERT_STREQ(result->Name(), "Mock");
1781 ASSERT_OK(result->ValidateOptions(db_opts_, cf_opts_));
1782 }
1783 }
1784
TEST_F(LoadCustomizableTest,LoadEncryptionCipherTest)1785 TEST_F(LoadCustomizableTest, LoadEncryptionCipherTest) {
1786 std::shared_ptr<BlockCipher> result;
1787 ASSERT_NOK(BlockCipher::CreateFromString(config_options_, "Mock", &result));
1788 ASSERT_OK(BlockCipher::CreateFromString(config_options_, "ROT13", &result));
1789 ASSERT_NE(result, nullptr);
1790 ASSERT_STREQ(result->Name(), "ROT13");
1791 if (RegisterTests("Test")) {
1792 ASSERT_OK(BlockCipher::CreateFromString(config_options_, "Mock", &result));
1793 ASSERT_NE(result, nullptr);
1794 ASSERT_STREQ(result->Name(), "Mock");
1795 }
1796 }
1797 #endif // !ROCKSDB_LITE
1798
TEST_F(LoadCustomizableTest,LoadSystemClockTest)1799 TEST_F(LoadCustomizableTest, LoadSystemClockTest) {
1800 std::shared_ptr<SystemClock> result;
1801 ASSERT_NOK(SystemClock::CreateFromString(
1802 config_options_, MockSystemClock::kClassName(), &result));
1803 ASSERT_OK(SystemClock::CreateFromString(
1804 config_options_, SystemClock::kDefaultName(), &result));
1805 ASSERT_NE(result, nullptr);
1806 ASSERT_TRUE(result->IsInstanceOf(SystemClock::kDefaultName()));
1807 if (RegisterTests("Test")) {
1808 ASSERT_OK(SystemClock::CreateFromString(
1809 config_options_, MockSystemClock::kClassName(), &result));
1810 ASSERT_NE(result, nullptr);
1811 ASSERT_STREQ(result->Name(), MockSystemClock::kClassName());
1812 }
1813 }
1814
TEST_F(LoadCustomizableTest,LoadFlushBlockPolicyFactoryTest)1815 TEST_F(LoadCustomizableTest, LoadFlushBlockPolicyFactoryTest) {
1816 std::shared_ptr<TableFactory> table;
1817 std::shared_ptr<FlushBlockPolicyFactory> result;
1818 ASSERT_NOK(FlushBlockPolicyFactory::CreateFromString(
1819 config_options_, TestFlushBlockPolicyFactory::kClassName(), &result));
1820
1821 ASSERT_OK(
1822 FlushBlockPolicyFactory::CreateFromString(config_options_, "", &result));
1823 ASSERT_NE(result, nullptr);
1824 ASSERT_STREQ(result->Name(), FlushBlockBySizePolicyFactory::kClassName());
1825
1826 ASSERT_OK(FlushBlockPolicyFactory::CreateFromString(
1827 config_options_, FlushBlockEveryKeyPolicyFactory::kClassName(), &result));
1828 ASSERT_NE(result, nullptr);
1829 ASSERT_STREQ(result->Name(), FlushBlockEveryKeyPolicyFactory::kClassName());
1830
1831 ASSERT_OK(FlushBlockPolicyFactory::CreateFromString(
1832 config_options_, FlushBlockBySizePolicyFactory::kClassName(), &result));
1833 ASSERT_NE(result, nullptr);
1834 ASSERT_STREQ(result->Name(), FlushBlockBySizePolicyFactory::kClassName());
1835 #ifndef ROCKSDB_LITE
1836 std::string table_opts = "id=BlockBasedTable; flush_block_policy_factory=";
1837 ASSERT_OK(TableFactory::CreateFromString(
1838 config_options_,
1839 table_opts + FlushBlockEveryKeyPolicyFactory::kClassName(), &table));
1840 auto bbto = table->GetOptions<BlockBasedTableOptions>();
1841 ASSERT_NE(bbto, nullptr);
1842 ASSERT_NE(bbto->flush_block_policy_factory.get(), nullptr);
1843 ASSERT_STREQ(bbto->flush_block_policy_factory->Name(),
1844 FlushBlockEveryKeyPolicyFactory::kClassName());
1845 if (RegisterTests("Test")) {
1846 ASSERT_OK(FlushBlockPolicyFactory::CreateFromString(
1847 config_options_, TestFlushBlockPolicyFactory::kClassName(), &result));
1848 ASSERT_NE(result, nullptr);
1849 ASSERT_STREQ(result->Name(), TestFlushBlockPolicyFactory::kClassName());
1850 ASSERT_OK(TableFactory::CreateFromString(
1851 config_options_, table_opts + TestFlushBlockPolicyFactory::kClassName(),
1852 &table));
1853 bbto = table->GetOptions<BlockBasedTableOptions>();
1854 ASSERT_NE(bbto, nullptr);
1855 ASSERT_NE(bbto->flush_block_policy_factory.get(), nullptr);
1856 ASSERT_STREQ(bbto->flush_block_policy_factory->Name(),
1857 TestFlushBlockPolicyFactory::kClassName());
1858 }
1859 #endif // ROCKSDB_LITE
1860 }
1861
1862 } // namespace ROCKSDB_NAMESPACE
main(int argc,char ** argv)1863 int main(int argc, char** argv) {
1864 ::testing::InitGoogleTest(&argc, argv);
1865 ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
1866 #ifdef GFLAGS
1867 ParseCommandLineFlags(&argc, &argv, true);
1868 #endif // GFLAGS
1869 return RUN_ALL_TESTS();
1870 }
1871