Create a Customizable class to load classes and configurations (#6590)
Summary: The Customizable class is an extension of the Configurable class and allows instances to be created by a name/ID. Classes that extend customizable can define their Type (e.g. "TableFactory", "Cache") and a method to instantiate them (TableFactory::CreateFromString). Customizable objects can be registered with the ObjectRegistry and created dynamically. Future PRs will make more types of objects extend Customizable. Pull Request resolved: https://github.com/facebook/rocksdb/pull/6590 Reviewed By: cheng-chang Differential Revision: D24841553 Pulled By: zhichao-cao fbshipit-source-id: d0c2132bd932e971cbfe2c908ca2e5db30c5e155main
parent
8b6b6aeb1a
commit
c442f6809f
@ -0,0 +1,138 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once |
||||
|
||||
#include "rocksdb/configurable.h" |
||||
#include "rocksdb/status.h" |
||||
|
||||
namespace ROCKSDB_NAMESPACE { |
||||
/**
|
||||
* Customizable a base class used by the rocksdb that describes a |
||||
* standard way of configuring and creating objects. Customizable objects |
||||
* are configurable objects that can be created from an ObjectRegistry. |
||||
* |
||||
* Customizable classes are used when there are multiple potential |
||||
* implementations of a class for use by RocksDB (e.g. Table, Cache, |
||||
* MergeOperator, etc). The abstract base class is expected to define a method |
||||
* declaring its type and a factory method for creating one of these, such as: |
||||
* static const char *Type() { return "Table"; } |
||||
* static Status CreateFromString(const ConfigOptions& options, |
||||
* const std::string& id, |
||||
* std::shared_ptr<TableFactory>* result); |
||||
* The "Type" string is expected to be unique (no two base classes are the same |
||||
* type). This factory is expected, based on the options and id, create and |
||||
* return the appropriate derived type of the customizable class (e.g. |
||||
* BlockBasedTableFactory, PlainTableFactory, etc). For extension developers, |
||||
* helper classes and methods are provided for writing this factory. |
||||
* |
||||
* Instances of a Customizable class need to define: |
||||
* - A "static const char *kClassName()" method. This method defines the name |
||||
* of the class instance (e.g. BlockBasedTable, LRUCache) and is used by the |
||||
* CheckedCast method. |
||||
* - The Name() of the object. This name is used when creating and saving |
||||
* instances of this class. Typically this name will be the same as |
||||
* kClassName(). |
||||
* |
||||
* Additionally, Customizable classes should register any options used to |
||||
* configure themselves with the Configurable subsystem. |
||||
* |
||||
* When a Customizable is being created, the "name" property specifies |
||||
* the name of the instance being created. |
||||
* For custom objects, their configuration and name can be specified by: |
||||
* [prop]={name=X;option 1 = value1[; option2=value2...]} |
||||
* |
||||
* [prop].name=X |
||||
* [prop].option1 = value1 |
||||
* |
||||
* [prop].name=X |
||||
* X.option1 =value1 |
||||
*/ |
||||
class Customizable : public Configurable { |
||||
public: |
||||
virtual ~Customizable() {} |
||||
|
||||
// Returns the name of this class of Customizable
|
||||
virtual const char* Name() const = 0; |
||||
|
||||
// Returns an identifier for this Customizable.
|
||||
// This could be its name or something more complex (like its URL/pattern).
|
||||
// Used for pretty printing.
|
||||
virtual std::string GetId() const { |
||||
std::string id = Name(); |
||||
return id; |
||||
} |
||||
|
||||
// This is typically determined by if the input name matches the
|
||||
// name of this object.
|
||||
// This method is typically used in conjunction with CheckedCast to find the
|
||||
// derived class instance from its base. For example, if you have an Env
|
||||
// and want the "Default" env, you would IsInstanceOf("Default") to get
|
||||
// the default implementation. This method should be used when you need a
|
||||
// specific derivative or implementation of a class.
|
||||
//
|
||||
// Intermediary caches (such as SharedCache) may wish to override this method
|
||||
// to check for the intermediary name (SharedCache). Classes with multiple
|
||||
// potential names (e.g. "PosixEnv", "DefaultEnv") may also wish to override
|
||||
// this method.
|
||||
//
|
||||
// @param name The name of the instance to find.
|
||||
// Returns true if the class is an instance of the input name.
|
||||
virtual bool IsInstanceOf(const std::string& name) const { |
||||
return name == Name(); |
||||
} |
||||
|
||||
// Returns the named instance of the Customizable as a T*, or nullptr if not
|
||||
// found. This method uses IsInstanceOf to find the appropriate class instance
|
||||
// and then casts it to the expected return type.
|
||||
template <typename T> |
||||
const T* CheckedCast() const { |
||||
if (IsInstanceOf(T::kClassName())) { |
||||
return static_cast<const T*>(this); |
||||
} else { |
||||
return nullptr; |
||||
} |
||||
} |
||||
|
||||
template <typename T> |
||||
T* CheckedCast() { |
||||
if (IsInstanceOf(T::kClassName())) { |
||||
return static_cast<T*>(this); |
||||
} else { |
||||
return nullptr; |
||||
} |
||||
} |
||||
|
||||
// Checks to see if this Customizable is equivalent to other.
|
||||
// This method assumes that the two objects are of the same class.
|
||||
// @param config_options Controls how the options are compared.
|
||||
// @param other The other object to compare to.
|
||||
// @param mismatch If the objects do not match, this parameter contains
|
||||
// the name of the option that triggered the match failure.
|
||||
// @param True if the objects match, false otherwise.
|
||||
// @see Configurable::AreEquivalent for more details
|
||||
bool AreEquivalent(const ConfigOptions& config_options, |
||||
const Configurable* other, |
||||
std::string* mismatch) const override; |
||||
#ifndef ROCKSDB_LITE |
||||
// Gets the value of the option associated with the input name
|
||||
// @see Configurable::GetOption for more details
|
||||
Status GetOption(const ConfigOptions& config_options, const std::string& name, |
||||
std::string* value) const override; |
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
protected: |
||||
// Given a name (e.g. rocksdb.my.type.opt), returns the short name (opt)
|
||||
std::string GetOptionName(const std::string& long_name) const override; |
||||
#ifndef ROCKSDB_LITE |
||||
std::string SerializeOptions(const ConfigOptions& options, |
||||
const std::string& prefix) const override; |
||||
#endif // ROCKSDB_LITE
|
||||
}; |
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
@ -0,0 +1,77 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "rocksdb/customizable.h" |
||||
|
||||
#include "options/configurable_helper.h" |
||||
#include "rocksdb/convenience.h" |
||||
#include "rocksdb/status.h" |
||||
#include "util/string_util.h" |
||||
|
||||
namespace ROCKSDB_NAMESPACE { |
||||
|
||||
std::string Customizable::GetOptionName(const std::string& long_name) const { |
||||
const std::string& name = Name(); |
||||
size_t name_len = name.size(); |
||||
if (long_name.size() > name_len + 1 && |
||||
long_name.compare(0, name_len, name) == 0 && |
||||
long_name.at(name_len) == '.') { |
||||
return long_name.substr(name_len + 1); |
||||
} else { |
||||
return Configurable::GetOptionName(long_name); |
||||
} |
||||
} |
||||
|
||||
#ifndef ROCKSDB_LITE |
||||
Status Customizable::GetOption(const ConfigOptions& config_options, |
||||
const std::string& opt_name, |
||||
std::string* value) const { |
||||
if (opt_name == ConfigurableHelper::kIdPropName) { |
||||
*value = GetId(); |
||||
return Status::OK(); |
||||
} else { |
||||
return Configurable::GetOption(config_options, opt_name, value); |
||||
} |
||||
} |
||||
|
||||
std::string Customizable::SerializeOptions(const ConfigOptions& config_options, |
||||
const std::string& prefix) const { |
||||
std::string result; |
||||
std::string parent; |
||||
if (!config_options.IsShallow()) { |
||||
parent = Configurable::SerializeOptions(config_options, ""); |
||||
} |
||||
if (parent.empty()) { |
||||
result = GetId(); |
||||
} else { |
||||
result.append(prefix + ConfigurableHelper::kIdPropName + "=" + GetId() + |
||||
config_options.delimiter); |
||||
result.append(parent); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
bool Customizable::AreEquivalent(const ConfigOptions& config_options, |
||||
const Configurable* other, |
||||
std::string* mismatch) const { |
||||
if (config_options.sanity_level > ConfigOptions::kSanityLevelNone && |
||||
this != other) { |
||||
const Customizable* custom = reinterpret_cast<const Customizable*>(other); |
||||
if (GetId() != custom->GetId()) { |
||||
*mismatch = ConfigurableHelper::kIdPropName; |
||||
return false; |
||||
} else if (config_options.sanity_level > |
||||
ConfigOptions::kSanityLevelLooselyCompatible) { |
||||
bool matches = |
||||
Configurable::AreEquivalent(config_options, other, mismatch); |
||||
return matches; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
@ -0,0 +1,216 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once |
||||
#include <functional> |
||||
#include <memory> |
||||
#include <unordered_map> |
||||
|
||||
#include "options/configurable_helper.h" |
||||
#include "options/options_helper.h" |
||||
#include "rocksdb/convenience.h" |
||||
#include "rocksdb/customizable.h" |
||||
#include "rocksdb/status.h" |
||||
#include "rocksdb/utilities/object_registry.h" |
||||
|
||||
namespace ROCKSDB_NAMESPACE { |
||||
template <typename T> |
||||
using SharedFactoryFunc = |
||||
std::function<bool(const std::string&, std::shared_ptr<T>*)>; |
||||
|
||||
template <typename T> |
||||
using UniqueFactoryFunc = |
||||
std::function<bool(const std::string&, std::unique_ptr<T>*)>; |
||||
|
||||
template <typename T> |
||||
using StaticFactoryFunc = std::function<bool(const std::string&, T**)>; |
||||
|
||||
// Creates a new shared Customizable object based on the input parameters.
|
||||
// This method parses the input value to determine the type of instance to
|
||||
// create. If there is an existing instance (in result) and it is the same type
|
||||
// as the object being created, the existing configuration is stored and used as
|
||||
// the default for the new object.
|
||||
//
|
||||
// The value parameter specified the instance class of the object to create.
|
||||
// If it is a simple string (e.g. BlockBasedTable), then the instance will be
|
||||
// created using the default settings. If the value is a set of name-value
|
||||
// pairs, then the "id" value is used to determine the instance to create and
|
||||
// the remaining parameters are used to configure the object. Id name-value
|
||||
// pairs are specified, there must be an "id=value" pairing or an error will
|
||||
// result.
|
||||
//
|
||||
// The config_options parameter controls the process and how errors are
|
||||
// returned. If ignore_unknown_options=true, unknown values are ignored during
|
||||
// the configuration If ignore_unsupported_options=true, unknown instance types
|
||||
// are ignored If invoke_prepare_options=true, the resulting instance wll be
|
||||
// initialized (via PrepareOptions
|
||||
//
|
||||
// @param config_options Controls how the instance is created and errors are
|
||||
// handled
|
||||
// @param value Either the simple name of the instance to create, or a set of
|
||||
// name-value pairs to
|
||||
// create and initailzie the object
|
||||
// @param func Optional function to call to attempt to create an instance
|
||||
// @param result The newly created instance.
|
||||
template <typename T> |
||||
static Status LoadSharedObject(const ConfigOptions& config_options, |
||||
const std::string& value, |
||||
const SharedFactoryFunc<T>& func, |
||||
std::shared_ptr<T>* result) { |
||||
std::string id; |
||||
std::unordered_map<std::string, std::string> opt_map; |
||||
Status status = ConfigurableHelper::GetOptionsMap(value, &id, &opt_map); |
||||
if (!status.ok()) { // GetOptionsMap failed
|
||||
return status; |
||||
} |
||||
std::string curr_opts; |
||||
#ifndef ROCKSDB_LITE |
||||
if (result->get() != nullptr && result->get()->GetId() == id) { |
||||
// Try to get the existing options, ignoring any errors
|
||||
ConfigOptions embedded = config_options; |
||||
embedded.delimiter = ";"; |
||||
result->get()->GetOptionString(embedded, &curr_opts).PermitUncheckedError(); |
||||
} |
||||
#endif |
||||
if (func == nullptr || !func(id, result)) { // No factory, or it failed
|
||||
if (id.empty() && opt_map.empty()) { |
||||
// No Id and no options. Clear the object
|
||||
result->reset(); |
||||
return Status::OK(); |
||||
} else if (id.empty()) { // We have no Id but have options. Not good
|
||||
return Status::NotSupported("Cannot reset object ", id); |
||||
} else { |
||||
#ifndef ROCKSDB_LITE |
||||
status = ObjectRegistry::NewInstance()->NewSharedObject(id, result); |
||||
#else |
||||
status = Status::NotSupported("Cannot load object in LITE mode ", id); |
||||
#endif |
||||
if (!status.ok()) { |
||||
if (config_options.ignore_unsupported_options) { |
||||
return Status::OK(); |
||||
} else { |
||||
return status; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return ConfigurableHelper::ConfigureNewObject(config_options, result->get(), |
||||
id, curr_opts, opt_map); |
||||
} |
||||
|
||||
// Creates a new unique customizable instance object based on the input
|
||||
// parameters.
|
||||
// @see LoadSharedObject for more information on the inner workings of this
|
||||
// method.
|
||||
//
|
||||
// @param config_options Controls how the instance is created and errors are
|
||||
// handled
|
||||
// @param value Either the simple name of the instance to create, or a set of
|
||||
// name-value pairs to
|
||||
// create and initailzie the object
|
||||
// @param func Optional function to call to attempt to create an instance
|
||||
// @param result The newly created instance.
|
||||
template <typename T> |
||||
static Status LoadUniqueObject(const ConfigOptions& config_options, |
||||
const std::string& value, |
||||
const UniqueFactoryFunc<T>& func, |
||||
std::unique_ptr<T>* result) { |
||||
std::string id; |
||||
std::unordered_map<std::string, std::string> opt_map; |
||||
Status status = ConfigurableHelper::GetOptionsMap(value, &id, &opt_map); |
||||
if (!status.ok()) { // GetOptionsMap failed
|
||||
return status; |
||||
} |
||||
std::string curr_opts; |
||||
#ifndef ROCKSDB_LITE |
||||
if (result->get() != nullptr && result->get()->GetId() == id) { |
||||
// Try to get the existing options, ignoring any errors
|
||||
ConfigOptions embedded = config_options; |
||||
embedded.delimiter = ";"; |
||||
result->get()->GetOptionString(embedded, &curr_opts).PermitUncheckedError(); |
||||
} |
||||
#endif |
||||
if (func == nullptr || !func(id, result)) { // No factory, or it failed
|
||||
if (id.empty() && opt_map.empty()) { |
||||
// No Id and no options. Clear the object
|
||||
result->reset(); |
||||
return Status::OK(); |
||||
} else if (id.empty()) { // We have no Id but have options. Not good
|
||||
return Status::NotSupported("Cannot reset object ", id); |
||||
} else { |
||||
#ifndef ROCKSDB_LITE |
||||
status = ObjectRegistry::NewInstance()->NewUniqueObject(id, result); |
||||
#else |
||||
status = Status::NotSupported("Cannot load object in LITE mode ", id); |
||||
#endif // ROCKSDB_LITE
|
||||
if (!status.ok()) { |
||||
if (config_options.ignore_unsupported_options) { |
||||
return Status::OK(); |
||||
} else { |
||||
return status; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return ConfigurableHelper::ConfigureNewObject(config_options, result->get(), |
||||
id, curr_opts, opt_map); |
||||
} |
||||
// Creates a new static (raw pointer) customizable instance object based on the
|
||||
// input parameters.
|
||||
// @see LoadSharedObject for more information on the inner workings of this
|
||||
// method.
|
||||
//
|
||||
// @param config_options Controls how the instance is created and errors are
|
||||
// handled
|
||||
// @param value Either the simple name of the instance to create, or a set of
|
||||
// name-value pairs to
|
||||
// create and initailzie the object
|
||||
// @param func Optional function to call to attempt to create an instance
|
||||
// @param result The newly created instance.
|
||||
template <typename T> |
||||
static Status LoadStaticObject(const ConfigOptions& config_options, |
||||
const std::string& value, |
||||
const StaticFactoryFunc<T>& func, T** result) { |
||||
std::string id; |
||||
std::unordered_map<std::string, std::string> opt_map; |
||||
Status status = ConfigurableHelper::GetOptionsMap(value, &id, &opt_map); |
||||
if (!status.ok()) { // GetOptionsMap failed
|
||||
return status; |
||||
} |
||||
std::string curr_opts; |
||||
#ifndef ROCKSDB_LITE |
||||
if (*result != nullptr && (*result)->GetId() == id) { |
||||
// Try to get the existing options, ignoring any errors
|
||||
ConfigOptions embedded = config_options; |
||||
embedded.delimiter = ";"; |
||||
(*result)->GetOptionString(embedded, &curr_opts).PermitUncheckedError(); |
||||
} |
||||
#endif |
||||
if (func == nullptr || !func(id, result)) { // No factory, or it failed
|
||||
if (id.empty() && opt_map.empty()) { |
||||
// No Id and no options. Clear the object
|
||||
*result = nullptr; |
||||
return Status::OK(); |
||||
} else if (id.empty()) { // We have no Id but have options. Not good
|
||||
return Status::NotSupported("Cannot reset object ", id); |
||||
} else { |
||||
#ifndef ROCKSDB_LITE |
||||
status = ObjectRegistry::NewInstance()->NewStaticObject(id, result); |
||||
#else |
||||
status = Status::NotSupported("Cannot load object in LITE mode ", id); |
||||
#endif // ROCKSDB_LITE
|
||||
if (!status.ok()) { |
||||
if (config_options.ignore_unsupported_options) { |
||||
return Status::OK(); |
||||
} else { |
||||
return status; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return ConfigurableHelper::ConfigureNewObject(config_options, *result, id, |
||||
curr_opts, opt_map); |
||||
} |
||||
} // namespace ROCKSDB_NAMESPACE
|
@ -0,0 +1,625 @@ |
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "rocksdb/customizable.h" |
||||
|
||||
#include <cctype> |
||||
#include <cinttypes> |
||||
#include <cstring> |
||||
#include <unordered_map> |
||||
|
||||
#include "options/configurable_helper.h" |
||||
#include "options/customizable_helper.h" |
||||
#include "options/options_helper.h" |
||||
#include "options/options_parser.h" |
||||
#include "rocksdb/convenience.h" |
||||
#include "rocksdb/utilities/object_registry.h" |
||||
#include "rocksdb/utilities/options_type.h" |
||||
#include "table/mock_table.h" |
||||
#include "test_util/testharness.h" |
||||
#include "test_util/testutil.h" |
||||
|
||||
#ifndef GFLAGS |
||||
bool FLAGS_enable_print = false; |
||||
#else |
||||
#include "util/gflags_compat.h" |
||||
using GFLAGS_NAMESPACE::ParseCommandLineFlags; |
||||
DEFINE_bool(enable_print, false, "Print options generated to console."); |
||||
#endif // GFLAGS
|
||||
|
||||
namespace ROCKSDB_NAMESPACE { |
||||
class StringLogger : public Logger { |
||||
public: |
||||
using Logger::Logv; |
||||
void Logv(const char* format, va_list ap) override { |
||||
char buffer[1000]; |
||||
vsnprintf(buffer, sizeof(buffer), format, ap); |
||||
string_.append(buffer); |
||||
} |
||||
const std::string& str() const { return string_; } |
||||
void clear() { string_.clear(); } |
||||
|
||||
private: |
||||
std::string string_; |
||||
}; |
||||
|
||||
class TestCustomizable : public Customizable { |
||||
public: |
||||
TestCustomizable(const std::string& name) : name_(name) {} |
||||
// Method to allow CheckedCast to work for this class
|
||||
static const char* kClassName() { |
||||
return "TestCustomizable"; |
||||
; |
||||
} |
||||
|
||||
const char* Name() const override { return name_.c_str(); } |
||||
static const char* Type() { return "test.custom"; } |
||||
static Status CreateFromString(const ConfigOptions& opts, |
||||
const std::string& value, |
||||
std::unique_ptr<TestCustomizable>* result); |
||||
static Status CreateFromString(const ConfigOptions& opts, |
||||
const std::string& value, |
||||
std::shared_ptr<TestCustomizable>* result); |
||||
static Status CreateFromString(const ConfigOptions& opts, |
||||
const std::string& value, |
||||
TestCustomizable** result); |
||||
bool IsInstanceOf(const std::string& name) const override { |
||||
if (name == kClassName()) { |
||||
return true; |
||||
} else { |
||||
return Customizable::IsInstanceOf(name); |
||||
} |
||||
} |
||||
|
||||
protected: |
||||
const std::string name_; |
||||
}; |
||||
|
||||
struct AOptions { |
||||
int i = 0; |
||||
bool b = false; |
||||
}; |
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo> a_option_info = { |
||||
#ifndef ROCKSDB_LITE |
||||
{"int", |
||||
{offsetof(struct AOptions, i), OptionType::kInt, |
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone}}, |
||||
{"bool", |
||||
{offsetof(struct AOptions, b), OptionType::kBoolean, |
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone}}, |
||||
#endif // ROCKSDB_LITE
|
||||
}; |
||||
class ACustomizable : public TestCustomizable { |
||||
public: |
||||
ACustomizable(const std::string& id) : TestCustomizable("A"), id_(id) { |
||||
ConfigurableHelper::RegisterOptions(*this, "A", &opts_, &a_option_info); |
||||
} |
||||
std::string GetId() const override { return id_; } |
||||
static const char* kClassName() { return "A"; } |
||||
|
||||
private: |
||||
AOptions opts_; |
||||
const std::string id_; |
||||
}; |
||||
|
||||
#ifndef ROCKSDB_LITE |
||||
static int A_count = 0; |
||||
const FactoryFunc<TestCustomizable>& a_func = |
||||
ObjectLibrary::Default()->Register<TestCustomizable>( |
||||
"A.*", |
||||
[](const std::string& name, std::unique_ptr<TestCustomizable>* guard, |
||||
std::string* /* msg */) { |
||||
guard->reset(new ACustomizable(name)); |
||||
A_count++; |
||||
return guard->get(); |
||||
}); |
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
struct BOptions { |
||||
std::string s; |
||||
bool b = false; |
||||
}; |
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo> b_option_info = { |
||||
#ifndef ROCKSDB_LITE |
||||
{"string", |
||||
{offsetof(struct BOptions, s), OptionType::kString, |
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone}}, |
||||
{"bool", |
||||
{offsetof(struct BOptions, b), OptionType::kBoolean, |
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone}}, |
||||
#endif // ROCKSDB_LITE
|
||||
}; |
||||
|
||||
class BCustomizable : public TestCustomizable { |
||||
private: |
||||
public: |
||||
BCustomizable(const std::string& name) : TestCustomizable(name) { |
||||
ConfigurableHelper::RegisterOptions(*this, name, &opts_, &b_option_info); |
||||
} |
||||
static const char* kClassName() { return "B"; } |
||||
|
||||
private: |
||||
BOptions opts_; |
||||
}; |
||||
|
||||
static bool LoadSharedB(const std::string& id, |
||||
std::shared_ptr<TestCustomizable>* result) { |
||||
if (id == "B") { |
||||
result->reset(new BCustomizable(id)); |
||||
return true; |
||||
} else if (id.empty()) { |
||||
result->reset(); |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
Status TestCustomizable::CreateFromString( |
||||
const ConfigOptions& config_options, const std::string& value, |
||||
std::shared_ptr<TestCustomizable>* result) { |
||||
return LoadSharedObject<TestCustomizable>(config_options, value, LoadSharedB, |
||||
result); |
||||
} |
||||
|
||||
Status TestCustomizable::CreateFromString( |
||||
const ConfigOptions& config_options, const std::string& value, |
||||
std::unique_ptr<TestCustomizable>* result) { |
||||
return LoadUniqueObject<TestCustomizable>( |
||||
config_options, value, |
||||
[](const std::string& id, std::unique_ptr<TestCustomizable>* u) { |
||||
if (id == "B") { |
||||
u->reset(new BCustomizable(id)); |
||||
return true; |
||||
} else if (id.empty()) { |
||||
u->reset(); |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
}, |
||||
result); |
||||
} |
||||
|
||||
Status TestCustomizable::CreateFromString(const ConfigOptions& config_options, |
||||
const std::string& value, |
||||
TestCustomizable** result) { |
||||
return LoadStaticObject<TestCustomizable>( |
||||
config_options, value, |
||||
[](const std::string& id, TestCustomizable** ptr) { |
||||
if (id == "B") { |
||||
*ptr = new BCustomizable(id); |
||||
return true; |
||||
} else if (id.empty()) { |
||||
*ptr = nullptr; |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
}, |
||||
result); |
||||
} |
||||
|
||||
#ifndef ROCKSDB_LITE |
||||
const FactoryFunc<TestCustomizable>& s_func = |
||||
ObjectLibrary::Default()->Register<TestCustomizable>( |
||||
"S", [](const std::string& name, |
||||
std::unique_ptr<TestCustomizable>* /* guard */, |
||||
std::string* /* msg */) { return new BCustomizable(name); }); |
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
struct SimpleOptions { |
||||
bool b = true; |
||||
std::unique_ptr<TestCustomizable> cu; |
||||
std::shared_ptr<TestCustomizable> cs; |
||||
TestCustomizable* cp = nullptr; |
||||
}; |
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo> simple_option_info = { |
||||
#ifndef ROCKSDB_LITE |
||||
{"bool", |
||||
{offsetof(struct SimpleOptions, b), OptionType::kBoolean, |
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone}}, |
||||
{"unique", OptionTypeInfo::AsCustomUniquePtr<TestCustomizable>( |
||||
offsetof(struct SimpleOptions, cu), |
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone)}, |
||||
{"shared", OptionTypeInfo::AsCustomSharedPtr<TestCustomizable>( |
||||
offsetof(struct SimpleOptions, cs), |
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone)}, |
||||
{"pointer", OptionTypeInfo::AsCustomRawPtr<TestCustomizable>( |
||||
offsetof(struct SimpleOptions, cp), |
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone)}, |
||||
#endif // ROCKSDB_LITE
|
||||
}; |
||||
|
||||
class SimpleConfigurable : public Configurable { |
||||
private: |
||||
SimpleOptions simple_; |
||||
|
||||
public: |
||||
SimpleConfigurable() { |
||||
ConfigurableHelper::RegisterOptions(*this, "simple", &simple_, |
||||
&simple_option_info); |
||||
} |
||||
|
||||
SimpleConfigurable( |
||||
const std::unordered_map<std::string, OptionTypeInfo>* map) { |
||||
ConfigurableHelper::RegisterOptions(*this, "simple", &simple_, map); |
||||
} |
||||
}; |
||||
|
||||
class CustomizableTest : public testing::Test { |
||||
public: |
||||
ConfigOptions config_options_; |
||||
}; |
||||
|
||||
#ifndef ROCKSDB_LITE // GetOptionsFromMap is not supported in ROCKSDB_LITE
|
||||
// Tests that a Customizable can be created by:
|
||||
// - a simple name
|
||||
// - a XXX.id option
|
||||
// - a property with a name
|
||||
TEST_F(CustomizableTest, CreateByNameTest) { |
||||
ObjectLibrary::Default()->Register<TestCustomizable>( |
||||
"TEST.*", |
||||
[](const std::string& name, std::unique_ptr<TestCustomizable>* guard, |
||||
std::string* /* msg */) { |
||||
guard->reset(new TestCustomizable(name)); |
||||
return guard->get(); |
||||
}); |
||||
std::unique_ptr<Configurable> configurable(new SimpleConfigurable()); |
||||
SimpleOptions* simple = configurable->GetOptions<SimpleOptions>("simple"); |
||||
ASSERT_NE(simple, nullptr); |
||||
ASSERT_OK( |
||||
configurable->ConfigureFromString(config_options_, "unique={id=TEST_1}")); |
||||
ASSERT_NE(simple->cu, nullptr); |
||||
ASSERT_EQ(simple->cu->GetId(), "TEST_1"); |
||||
ASSERT_OK( |
||||
configurable->ConfigureFromString(config_options_, "unique.id=TEST_2")); |
||||
ASSERT_NE(simple->cu, nullptr); |
||||
ASSERT_EQ(simple->cu->GetId(), "TEST_2"); |
||||
ASSERT_OK( |
||||
configurable->ConfigureFromString(config_options_, "unique=TEST_3")); |
||||
ASSERT_NE(simple->cu, nullptr); |
||||
ASSERT_EQ(simple->cu->GetId(), "TEST_3"); |
||||
} |
||||
|
||||
TEST_F(CustomizableTest, ToStringTest) { |
||||
std::unique_ptr<TestCustomizable> custom(new TestCustomizable("test")); |
||||
ASSERT_EQ(custom->ToString(config_options_), "test"); |
||||
} |
||||
|
||||
TEST_F(CustomizableTest, SimpleConfigureTest) { |
||||
std::unordered_map<std::string, std::string> opt_map = { |
||||
{"unique", "id=A;int=1;bool=true"}, |
||||
{"shared", "id=B;string=s"}, |
||||
}; |
||||
std::unique_ptr<Configurable> configurable(new SimpleConfigurable()); |
||||
ASSERT_OK(configurable->ConfigureFromMap(config_options_, opt_map)); |
||||
SimpleOptions* simple = configurable->GetOptions<SimpleOptions>("simple"); |
||||
ASSERT_NE(simple, nullptr); |
||||
ASSERT_NE(simple->cu, nullptr); |
||||
ASSERT_EQ(simple->cu->GetId(), "A"); |
||||
std::string opt_str; |
||||
std::string mismatch; |
||||
ASSERT_OK(configurable->GetOptionString(config_options_, &opt_str)); |
||||
std::unique_ptr<Configurable> copy(new SimpleConfigurable()); |
||||
ASSERT_OK(copy->ConfigureFromString(config_options_, opt_str)); |
||||
ASSERT_TRUE( |
||||
configurable->AreEquivalent(config_options_, copy.get(), &mismatch)); |
||||
} |
||||
|
||||
static void GetMapFromProperties( |
||||
const std::string& props, |
||||
std::unordered_map<std::string, std::string>* map) { |
||||
std::istringstream iss(props); |
||||
std::unordered_map<std::string, std::string> copy_map; |
||||
std::string line; |
||||
map->clear(); |
||||
for (int line_num = 0; std::getline(iss, line); line_num++) { |
||||
std::string name; |
||||
std::string value; |
||||
ASSERT_OK( |
||||
RocksDBOptionsParser::ParseStatement(&name, &value, line, line_num)); |
||||
(*map)[name] = value; |
||||
} |
||||
} |
||||
|
||||
TEST_F(CustomizableTest, ConfigureFromPropsTest) { |
||||
std::unordered_map<std::string, std::string> opt_map = { |
||||
{"unique.id", "A"}, {"unique.A.int", "1"}, {"unique.A.bool", "true"}, |
||||
{"shared.id", "B"}, {"shared.B.string", "s"}, |
||||
}; |
||||
std::unique_ptr<Configurable> configurable(new SimpleConfigurable()); |
||||
ASSERT_OK(configurable->ConfigureFromMap(config_options_, opt_map)); |
||||
SimpleOptions* simple = configurable->GetOptions<SimpleOptions>("simple"); |
||||
ASSERT_NE(simple, nullptr); |
||||
ASSERT_NE(simple->cu, nullptr); |
||||
ASSERT_EQ(simple->cu->GetId(), "A"); |
||||
std::string opt_str; |
||||
std::string mismatch; |
||||
config_options_.delimiter = "\n"; |
||||
std::unordered_map<std::string, std::string> props; |
||||
ASSERT_OK(configurable->GetOptionString(config_options_, &opt_str)); |
||||
GetMapFromProperties(opt_str, &props); |
||||
std::unique_ptr<Configurable> copy(new SimpleConfigurable()); |
||||
ASSERT_OK(copy->ConfigureFromMap(config_options_, props)); |
||||
ASSERT_TRUE( |
||||
configurable->AreEquivalent(config_options_, copy.get(), &mismatch)); |
||||
} |
||||
|
||||
TEST_F(CustomizableTest, ConfigureFromShortTest) { |
||||
std::unordered_map<std::string, std::string> opt_map = { |
||||
{"unique.id", "A"}, {"unique.A.int", "1"}, {"unique.A.bool", "true"}, |
||||
{"shared.id", "B"}, {"shared.B.string", "s"}, |
||||
}; |
||||
std::unique_ptr<Configurable> configurable(new SimpleConfigurable()); |
||||
ASSERT_OK(configurable->ConfigureFromMap(config_options_, opt_map)); |
||||
SimpleOptions* simple = configurable->GetOptions<SimpleOptions>("simple"); |
||||
ASSERT_NE(simple, nullptr); |
||||
ASSERT_NE(simple->cu, nullptr); |
||||
ASSERT_EQ(simple->cu->GetId(), "A"); |
||||
} |
||||
|
||||
TEST_F(CustomizableTest, AreEquivalentOptionsTest) { |
||||
std::unordered_map<std::string, std::string> opt_map = { |
||||
{"unique", "id=A;int=1;bool=true"}, |
||||
{"shared", "id=A;int=1;bool=true"}, |
||||
}; |
||||
std::string mismatch; |
||||
ConfigOptions config_options = config_options_; |
||||
config_options.invoke_prepare_options = false; |
||||
std::unique_ptr<Configurable> c1(new SimpleConfigurable()); |
||||
std::unique_ptr<Configurable> c2(new SimpleConfigurable()); |
||||
ASSERT_OK(c1->ConfigureFromMap(config_options, opt_map)); |
||||
ASSERT_OK(c2->ConfigureFromMap(config_options, opt_map)); |
||||
ASSERT_TRUE(c1->AreEquivalent(config_options, c2.get(), &mismatch)); |
||||
SimpleOptions* simple = c1->GetOptions<SimpleOptions>("simple"); |
||||
ASSERT_TRUE( |
||||
simple->cu->AreEquivalent(config_options, simple->cs.get(), &mismatch)); |
||||
ASSERT_OK(simple->cu->ConfigureOption(config_options, "int", "2")); |
||||
ASSERT_FALSE( |
||||
simple->cu->AreEquivalent(config_options, simple->cs.get(), &mismatch)); |
||||
ASSERT_FALSE(c1->AreEquivalent(config_options, c2.get(), &mismatch)); |
||||
ConfigOptions loosely = config_options; |
||||
loosely.sanity_level = ConfigOptions::kSanityLevelLooselyCompatible; |
||||
ASSERT_TRUE(c1->AreEquivalent(loosely, c2.get(), &mismatch)); |
||||
ASSERT_TRUE(simple->cu->AreEquivalent(loosely, simple->cs.get(), &mismatch)); |
||||
|
||||
ASSERT_OK(c1->ConfigureOption(config_options, "shared", "id=B;string=3")); |
||||
ASSERT_TRUE(c1->AreEquivalent(loosely, c2.get(), &mismatch)); |
||||
ASSERT_FALSE(c1->AreEquivalent(config_options, c2.get(), &mismatch)); |
||||
ASSERT_FALSE(simple->cs->AreEquivalent(loosely, simple->cu.get(), &mismatch)); |
||||
simple->cs.reset(); |
||||
ASSERT_TRUE(c1->AreEquivalent(loosely, c2.get(), &mismatch)); |
||||
ASSERT_FALSE(c1->AreEquivalent(config_options, c2.get(), &mismatch)); |
||||
} |
||||
|
||||
// Tests that we can initialize a customizable from its options
|
||||
TEST_F(CustomizableTest, ConfigureStandaloneCustomTest) { |
||||
std::unique_ptr<TestCustomizable> base, copy; |
||||
auto registry = ObjectRegistry::NewInstance(); |
||||
ASSERT_OK(registry->NewUniqueObject<TestCustomizable>("A", &base)); |
||||
ASSERT_OK(registry->NewUniqueObject<TestCustomizable>("A", ©)); |
||||
ASSERT_OK(base->ConfigureFromString(config_options_, "int=33;bool=true")); |
||||
std::string opt_str; |
||||
std::string mismatch; |
||||
ASSERT_OK(base->GetOptionString(config_options_, &opt_str)); |
||||
ASSERT_OK(copy->ConfigureFromString(config_options_, opt_str)); |
||||
ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch)); |
||||
} |
||||
|
||||
// Tests that we fail appropriately if the pattern is not registered
|
||||
TEST_F(CustomizableTest, BadNameTest) { |
||||
config_options_.ignore_unsupported_options = false; |
||||
std::unique_ptr<Configurable> c1(new SimpleConfigurable()); |
||||
ASSERT_NOK( |
||||
c1->ConfigureFromString(config_options_, "unique.shared.id=bad name")); |
||||
config_options_.ignore_unsupported_options = true; |
||||
ASSERT_OK( |
||||
c1->ConfigureFromString(config_options_, "unique.shared.id=bad name")); |
||||
} |
||||
|
||||
// Tests that we fail appropriately if a bad option is passed to the underlying
|
||||
// configurable
|
||||
TEST_F(CustomizableTest, BadOptionTest) { |
||||
std::unique_ptr<Configurable> c1(new SimpleConfigurable()); |
||||
ConfigOptions ignore = config_options_; |
||||
ignore.ignore_unknown_options = true; |
||||
|
||||
ASSERT_NOK(c1->ConfigureFromString(config_options_, "A.int=11")); |
||||
ASSERT_NOK(c1->ConfigureFromString(config_options_, "shared={id=B;int=1}")); |
||||
ASSERT_OK(c1->ConfigureFromString(ignore, "shared={id=A;string=s}")); |
||||
ASSERT_NOK(c1->ConfigureFromString(config_options_, "B.int=11")); |
||||
ASSERT_OK(c1->ConfigureFromString(ignore, "B.int=11")); |
||||
ASSERT_NOK(c1->ConfigureFromString(config_options_, "A.string=s")); |
||||
ASSERT_OK(c1->ConfigureFromString(ignore, "A.string=s")); |
||||
// Test as detached
|
||||
ASSERT_NOK( |
||||
c1->ConfigureFromString(config_options_, "shared.id=A;A.string=b}")); |
||||
ASSERT_OK(c1->ConfigureFromString(ignore, "shared.id=A;A.string=s}")); |
||||
} |
||||
|
||||
// Tests that different IDs lead to different objects
|
||||
TEST_F(CustomizableTest, UniqueIdTest) { |
||||
std::unique_ptr<Configurable> base(new SimpleConfigurable()); |
||||
ASSERT_OK(base->ConfigureFromString(config_options_, |
||||
"unique={id=A_1;int=1;bool=true}")); |
||||
SimpleOptions* simple = base->GetOptions<SimpleOptions>("simple"); |
||||
ASSERT_NE(simple, nullptr); |
||||
ASSERT_NE(simple->cu, nullptr); |
||||
ASSERT_EQ(simple->cu->GetId(), std::string("A_1")); |
||||
std::string opt_str; |
||||
std::string mismatch; |
||||
ASSERT_OK(base->GetOptionString(config_options_, &opt_str)); |
||||
std::unique_ptr<Configurable> copy(new SimpleConfigurable()); |
||||
ASSERT_OK(copy->ConfigureFromString(config_options_, opt_str)); |
||||
ASSERT_TRUE(base->AreEquivalent(config_options_, copy.get(), &mismatch)); |
||||
ASSERT_OK(base->ConfigureFromString(config_options_, |
||||
"unique={id=A_2;int=1;bool=true}")); |
||||
ASSERT_FALSE(base->AreEquivalent(config_options_, copy.get(), &mismatch)); |
||||
ASSERT_EQ(simple->cu->GetId(), std::string("A_2")); |
||||
} |
||||
|
||||
TEST_F(CustomizableTest, IsInstanceOfTest) { |
||||
std::shared_ptr<TestCustomizable> tc = std::make_shared<ACustomizable>("A"); |
||||
|
||||
ASSERT_TRUE(tc->IsInstanceOf("A")); |
||||
ASSERT_TRUE(tc->IsInstanceOf("TestCustomizable")); |
||||
ASSERT_FALSE(tc->IsInstanceOf("B")); |
||||
ASSERT_EQ(tc->CheckedCast<ACustomizable>(), tc.get()); |
||||
ASSERT_EQ(tc->CheckedCast<TestCustomizable>(), tc.get()); |
||||
ASSERT_EQ(tc->CheckedCast<BCustomizable>(), nullptr); |
||||
|
||||
tc.reset(new BCustomizable("B")); |
||||
ASSERT_TRUE(tc->IsInstanceOf("B")); |
||||
ASSERT_TRUE(tc->IsInstanceOf("TestCustomizable")); |
||||
ASSERT_FALSE(tc->IsInstanceOf("A")); |
||||
ASSERT_EQ(tc->CheckedCast<BCustomizable>(), tc.get()); |
||||
ASSERT_EQ(tc->CheckedCast<TestCustomizable>(), tc.get()); |
||||
ASSERT_EQ(tc->CheckedCast<ACustomizable>(), nullptr); |
||||
} |
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo> inner_option_info = { |
||||
#ifndef ROCKSDB_LITE |
||||
{"inner", |
||||
OptionTypeInfo::AsCustomSharedPtr<TestCustomizable>( |
||||
0, OptionVerificationType::kNormal, OptionTypeFlags::kStringNameOnly)} |
||||
#endif // ROCKSDB_LITE
|
||||
}; |
||||
|
||||
class ShallowCustomizable : public Customizable { |
||||
public: |
||||
ShallowCustomizable() { |
||||
inner_ = std::make_shared<ACustomizable>("a"); |
||||
ConfigurableHelper::RegisterOptions(*this, "inner", &inner_, |
||||
&inner_option_info); |
||||
}; |
||||
static const char* kClassName() { return "shallow"; } |
||||
const char* Name() const override { return kClassName(); } |
||||
|
||||
private: |
||||
std::shared_ptr<TestCustomizable> inner_; |
||||
}; |
||||
|
||||
TEST_F(CustomizableTest, TestStringDepth) { |
||||
ConfigOptions shallow = config_options_; |
||||
std::unique_ptr<Configurable> c(new ShallowCustomizable()); |
||||
std::string opt_str; |
||||
shallow.depth = ConfigOptions::Depth::kDepthShallow; |
||||
ASSERT_OK(c->GetOptionString(shallow, &opt_str)); |
||||
ASSERT_EQ(opt_str, "inner=a;"); |
||||
shallow.depth = ConfigOptions::Depth::kDepthDetailed; |
||||
ASSERT_OK(c->GetOptionString(shallow, &opt_str)); |
||||
ASSERT_NE(opt_str, "inner=a;"); |
||||
} |
||||
|
||||
// Tests that we only get a new customizable when it changes
|
||||
TEST_F(CustomizableTest, NewCustomizableTest) { |
||||
std::unique_ptr<Configurable> base(new SimpleConfigurable()); |
||||
A_count = 0; |
||||
ASSERT_OK(base->ConfigureFromString(config_options_, |
||||
"unique={id=A_1;int=1;bool=true}")); |
||||
SimpleOptions* simple = base->GetOptions<SimpleOptions>("simple"); |
||||
ASSERT_NE(simple, nullptr); |
||||
ASSERT_NE(simple->cu, nullptr); |
||||
ASSERT_EQ(A_count, 1); // Created one A
|
||||
ASSERT_OK(base->ConfigureFromString(config_options_, |
||||
"unique={id=A_1;int=1;bool=false}")); |
||||
ASSERT_EQ(A_count, 2); // Create another A_1
|
||||
ASSERT_OK(base->ConfigureFromString(config_options_, |
||||
"unique={id=A_2;int=1;bool=false}")); |
||||
ASSERT_EQ(A_count, 3); // Created another A
|
||||
ASSERT_OK(base->ConfigureFromString(config_options_, "unique=")); |
||||
ASSERT_EQ(simple->cu, nullptr); |
||||
ASSERT_EQ(A_count, 3); |
||||
} |
||||
|
||||
TEST_F(CustomizableTest, IgnoreUnknownObjects) { |
||||
ConfigOptions ignore = config_options_; |
||||
std::shared_ptr<TestCustomizable> shared; |
||||
std::unique_ptr<TestCustomizable> unique; |
||||
TestCustomizable* pointer = nullptr; |
||||
ignore.ignore_unsupported_options = false; |
||||
ASSERT_NOK( |
||||
LoadSharedObject<TestCustomizable>(ignore, "Unknown", nullptr, &shared)); |
||||
ASSERT_NOK( |
||||
LoadUniqueObject<TestCustomizable>(ignore, "Unknown", nullptr, &unique)); |
||||
ASSERT_NOK( |
||||
LoadStaticObject<TestCustomizable>(ignore, "Unknown", nullptr, &pointer)); |
||||
ASSERT_EQ(shared.get(), nullptr); |
||||
ASSERT_EQ(unique.get(), nullptr); |
||||
ASSERT_EQ(pointer, nullptr); |
||||
ignore.ignore_unsupported_options = true; |
||||
ASSERT_OK( |
||||
LoadSharedObject<TestCustomizable>(ignore, "Unknown", nullptr, &shared)); |
||||
ASSERT_OK( |
||||
LoadUniqueObject<TestCustomizable>(ignore, "Unknown", nullptr, &unique)); |
||||
ASSERT_OK( |
||||
LoadStaticObject<TestCustomizable>(ignore, "Unknown", nullptr, &pointer)); |
||||
ASSERT_EQ(shared.get(), nullptr); |
||||
ASSERT_EQ(unique.get(), nullptr); |
||||
ASSERT_EQ(pointer, nullptr); |
||||
ASSERT_OK(LoadSharedObject<TestCustomizable>(ignore, "id=Unknown", nullptr, |
||||
&shared)); |
||||
ASSERT_OK(LoadUniqueObject<TestCustomizable>(ignore, "id=Unknown", nullptr, |
||||
&unique)); |
||||
ASSERT_OK(LoadStaticObject<TestCustomizable>(ignore, "id=Unknown", nullptr, |
||||
&pointer)); |
||||
ASSERT_EQ(shared.get(), nullptr); |
||||
ASSERT_EQ(unique.get(), nullptr); |
||||
ASSERT_EQ(pointer, nullptr); |
||||
ASSERT_OK(LoadSharedObject<TestCustomizable>(ignore, "id=Unknown;option=bad", |
||||
nullptr, &shared)); |
||||
ASSERT_OK(LoadUniqueObject<TestCustomizable>(ignore, "id=Unknown;option=bad", |
||||
nullptr, &unique)); |
||||
ASSERT_OK(LoadStaticObject<TestCustomizable>(ignore, "id=Unknown;option=bad", |
||||
nullptr, &pointer)); |
||||
ASSERT_EQ(shared.get(), nullptr); |
||||
ASSERT_EQ(unique.get(), nullptr); |
||||
ASSERT_EQ(pointer, nullptr); |
||||
} |
||||
|
||||
TEST_F(CustomizableTest, FactoryFunctionTest) { |
||||
std::shared_ptr<TestCustomizable> shared; |
||||
std::unique_ptr<TestCustomizable> unique; |
||||
TestCustomizable* pointer = nullptr; |
||||
ConfigOptions ignore = config_options_; |
||||
ignore.ignore_unsupported_options = false; |
||||
ASSERT_OK(TestCustomizable::CreateFromString(ignore, "B", &shared)); |
||||
ASSERT_OK(TestCustomizable::CreateFromString(ignore, "B", &unique)); |
||||
ASSERT_OK(TestCustomizable::CreateFromString(ignore, "B", &pointer)); |
||||
ASSERT_NE(shared.get(), nullptr); |
||||
ASSERT_NE(unique.get(), nullptr); |
||||
ASSERT_NE(pointer, nullptr); |
||||
delete pointer; |
||||
pointer = nullptr; |
||||
ASSERT_OK(TestCustomizable::CreateFromString(ignore, "", &shared)); |
||||
ASSERT_OK(TestCustomizable::CreateFromString(ignore, "", &unique)); |
||||
ASSERT_OK(TestCustomizable::CreateFromString(ignore, "", &pointer)); |
||||
ASSERT_EQ(shared.get(), nullptr); |
||||
ASSERT_EQ(unique.get(), nullptr); |
||||
ASSERT_EQ(pointer, nullptr); |
||||
ASSERT_NOK(TestCustomizable::CreateFromString(ignore, "option=bad", &shared)); |
||||
ASSERT_NOK(TestCustomizable::CreateFromString(ignore, "option=bad", &unique)); |
||||
ASSERT_NOK( |
||||
TestCustomizable::CreateFromString(ignore, "option=bad", &pointer)); |
||||
ASSERT_EQ(pointer, nullptr); |
||||
} |
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
#ifdef GFLAGS |
||||
ParseCommandLineFlags(&argc, &argv, true); |
||||
#endif // GFLAGS
|
||||
return RUN_ALL_TESTS(); |
||||
} |
Loading…
Reference in new issue