From d7225fa01f9527fc00fc49cbb7aba8483c67e089 Mon Sep 17 00:00:00 2001 From: Pawel Kubik Date: Fri, 9 Oct 2015 13:18:13 +0200 Subject: [PATCH] config: kvstore visitors refactoring [Feature] kvstore and kvjson visitors refactoring [Cause] N/A [Solution] 1. All KVStore methods accepting non-string arguments has been moved to the FromKVStoreVisitors. 2. FromKVStoreIgnoringVisitor has been added for loading incomplete config from KVStore. 3. FromKVJsonVisitor has been removed. As replacement FromKVStoreIgnoringVisitor is now called right after FromJsonVisitor. [Verification] Build, install, run tests Change-Id: I9d77388830c6e2914b1e1d91dcad211c6ae7059e --- libs/config/exception.hpp | 16 ++ libs/config/from-kvjson-visitor.hpp | 359 ------------------------- libs/config/from-kvstore-ignoring-visitor.hpp | 97 +++++++ libs/config/from-kvstore-visitor-base.hpp | 164 +++++++++++ libs/config/from-kvstore-visitor.hpp | 66 +---- libs/config/kvstore-visitor-utils.hpp | 103 +++++++ libs/config/kvstore.cpp | 163 ++++------- libs/config/kvstore.hpp | 228 +--------------- libs/config/manager.hpp | 8 +- libs/config/to-kvstore-visitor.hpp | 62 ++++- tests/unit_tests/config/testconfig-example.hpp | 9 +- tests/unit_tests/config/ut-configuration.cpp | 1 - tests/unit_tests/config/ut-dynvisit.cpp | 14 +- tests/unit_tests/config/ut-kvstore.cpp | 177 ++++-------- 14 files changed, 569 insertions(+), 898 deletions(-) delete mode 100644 libs/config/from-kvjson-visitor.hpp create mode 100644 libs/config/from-kvstore-ignoring-visitor.hpp create mode 100644 libs/config/from-kvstore-visitor-base.hpp create mode 100644 libs/config/kvstore-visitor-utils.hpp diff --git a/libs/config/exception.hpp b/libs/config/exception.hpp index 977ec32..390dbec 100644 --- a/libs/config/exception.hpp +++ b/libs/config/exception.hpp @@ -39,6 +39,22 @@ struct ConfigException: public std::runtime_error { ConfigException(const std::string& error) : std::runtime_error(error) {} }; +/** + * No such key in the config error. + */ +struct NoKeyException: public ConfigException { + + NoKeyException(const std::string& error) : ConfigException(error) {} +}; + +/** + * Container size does not match the config. + */ +struct ContainerSizeException: public ConfigException { + + ContainerSizeException(const std::string& error): ConfigException(error) {} +}; + } // namespace config #endif // COMMON_CONFIG_EXCEPTION_HPP diff --git a/libs/config/from-kvjson-visitor.hpp b/libs/config/from-kvjson-visitor.hpp deleted file mode 100644 index e66b90d..0000000 --- a/libs/config/from-kvjson-visitor.hpp +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved - * - * Contact: Krzysztof Dynowski (k.dynowski@samsumg.com) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - - -/** - * @file - * @author Krzysztof Dynowski (k.dynowski@samsumg.com) - * @brief KVStore visitor with defaults values from json - */ - -#ifndef COMMON_CONFIG_FROM_KVJSON_VISITOR_HPP -#define COMMON_CONFIG_FROM_KVJSON_VISITOR_HPP - -#include "config/from-kvstore-visitor.hpp" -#include "config/is-union.hpp" -#include "config/visit-fields.hpp" - -#include -#include - -namespace config { - -class FromKVJsonVisitor { -public: - FromKVJsonVisitor(KVStore& store, const std::string& json, const std::string& prefix) - : mStore(store) - , mKeyPrefix(prefix) - , mIsUnion(false) - { - mObject = json_tokener_parse(json.c_str()); - if (mObject == nullptr) { - throw ConfigException("Json parsing error"); - } - } - - - ~FromKVJsonVisitor() { - json_object_put(mObject); - } - - FromKVJsonVisitor(const FromKVJsonVisitor& v) : - mStore(v.mStore), - mKeyPrefix(v.mKeyPrefix), - mObject(v.mObject ? json_object_get(v.mObject) : nullptr), - mIsUnion(v.mIsUnion) - { - } - FromKVJsonVisitor& operator=(const FromKVJsonVisitor&) = delete; - - template - void visit(const std::string& name, T& value) { - getValue(name, value); - } - - template - void visit(std::size_t& idx, T& value) { - getValue(idx, value); - idx += 1; - } - -private: - KVStore& mStore; - std::string mKeyPrefix; - json_object* mObject; - bool mIsUnion; - - // create visitor for field object (visitable object) - FromKVJsonVisitor(const FromKVJsonVisitor& v, const std::string& name, const bool isUnion) : - mStore(v.mStore), - mKeyPrefix(key(v.mKeyPrefix, name)), - mIsUnion(isUnion || v.mIsUnion) - { - json_object* object = nullptr; - if (v.mObject && !json_object_object_get_ex(v.mObject, name.c_str(), &object)) { - if (!mIsUnion) - throw ConfigException("Missing json field " + mKeyPrefix); - } - mObject = object ? json_object_get(object) : nullptr; - } - - // create visitor for vector i-th element (visitable object) - FromKVJsonVisitor(const FromKVJsonVisitor& v, int i, const bool isUnion) : - mStore(v.mStore), - mKeyPrefix(key(v.mKeyPrefix, std::to_string(i))), - mIsUnion(isUnion || v.mIsUnion) - { - json_object* object = nullptr; - if (v.mObject) { - object = json_object_array_get_idx(v.mObject, i); - checkType(object, json_type_object); - } - mObject = object ? json_object_get(object) : nullptr; - } - - template::value, int>::type = 0> - void getValue(const std::string& name, T& t) - { - getValue(name, - *reinterpret_cast::type*>(&t)); - } - - template::value && - !std::is_enum::value, int>::type = 0> - void getValue(const std::string& name, T& t) - { - std::string k = key(mKeyPrefix, name); - if (mStore.exists(k)) { - t = mStore.get(k); - } else { - json_object* object = nullptr; - if (mObject) { - json_object_object_get_ex(mObject, name.c_str(), &object); - } - if (!object) { - throw ConfigException("Missing json field " + k); - } - fromJsonObject(object, t); - } - } - - template::value, int>::type = 0> - void getValue(const std::string& name, T& t) - { - FromKVJsonVisitor visitor(*this, name, true); - t.accept(visitor); - } - - template::value && !isUnion::value, int>::type = 0> - void getValue(const std::string& name, T& t) - { - FromKVJsonVisitor visitor(*this, name, false); - t.accept(visitor); - } - - int getArraySize(std::string& name, json_object* object) - { - if (mStore.exists(name)) { - return mStore.get(name); - } - if (object) { - return json_object_array_length(object); - } - return -1; - } - - template - void getValue(const std::string& name, std::vector& value) - { - json_object* object = nullptr; - if (mObject && json_object_object_get_ex(mObject, name.c_str(), &object)) { - checkType(object, json_type_array); - } - - std::string k = key(mKeyPrefix, name); - int length = getArraySize(k, object); - if (length < 0) { - throw ConfigException("Missing array length " + k); - } - value.resize(static_cast(length)); - FromKVJsonVisitor visitor(*this, name, false); - if (mStore.exists(k)) { - json_object_put(visitor.mObject); - visitor.mObject = nullptr; - } - for (int i = 0; i < length; ++i) { - visitor.getValue(i, value[i]); - } - } - - template - void getValue(const std::string& name, std::array& values) - { - json_object* object = nullptr; - if (mObject && json_object_object_get_ex(mObject, name.c_str(), &object)) { - checkType(object, json_type_array); - } - - std::string k = key(mKeyPrefix, name); - FromKVJsonVisitor visitor(*this, name, false); - if (mStore.exists(k)) { - json_object_put(visitor.mObject); - visitor.mObject = nullptr; - } - for (std::size_t i = 0; i < N; ++i) { - visitor.getValue(i, values[i]); - } - } - - template - void getValue(const std::string& name, std::pair& values) - { - json_object* object = nullptr; - if (mObject && json_object_object_get_ex(mObject, name.c_str(), &object)) { - checkType(object, json_type_array); - } - - std::string k = key(mKeyPrefix, name); - FromKVJsonVisitor visitor(*this, name, false); - if (mStore.exists(k)) { - json_object_put(visitor.mObject); - visitor.mObject = nullptr; - } - - std::size_t idx = 0; - visitFields(values, &visitor, idx); - } - - template::value, int>::type = 0> - void getValue(int i, T& t) - { - std::string k = key(mKeyPrefix, std::to_string(i)); - if (mStore.exists(k)) { - t = mStore.get(k); - } - else { - json_object* object = mObject ? json_object_array_get_idx(mObject, i) : nullptr; - if (!object) { - throw ConfigException("Missing json array elem " + k); - } - fromJsonObject(object, t); - } - } - - template::value, int>::type = 0> - void getValue(int i, T& t) - { - FromKVJsonVisitor visitor(*this, i, true); - t.accept(visitor); - } - - template::value && !isUnion::value, int>::type = 0> - void getValue(int i, T& t) - { - FromKVJsonVisitor visitor(*this, i, false); - t.accept(visitor); - } - - template - void getValue(int i, std::vector& value) - { - std::string k = key(mKeyPrefix, std::to_string(i)); - int length = getArraySize(k, mObject); - value.resize(static_cast(length)); - FromKVJsonVisitor visitor(*this, i, false); - if (mStore.exists(k)) { - json_object_put(visitor.mObject); - visitor.mObject = nullptr; - } - for (int idx = 0; idx < length; ++idx) { - visitor.getValue(idx, value[idx]); - } - } - - template - void getValue(int i, std::array& values) - { - std::string k = key(mKeyPrefix, std::to_string(i)); - - FromKVJsonVisitor visitor(*this, i, false); - if (mStore.exists(k)) { - json_object_put(visitor.mObject); - visitor.mObject = nullptr; - } - for (std::size_t idx = 0; idx < N; ++idx) { - visitor.getValue(idx, values[idx]); - } - } - - static void checkType(json_object* object, json_type type) - { - if (type != json_object_get_type(object)) { - throw ConfigException("Invalid field type " + std::to_string(type)); - } - } - - static void fromJsonObject(json_object* object, int& value) - { - checkType(object, json_type_int); - std::int64_t value64 = json_object_get_int64(object); - if (value64 > INT32_MAX || value64 < INT32_MIN) { - throw ConfigException("Value out of range"); - } - value = static_cast(value64); - } - - static void fromJsonObject(json_object* object, std::int64_t& value) - { - checkType(object, json_type_int); - value = json_object_get_int64(object); - } - - static void fromJsonObject(json_object* object, std::uint32_t& value) - { - checkType(object, json_type_int); - std::int64_t value64 = json_object_get_int64(object); - if (value64 > UINT32_MAX || value64 < 0) { - throw ConfigException("Value out of range"); - } - value = static_cast(value64); - } - - static void fromJsonObject(json_object* object, std::uint64_t& value) - { - checkType(object, json_type_int); - std::int64_t value64 = json_object_get_int64(object); - if (value64 < 0) { - throw ConfigException("Value out of range"); - } - value = static_cast(value64); - } - - static void fromJsonObject(json_object* object, bool& value) - { - checkType(object, json_type_boolean); - value = json_object_get_boolean(object); - } - - static void fromJsonObject(json_object* object, double& value) - { - checkType(object, json_type_double); - value = json_object_get_double(object); - } - - static void fromJsonObject(json_object* object, std::string& value) - { - checkType(object, json_type_string); - value = json_object_get_string(object); - } - - static void fromJsonObject(json_object* object, char* &value) - { - checkType(object, json_type_string); - - int len = json_object_get_string_len(object); - value = new char[len + 1]; - std::strncpy(value, json_object_get_string(object), len); - value[len] = '\0'; - } -}; - -} // namespace config - -#endif // COMMON_CONFIG_FROM_KVJSON_VISITOR_HPP diff --git a/libs/config/from-kvstore-ignoring-visitor.hpp b/libs/config/from-kvstore-ignoring-visitor.hpp new file mode 100644 index 0000000..094db50 --- /dev/null +++ b/libs/config/from-kvstore-ignoring-visitor.hpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Pawel Kubik (p.kubik@samsung.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Pawel Kubik (p.kubik@samsung.com) + * @brief Visitor for loading from KVStore that doesn't fail on missing values + */ + +#ifndef COMMON_CONFIG_FROM_KVSTORE_IGNORING_VISITOR_HPP +#define COMMON_CONFIG_FROM_KVSTORE_IGNORING_VISITOR_HPP + +#include "config/from-kvstore-visitor.hpp" + + +namespace config { + +/** + * A variant of KVStoreVisitor that ignore non-existing fields. + * + * Allows to partially update visited structures with fields that exist in the KVStore. + */ +class FromKVStoreIgnoringVisitor : public FromKVStoreVisitorBase { +public: + FromKVStoreIgnoringVisitor(KVStore& store, const std::string& prefix) + : FromKVStoreVisitorBase(store, prefix) + { + } + + FromKVStoreIgnoringVisitor(const FromKVStoreVisitorBase& visitor, + const std::string& prefix) + : FromKVStoreVisitorBase(visitor, prefix) + { + } + + FromKVStoreVisitor& operator=(const FromKVStoreVisitor&) = delete; + +protected: + template + void visitImpl(const std::string& name, T& value) + { + try { + getInternal(name, value); + } catch (const NoKeyException& e) { + } catch (const ContainerSizeException& e) { + } + } + +private: + FromKVStoreIgnoringVisitor(const FromKVStoreIgnoringVisitor& visitor, + const std::string& prefix) + : FromKVStoreVisitorBase(visitor.mStore, prefix) + { + } + + template::value, int>::type = 0> + void getInternal(const std::string& name, T& value) + { + std::string type; + getInternal(key(name, "type"), type); + if (type.empty()) { + return; + } + + FromKVStoreIgnoringVisitor visitor(*this, name); + value.accept(visitor); + } + + template::value, int>::type = 0> + void getInternal(const std::string& name, T& value) + { + FromKVStoreVisitorBase::visitImpl(name, value); + } + + friend class FromKVStoreVisitorBase; +}; + +} // namespace config + +#endif // COMMON_CONFIG_FROM_KVSTORE_IGNORING_VISITOR_HPP diff --git a/libs/config/from-kvstore-visitor-base.hpp b/libs/config/from-kvstore-visitor-base.hpp new file mode 100644 index 0000000..a6e209c --- /dev/null +++ b/libs/config/from-kvstore-visitor-base.hpp @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Pawel Kubik (p.kubik@samsung.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Pawel Kubik (p.kubik@samsung.com) + * @brief Base of visitors for loading from KVStore + */ + +#ifndef COMMON_CONFIG_FROM_KVSTORE_VISITOR_BASE_HPP +#define COMMON_CONFIG_FROM_KVSTORE_VISITOR_BASE_HPP + +#include "config/is-visitable.hpp" +#include "config/kvstore.hpp" +#include "config/kvstore-visitor-utils.hpp" +#include "config/visit-fields.hpp" + + +namespace config { + +/** + * Base class for KVStore visitors. + * + * A curiously recurring template pattern example. This + * base-class provides a set of recursively called function templates. A child class can + * modify the base class behaviour in certain cases by defining: + * - a set of functions that match those cases + * - a template function that match any other case and calls the base class function + */ +template +class FromKVStoreVisitorBase { +public: + FromKVStoreVisitorBase& operator=(const FromKVStoreVisitorBase&) = delete; + + template + void visit(const std::string& name, T& value) + { + static_cast(this)->visitImpl(key(mKeyPrefix, name), value); + } + +protected: + KVStore& mStore; + std::string mKeyPrefix; + + template + void visitImpl(const std::string& name, T& value) + { + getInternal(name, value); + } + + FromKVStoreVisitorBase(KVStore& store, const std::string& prefix) + : mStore(store), + mKeyPrefix(prefix) + { + } + + FromKVStoreVisitorBase(const FromKVStoreVisitorBase& visitor, + const std::string& prefix) + : mStore(visitor.mStore), + mKeyPrefix(prefix) + { + } + +private: + template::value + && !std::is_enum::value, int>::type = 0> + void getInternal(const std::string& name, T& value) + { + value = fromString(mStore.get(name)); + } + + template::value, int>::type = 0> + void getInternal(const std::string& name, T& value) + { + RecursiveVisitor visitor(*this, name); + value.accept(visitor); + } + + template::value, int>::type = 0> + void getInternal(const std::string& name, T& value) + { + auto rawValue = static_cast::type>(value); + static_cast(this)->visitImpl(name, rawValue); + value = static_cast(rawValue); + } + + template + void getInternal(const std::string& name, std::vector& values) + { + size_t storedSize = 0; + getInternal(name, storedSize); + + if (storedSize == 0) { + return; + } + + values.resize(storedSize); + for (size_t i = 0; i < storedSize; ++i) { + const std::string k = key(name, std::to_string(i)); + if (!mStore.prefixExists(k)) { + throw ConfigException("Corrupted list serialization."); + } + static_cast(this)->visitImpl(k, values[i]); + } + } + + template + void getInternal(const std::string& name, std::array& values) + { + size_t storedSize = 0; + getInternal(name, storedSize); + + if (storedSize != values.size()) { + throw ContainerSizeException("Size of stored array doesn't match provided one."); + } + + for (size_t i = 0; i < storedSize; ++i) { + const std::string k = key(name, std::to_string(i)); + if (!mStore.prefixExists(k)) { + throw ConfigException("Corrupted list serialization."); + } + static_cast(this)->visitImpl(k, values[i]); + } + } + + template + void getInternal(const std::string& key, std::pair& values) + { + std::vector strValues; + + getInternal(key, strValues); + if (strValues.empty()) { + return; + } + + if (strValues.size() != sizeof...(T)) { + throw ContainerSizeException("Size of stored tuple doesn't match provided one."); + } + + GetTupleVisitor visitor; + visitFields(values, &visitor, strValues.begin()); + } +}; + +} // namespace config + +#endif // COMMON_CONFIG_FROM_KVSTORE_VISITOR_BASE_HPP diff --git a/libs/config/from-kvstore-visitor.hpp b/libs/config/from-kvstore-visitor.hpp index 56bcd14..8cb561f 100644 --- a/libs/config/from-kvstore-visitor.hpp +++ b/libs/config/from-kvstore-visitor.hpp @@ -1,7 +1,7 @@ /* * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved * - * Contact: Jan Olszak (j.olszak@samsung.com) + * Contact: Pawel Kubik (p.kubik@samsung.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,73 +18,33 @@ /** * @file - * @author Jan Olszak (j.olszak@samsung.com) - * @brief Visitor for loading from KVStore + * @author Pawel Kubik (p.kubik@samsung.com) + * @brief Default visitor for loading from KVStore */ #ifndef COMMON_CONFIG_FROM_KVSTORE_VISITOR_HPP #define COMMON_CONFIG_FROM_KVSTORE_VISITOR_HPP -#include "config/is-visitable.hpp" -#include "config/kvstore.hpp" +#include "config/from-kvstore-visitor-base.hpp" + namespace config { -class FromKVStoreVisitor { +/** + * Default KVStore visitor. + */ +class FromKVStoreVisitor : public FromKVStoreVisitorBase { public: FromKVStoreVisitor(KVStore& store, const std::string& prefix) - : mStore(store), - mKeyPrefix(prefix) - { - } - - FromKVStoreVisitor& operator=(const FromKVStoreVisitor&) = delete; - - template - void visit(const std::string& name, T& value) + : FromKVStoreVisitorBase(store, prefix) { - getInternal(key(mKeyPrefix, name), value); } -private: - KVStore& mStore; - std::string mKeyPrefix; - - FromKVStoreVisitor(const FromKVStoreVisitor& visitor, const std::string& prefix) - : mStore(visitor.mStore), - mKeyPrefix(prefix) + FromKVStoreVisitor(const FromKVStoreVisitorBase& visitor, + const std::string& prefix) + : FromKVStoreVisitorBase(visitor, prefix) { } - - template::value, int>::type = 0> - void getInternal(const std::string& name, T& value) - { - value = mStore.get(name); - } - - template::value, int>::type = 0> - void getInternal(const std::string& name, T& value) - { - FromKVStoreVisitor visitor(*this, name); - value.accept(visitor); - } - - template::value, int>::type = 0> - void getInternal(const std::string& name, std::vector& values) - { - values.clear(); - - size_t vectorSize = mStore.get(name); - if (vectorSize == 0) { - return; - } - - values.resize(vectorSize); - for (size_t i = 0; i < vectorSize; ++i) { - const std::string k = key(name, std::to_string(i)); - getInternal(k, values[i]); - } - } }; } // namespace config diff --git a/libs/config/kvstore-visitor-utils.hpp b/libs/config/kvstore-visitor-utils.hpp new file mode 100644 index 0000000..5368af4 --- /dev/null +++ b/libs/config/kvstore-visitor-utils.hpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Pawel Kubik (p.kubik@samsung.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +/** + * @file + * @author Pawel Kubik (p.kubik@samsung.com) + * @brief KVStore visitors utilities + */ + +#ifndef COMMON_CONFIG_KVSTORE_VISITOR_UTILS_HPP +#define COMMON_CONFIG_KVSTORE_VISITOR_UTILS_HPP + +namespace config { + +template +T fromString(const std::string& strValue) +{ + std::istringstream iss(strValue); + T value; + iss >> value; + return value; +} + +template +std::string toString(const T& value) +{ + std::ostringstream oss; + oss << value; + return oss.str(); +} + +/** + * Concatenates all parameters into one std::string. + * Uses '.' to connect the terms. + * @param args components of the string + * @tparam delim optional delimiter + * @tparam Args any type implementing str + * @return string created from he args + */ +template +std::string key(const Arg1& a1, const Args& ... args) +{ + std::string ret = toString(a1); + std::initializer_list strings {toString(args)...}; + for (const std::string& s : strings) { + ret += delim + s; + } + + return ret; +} + +/** + * Function added for key function completeness. + * + * @tparam delim = '.' parameter not used, added for consistency + * @return empty string + */ +template +std::string key() +{ + return std::string(); +} + +struct GetTupleVisitor +{ + template + static void visit(std::vector::iterator& it, T& value) + { + if (!it->empty()) { + value = fromString(*it); + } + ++it; + } +}; + +struct SetTupleVisitor +{ + template + static void visit(std::vector::iterator& it, const T& value) + { + *it = toString(value); + ++it; + } +}; + +} // namespace config + +#endif // COMMON_CONFIG_KVSTORE_VISITOR_UTILS_HPP diff --git a/libs/config/kvstore.cpp b/libs/config/kvstore.cpp index 25febf6..2726a8d 100644 --- a/libs/config/kvstore.cpp +++ b/libs/config/kvstore.cpp @@ -150,6 +150,43 @@ KVStore::~KVStore() assert(mTransactionDepth == 0); } +void KVStore::set(const std::string& key, const std::string& value) +{ + Transaction transaction(*this); + ScopedReset scopedReset(mSetValueStmt); + + ::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); + ::sqlite3_bind_text(mSetValueStmt->get(), 2, value.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); + + + if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) { + throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); + } + transaction.commit(); +} + +std::string KVStore::get(const std::string& key) +{ + Transaction transaction(*this); + ScopedReset scopedReset(mGetValueStmt); + + ::sqlite3_bind_text(mGetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT); + + int ret = ::sqlite3_step(mGetValueStmt->get()); + if (ret == SQLITE_DONE) { + throw NoKeyException("No value corresponding to the key: " + key + "@" + mPath); + } + if (ret != SQLITE_ROW) { + throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); + } + + std::string value = reinterpret_cast( + sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN)); + + transaction.commit(); + return value; +} + void KVStore::setupDb() { // called only from ctor, transaction is not needed @@ -161,9 +198,9 @@ void KVStore::prepareStatements() mGetValueStmt.reset( new sqlite3::Statement(mConn, "SELECT value FROM data WHERE key = ? LIMIT 1")); mGetKeyExistsStmt.reset( - // following line left in comment to have example of any subkey matching - //new sqlite3::Statement(mConn, "SELECT 1 FROM data WHERE key = ?1 OR key GLOB escapeStr(?1) || '.*' LIMIT 1")); - new sqlite3::Statement(mConn, "SELECT 1 FROM data WHERE key = ?1 LIMIT 1")); + new sqlite3::Statement(mConn, "SELECT 1 FROM data WHERE key = ?1 LIMIT 1")); + mGetKeyPrefixExistsStmt.reset( + new sqlite3::Statement(mConn, "SELECT 1 FROM data WHERE key = ?1 OR key GLOB escapeStr(?1) || '.*' LIMIT 1")); mGetIsEmptyStmt.reset( new sqlite3::Statement(mConn, "SELECT 1 FROM data LIMIT 1")); mSetValueStmt.reset( @@ -219,133 +256,33 @@ bool KVStore::exists(const std::string& key) return ret == SQLITE_ROW; } -void KVStore::remove(const std::string& key) -{ - Transaction transaction(*this); - ScopedReset scopedReset(mRemoveValuesStmt); - - ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); - - if (::sqlite3_step(mRemoveValuesStmt->get()) != SQLITE_DONE) { - throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); - } - transaction.commit(); -} - -void KVStore::setInternal(const std::string& key, const char* value) -{ - Transaction transaction(*this); - ScopedReset scopedReset(mSetValueStmt); - - ::sqlite3_bind_text(mSetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); - ::sqlite3_bind_text(mSetValueStmt->get(), 2, value, AUTO_DETERM_SIZE, SQLITE_STATIC); - - - if (::sqlite3_step(mSetValueStmt->get()) != SQLITE_DONE) { - throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); - } - transaction.commit(); -} - -void KVStore::setInternal(const std::string& key, const std::string& value) -{ - setInternal(key, value.c_str()); -} - -void KVStore::setInternal(const std::string& key, const std::initializer_list& values) -{ - setInternal(key, std::vector(values)); -} - -void KVStore::setInternal(const std::string& key, const std::vector& values) +bool KVStore::prefixExists(const std::string& key) { - if (values.size() > std::numeric_limits::max()) { - throw ConfigException("Too many values to insert"); - } - Transaction transaction(*this); + ScopedReset scopedReset(mGetKeyPrefixExistsStmt); - remove(key); - - // Save vector's capacity - setInternal(key, values.size()); - - // Save vector's elements - for (unsigned int i = 0; i < values.size(); ++i) { - setInternal(config::key(key, std::to_string(i)), - values[i]); - } - transaction.commit(); -} + ::sqlite3_bind_text(mGetKeyPrefixExistsStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT); -std::string KVStore::getInternal(const std::string& key, std::string*) -{ - Transaction transaction(*this); - ScopedReset scopedReset(mGetValueStmt); - - ::sqlite3_bind_text(mGetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT); - - int ret = ::sqlite3_step(mGetValueStmt->get()); - if (ret == SQLITE_DONE) { - throw ConfigException("No value corresponding to the key: " + key + "@" + mPath); - } - if (ret != SQLITE_ROW) { + int ret = ::sqlite3_step(mGetKeyPrefixExistsStmt->get()); + if (ret != SQLITE_DONE && ret != SQLITE_ROW) { throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); } - std::string value = reinterpret_cast( - sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN)); - transaction.commit(); - return value; + return ret == SQLITE_ROW; } -char* KVStore::getInternal(const std::string& key, char**) +void KVStore::remove(const std::string& key) { Transaction transaction(*this); - ScopedReset scopedReset(mGetValueStmt); + ScopedReset scopedReset(mRemoveValuesStmt); - ::sqlite3_bind_text(mGetValueStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_TRANSIENT); + ::sqlite3_bind_text(mRemoveValuesStmt->get(), 1, key.c_str(), AUTO_DETERM_SIZE, SQLITE_STATIC); - int ret = ::sqlite3_step(mGetValueStmt->get()); - if (ret == SQLITE_DONE) { - throw ConfigException("No value corresponding to the key: " + key + "@" + mPath); - } - if (ret != SQLITE_ROW) { + if (::sqlite3_step(mRemoveValuesStmt->get()) != SQLITE_DONE) { throw ConfigException("Error during stepping: " + mConn.getErrorMessage()); } - - const char* source = reinterpret_cast(sqlite3_column_text(mGetValueStmt->get(), FIRST_COLUMN)); - - size_t length = std::strlen(source); - char* value = new char[length + 1]; - - std::strncpy(value, source, length); - value[length] = '\0'; - - transaction.commit(); - return value; -} - -std::vector KVStore::getInternal(const std::string& key, std::vector*) -{ - Transaction transaction(*this); - - unsigned int valuesSize = get(key); - std::vector values(valuesSize); - if (valuesSize == 0) { - transaction.commit(); - return values; - } - - for (unsigned int i = 0; i < values.size(); ++i) { - values[i] = getInternal(config::key(key, std::to_string(i)), - static_cast(nullptr)); - - } - transaction.commit(); - return values; } std::vector KVStore::getKeys() diff --git a/libs/config/kvstore.hpp b/libs/config/kvstore.hpp index 40aa199..52b5328 100644 --- a/libs/config/kvstore.hpp +++ b/libs/config/kvstore.hpp @@ -27,7 +27,6 @@ #include "config/sqlite3/connection.hpp" #include "config/sqlite3/statement.hpp" -#include "config/visit-fields.hpp" #include #include @@ -82,13 +81,20 @@ public: bool isEmpty(); /** - * @param key string regexp of the stored values + * @param key string of the stored value * * @return Does this key exist in the database */ bool exists(const std::string& key); /** + * @param key string of the stored value + * + * @return Does a key starting with an argument exist in the database + */ + bool prefixExists(const std::string& key); + + /** * Removes values corresponding to the passed key. * Many values may correspond to one key, so many values may * need to be deleted @@ -103,25 +109,15 @@ public: * @param key string key of the value * @param value value corresponding to the key */ - template - void set(const std::string& key, const T& value) - { - return setInternal(key, value); - } + void set(const std::string& key, const std::string& value); /** * Gets the value corresponding to the key. - * Uses stringstreams to parse. * * @param key string key of the value - * @tparam T = std::string desired type of the return value * @return value corresponding to the key */ - template - T get(const std::string& key) - { - return getInternal(key, static_cast(nullptr)); - } + std::string get(const std::string& key); /** * Returns all stored keys. @@ -135,39 +131,11 @@ private: size_t mTransactionDepth; bool mIsTransactionCommited; - void setInternal(const std::string& key, const std::string& value); - void setInternal(const std::string& key, const char* value); - void setInternal(const std::string& key, const std::initializer_list& values); - void setInternal(const std::string& key, const std::vector& values); - template::value, int>::type = 0> - void setInternal(const std::string& key, const T& value); - template::value, int>::type = 0> - void setInternal(const std::string& key, const T& value); - template - void setInternal(const std::string& key, const std::vector& values); - template - void setInternal(const std::string& key, const std::array& values); - template - void setInternal(const std::string& key, const std::pair& values); - - std::string getInternal(const std::string& key, std::string*); - char* getInternal(const std::string& key, char**); - std::vector getInternal(const std::string& key, std::vector*); - template::value, int>::type = 0> - T getInternal(const std::string& key, T*); - template::value, int>::type = 0> - T getInternal(const std::string& key, T*); - template - std::vector getInternal(const std::string& key, std::vector*); - template - std::array getInternal(const std::string& key, std::array*); - template - std::pair getInternal(const std::string& key, std::pair*); - std::string mPath; sqlite3::Connection mConn; std::unique_ptr mGetValueStmt; std::unique_ptr mGetKeyExistsStmt; + std::unique_ptr mGetKeyPrefixExistsStmt; std::unique_ptr mGetIsEmptyStmt; std::unique_ptr mGetValueListStmt; std::unique_ptr mSetValueStmt; @@ -179,180 +147,6 @@ private: void createFunctions(); }; -namespace { -template -std::string toString(const T& value) -{ - std::ostringstream oss; - oss << value; - return oss.str(); -} - -template -T fromString(const std::string& strValue) -{ - std::istringstream iss(strValue); - T value; - iss >> value; - return value; -} - -} // namespace - -template::value, int>::type> -void KVStore::setInternal(const std::string& key, const T& value) -{ - setInternal(key, toString(value)); -} - -template::value, int>::type> -void KVStore::setInternal(const std::string& key, const T& value) -{ - setInternal(key, - static_cast::type>(value)); -} - -template -void KVStore::setInternal(const std::string& key, const std::vector& values) -{ - std::vector strValues(values.size()); - - std::transform(values.begin(), - values.end(), - strValues.begin(), - toString); - - setInternal(key, strValues); -} - -template -void KVStore::setInternal(const std::string& key, const std::array& values) -{ - std::vector strValues(N); - - std::transform(values.begin(), - values.end(), - strValues.begin(), - toString); - - setInternal(key, strValues); -} - -struct SetTupleVisitor -{ - template - static void visit(std::vector::iterator& it, const T& value) - { - *it = toString(value); - ++it; - } -}; - -template -void KVStore::setInternal(const std::string& key, const std::pair& values) -{ - std::vector strValues(std::tuple_size>::value); - - SetTupleVisitor visitor; - visitFields(values, &visitor, strValues.begin()); - setInternal(key, strValues); -} - -template::value, int>::type> -T KVStore::getInternal(const std::string& key, T*) -{ - return fromString(getInternal(key, static_cast(nullptr))); -} - -template::value, int>::type> -T KVStore::getInternal(const std::string& key, T*) -{ - return static_cast(getInternal(key, - static_cast::type*>(nullptr))); -} - -template -std::array KVStore::getInternal(const std::string& key, std::array*) -{ - std::vector strValues = getInternal(key, static_cast*>(nullptr)); - std::array values; - - std::transform(strValues.begin(), - strValues.end(), - values.begin(), - fromString); - - return values; -} - -template -std::vector KVStore::getInternal(const std::string& key, std::vector*) -{ - std::vector strValues = getInternal(key, static_cast*>(nullptr)); - std::vector values(strValues.size()); - - std::transform(strValues.begin(), - strValues.end(), - values.begin(), - fromString); - - return values; -} - -struct GetTupleVisitor -{ - template - static void visit(std::vector::iterator& it, T& value) - { - value = fromString(*it); - ++it; - } -}; - -template -std::pair KVStore::getInternal(const std::string& key, std::pair*) -{ - std::vector strValues = getInternal(key, static_cast*>(nullptr)); - std::pair values; - - GetTupleVisitor visitor; - visitFields(values, &visitor, strValues.begin()); - - return values; -} - -/** - * Concatenates all parameters into one std::string. - * Uses '.' to connect the terms. - * @param args components of the string - * @tparam delim optional delimiter - * @tparam Args any type implementing str - * @return string created from he args - */ -template -std::string key(const Arg1& a1, const Args& ... args) -{ - std::string ret = toString(a1); - std::initializer_list strings {toString(args)...}; - for (const std::string& s : strings) { - ret += delim + s; - } - - return ret; -} - -/** - * Function added for key function completeness. - * - * @tparam delim = '.' parameter not used, added for consistency - * @return empty string - */ -template -std::string key() -{ - return std::string(); -} - } // namespace config #endif // COMMON_CONFIG_KVSTORE_HPP diff --git a/libs/config/manager.hpp b/libs/config/manager.hpp index e2877df..d1df907 100644 --- a/libs/config/manager.hpp +++ b/libs/config/manager.hpp @@ -98,8 +98,8 @@ #include "config/from-gvariant-visitor.hpp" #include "config/from-json-visitor.hpp" #include "config/from-kvstore-visitor.hpp" +#include "config/from-kvstore-ignoring-visitor.hpp" #include "config/from-fdstore-visitor.hpp" -#include "config/from-kvjson-visitor.hpp" #include "config/fs-utils.hpp" #include "config/is-union.hpp" @@ -260,8 +260,10 @@ void loadFromKVStoreWithJson(const std::string& kvfile, KVStore store(kvfile); KVStore::Transaction transaction(store); - FromKVJsonVisitor visitor(store, json, kvConfigName); - config.accept(visitor); + FromJsonVisitor fromJsonVisitor(json); + FromKVStoreIgnoringVisitor fromKVStoreVisitor(store, kvConfigName); + config.accept(fromJsonVisitor); + config.accept(fromKVStoreVisitor); transaction.commit(); } diff --git a/libs/config/to-kvstore-visitor.hpp b/libs/config/to-kvstore-visitor.hpp index 14944dc..6992262 100644 --- a/libs/config/to-kvstore-visitor.hpp +++ b/libs/config/to-kvstore-visitor.hpp @@ -27,6 +27,7 @@ #include "config/is-visitable.hpp" #include "config/kvstore.hpp" +#include "config/kvstore-visitor-utils.hpp" namespace config { @@ -57,10 +58,12 @@ private: { } - template::value, int>::type = 0> + template::value + && !std::is_enum::value, int>::type = 0> void setInternal(const std::string& name, const T& value) { - mStore.set(name, value); + mStore.set(name, toString(value)); } template::value, int>::type = 0> @@ -70,14 +73,59 @@ private: value.accept(visitor); } - template::value, int>::type = 0> - void setInternal(const std::string& name, const std::vector& values) + template::value, int>::type = 0> + void setInternal(const std::string& name, const T& value) { + setInternal(name, static_cast::type>(value)); + } + + template + void setRangeInternal(const std::string& name, + const I& begin, + const I& end, + const size_t size) { + if (size > std::numeric_limits::max()) { + throw ConfigException("Too many values to insert"); + } + + KVStore::Transaction transaction(mStore); + mStore.remove(name); - mStore.set(name, values.size()); - for (size_t i = 0; i < values.size(); ++i) { - setInternal(key(name, std::to_string(i)), values[i]); + setInternal(name, size); + size_t i = 0; + for (auto it = begin; it != end; ++it) { + const std::string k = key(name, std::to_string(i)); + setInternal(k, *it); + ++i; } + transaction.commit(); + } + + template + void setInternal(const std::string& name, const std::vector& values) + { + setRangeInternal(name, values.begin(), values.end(), values.size()); + } + + template + void setInternal(const std::string& name, const std::array& values) { + setRangeInternal(name, values.begin(), values.end(), N); + } + + template + void setInternal(const std::string& name, const std::initializer_list& values) + { + setRangeInternal(name, values.begin(), values.end(), values.size()); + } + + template + void setInternal(const std::string& key, const std::pair& values) + { + std::vector strValues(std::tuple_size>::value); + + SetTupleVisitor visitor; + visitFields(values, &visitor, strValues.begin()); + setInternal(key, strValues); } }; diff --git a/tests/unit_tests/config/testconfig-example.hpp b/tests/unit_tests/config/testconfig-example.hpp index 0ddd975..42620db 100644 --- a/tests/unit_tests/config/testconfig-example.hpp +++ b/tests/unit_tests/config/testconfig-example.hpp @@ -93,7 +93,6 @@ struct TestConfig { std::uint32_t uint32Val; std::uint64_t uint64Val; std::string stringVal; - char* cstringVal; double doubleVal; bool boolVal; TestEnum enumVal; @@ -121,7 +120,6 @@ struct TestConfig { uint32Val, uint64Val, stringVal, - cstringVal, doubleVal, boolVal, enumVal, @@ -167,7 +165,6 @@ const std::string jsonTestString = "\"uint32Val\": 123456, " "\"uint64Val\": 1234567890123456789, " "\"stringVal\": \"blah\", " - "\"cstringVal\": \"blah\", " "\"doubleVal\": -1.234000, " "\"boolVal\": true, " "\"enumVal\": 12, " @@ -194,15 +191,15 @@ const std::string jsonEmptyTestString = "\"uint32Val\": 0, " "\"uint64Val\": 0, " "\"stringVal\": \"\", " - "\"cstringVal\": \"\", " + "\"doubleVal\": 0.0, " "\"boolVal\": false, " "\"enumVal\": 0, " "\"emptyIntVector\": [ ], " "\"intVector\": [ ], " "\"stringVector\": [ ], " "\"doubleVector\": [ ], " - "\"intArray\": [ ], " - "\"intIntPair\": [ ], " + "\"intArray\": [ 0, 0 ], " + "\"intIntPair\": [ 0, 0 ], " "\"subObj\": { \"intVal\": 0, \"intVector\": [ ], \"subSubObj\": { \"intVal\": 0 } }, " "\"subVector\": [ ], " "\"union1\": { \"type\": \"int\", \"value\": 0 }, " diff --git a/tests/unit_tests/config/ut-configuration.cpp b/tests/unit_tests/config/ut-configuration.cpp index ab1a200..1546abc 100644 --- a/tests/unit_tests/config/ut-configuration.cpp +++ b/tests/unit_tests/config/ut-configuration.cpp @@ -64,7 +64,6 @@ BOOST_AUTO_TEST_CASE(FromJsonString) BOOST_CHECK_EQUAL(123456, testConfig.uint32Val); BOOST_CHECK_EQUAL(1234567890123456789ll, testConfig.uint64Val); BOOST_CHECK_EQUAL("blah", testConfig.stringVal); - BOOST_CHECK_EQUAL("blah", testConfig.cstringVal); BOOST_CHECK_CLOSE(-1.234, testConfig.doubleVal, TOLERANCE); BOOST_CHECK_EQUAL(true, testConfig.boolVal); BOOST_CHECK(TestEnum::SECOND == testConfig.enumVal); diff --git a/tests/unit_tests/config/ut-dynvisit.cpp b/tests/unit_tests/config/ut-dynvisit.cpp index b2a0372..d059b01 100644 --- a/tests/unit_tests/config/ut-dynvisit.cpp +++ b/tests/unit_tests/config/ut-dynvisit.cpp @@ -29,8 +29,6 @@ #include "utils/scoped-dir.hpp" #include "config/manager.hpp" -#include "config/from-kvjson-visitor.hpp" - namespace { using namespace utils; @@ -70,12 +68,12 @@ void checkJsonConfig(const TestConfig& cfg, const std::string& json) void checkKVConfig(const TestConfig& cfg, const std::string& db) { KVStore store(db); - BOOST_CHECK_EQUAL(store.get("conf.intVal"), cfg.intVal); - BOOST_CHECK_EQUAL(store.get("conf.int64Val"), cfg.int64Val); - BOOST_CHECK_EQUAL(store.get("conf.boolVal"), cfg.boolVal); - BOOST_CHECK_EQUAL(store.get("conf.stringVal"), cfg.stringVal); - BOOST_CHECK_EQUAL(store.get("conf.intVector"), cfg.intVector.size()); - BOOST_CHECK_EQUAL(store.get("conf.subObj.intVal"), cfg.subObj.intVal); + BOOST_CHECK_EQUAL(store.get("conf.intVal"), std::to_string(cfg.intVal)); + BOOST_CHECK_EQUAL(store.get("conf.int64Val"), std::to_string(cfg.int64Val)); + BOOST_CHECK_EQUAL(store.get("conf.boolVal"), std::to_string(cfg.boolVal)); + BOOST_CHECK_EQUAL(store.get("conf.stringVal"), cfg.stringVal); + BOOST_CHECK_EQUAL(store.get("conf.intVector"), std::to_string(cfg.intVector.size())); + BOOST_CHECK_EQUAL(store.get("conf.subObj.intVal"), std::to_string(cfg.subObj.intVal)); } BOOST_AUTO_TEST_CASE(ReadConfigDefaults) diff --git a/tests/unit_tests/config/ut-kvstore.cpp b/tests/unit_tests/config/ut-kvstore.cpp index 13691c7..06fc701 100644 --- a/tests/unit_tests/config/ut-kvstore.cpp +++ b/tests/unit_tests/config/ut-kvstore.cpp @@ -57,41 +57,6 @@ struct Fixture { } }; -class TestClass { -public: - TestClass(int v): value(v) {} - TestClass(): value(0) {} - friend std::ostream& operator<< (std::ostream& out, const TestClass& cPoint); - friend std::istream& operator>> (std::istream& in, TestClass& cPoint); - friend bool operator== (const TestClass& lhs, const TestClass& rhs); - friend bool operator!= (const TestClass& lhs, const TestClass& rhs); - -private: - int value ; -}; - -bool operator==(const TestClass& lhs, const TestClass& rhs) -{ - return lhs.value == rhs.value; -} - -bool operator!=(const TestClass& lhs, const TestClass& rhs) -{ - return lhs.value != rhs.value; -} - -std::ostream& operator<< (std::ostream& out, const TestClass& tc) -{ - out << tc.value;; - return out; -} - -std::istream& operator>> (std::istream& in, TestClass& tc) -{ - in >> tc.value; - return in; -} - } // namespace BOOST_FIXTURE_TEST_SUITE(KVStoreSuite, Fixture) @@ -113,114 +78,77 @@ BOOST_AUTO_TEST_CASE(EscapedCharacters) { // '*' ?' '[' ']' are escaped // They shouldn't influence the internal implementation - std::string HARD_KEY = "[" + KEY; - BOOST_CHECK_NO_THROW(c.set(HARD_KEY, "A")); - BOOST_CHECK_NO_THROW(c.set(KEY, "B")); - BOOST_CHECK(c.exists(HARD_KEY)); - BOOST_CHECK(c.exists(KEY)); - BOOST_CHECK_NO_THROW(c.clear()); + for (char sc: {'[', ']', '?', '*'}) { + std::string HARD_KEY = sc + KEY; - HARD_KEY = "]" + KEY; - BOOST_CHECK_NO_THROW(c.set(HARD_KEY, "A")); - BOOST_CHECK_NO_THROW(c.set(KEY, "B")); - BOOST_CHECK(c.exists(HARD_KEY)); - BOOST_CHECK(c.exists(KEY)); - BOOST_CHECK_NO_THROW(c.clear()); + BOOST_CHECK_NO_THROW(c.set(HARD_KEY, "A")); + BOOST_CHECK_NO_THROW(c.set(KEY, "B")); - HARD_KEY = "?" + KEY; - BOOST_CHECK_NO_THROW(c.set(HARD_KEY, "A")); - BOOST_CHECK_NO_THROW(c.set(KEY, "B")); - BOOST_CHECK(c.exists(HARD_KEY)); - BOOST_CHECK(c.exists(KEY)); - BOOST_CHECK_NO_THROW(c.clear()); + BOOST_CHECK(c.exists(HARD_KEY)); + BOOST_CHECK(c.exists(KEY)); - HARD_KEY = "*" + KEY; - BOOST_CHECK_NO_THROW(c.set(HARD_KEY, "A")); - BOOST_CHECK_NO_THROW(c.set(KEY, "B")); - BOOST_CHECK(c.exists(HARD_KEY)); - BOOST_CHECK(c.exists(KEY)); + BOOST_CHECK_NO_THROW(c.clear()); + } } -namespace { -template -void testSingleValue(Fixture& f, const A& a, const B& b) +BOOST_AUTO_TEST_CASE(PrefixExists) { - // Set - BOOST_CHECK_NO_THROW(f.c.set(KEY, a)); - BOOST_CHECK_EQUAL(f.c.get(KEY), a); + // '*' ?' '[' ']' are escaped + // They shouldn't influence the internal implementation + for (char sc: {'[', ']', '?', '*'}) { + std::string HARD_KEY = sc + KEY; + std::string FIELD_HARD_KEY = HARD_KEY + ".field"; - // Update - BOOST_CHECK_NO_THROW(f.c.set(KEY, b)); - BOOST_CHECK_EQUAL(f.c.get(KEY), b); - BOOST_CHECK(f.c.exists(KEY)); + BOOST_CHECK_NO_THROW(c.set(FIELD_HARD_KEY, "C")); - // Remove - BOOST_CHECK_NO_THROW(f.c.remove(KEY)); - BOOST_CHECK(!f.c.exists(KEY)); - BOOST_CHECK_THROW(f.c.get(KEY), ConfigException); -} -} // namespace + BOOST_CHECK(!c.exists(KEY)); + BOOST_CHECK(!c.exists(HARD_KEY)); + BOOST_CHECK(c.exists(FIELD_HARD_KEY)); + BOOST_CHECK(!c.prefixExists(KEY)); + BOOST_CHECK(c.prefixExists(HARD_KEY)); + BOOST_CHECK(c.prefixExists(FIELD_HARD_KEY)); -BOOST_AUTO_TEST_CASE(SingleValue) -{ - testSingleValue(*this, "A", "B"); - testSingleValue(*this, 1, 2); - testSingleValue(*this, 1.1, 2.2); - testSingleValue(*this, 2, "A"); - testSingleValue(*this, INT64_MAX, INT64_MAX - 2); - testSingleValue(*this, 11, 22); + BOOST_CHECK_NO_THROW(c.clear()); + } } namespace { -template -void setVector(Fixture& f, const std::vector& vec) -{ - std::vector storedVec; - BOOST_CHECK_NO_THROW(f.c.set(KEY, vec)); - BOOST_CHECK_NO_THROW(storedVec = f.c.get>(KEY)) - BOOST_CHECK_EQUAL_COLLECTIONS(storedVec.begin(), storedVec.end(), vec.begin(), vec.end()); -} - -template -void testVectorOfValues(Fixture& f, - const std::vector& a, - const std::vector& b, - const std::vector& c) +void testSingleValue(Fixture& f, const std::string& a, const std::string& b) { // Set - setVector(f, a); - setVector(f, b); - setVector(f, c); + BOOST_CHECK_NO_THROW(f.c.set(KEY, a)); + BOOST_CHECK_EQUAL(f.c.get(KEY), a); + + // Update + BOOST_CHECK_NO_THROW(f.c.set(KEY, b)); + BOOST_CHECK_EQUAL(f.c.get(KEY), b); + BOOST_CHECK(f.c.exists(KEY)); // Remove BOOST_CHECK_NO_THROW(f.c.remove(KEY)); BOOST_CHECK(!f.c.exists(KEY)); - BOOST_CHECK(f.c.isEmpty()); - BOOST_CHECK_THROW(f.c.get >(KEY), ConfigException); BOOST_CHECK_THROW(f.c.get(KEY), ConfigException); } } // namespace -BOOST_AUTO_TEST_CASE(VectorOfValues) + +BOOST_AUTO_TEST_CASE(SingleValue) { - testVectorOfValues(*this, {"A", "B"}, {"A", "C"}, {"A", "B", "C"}); - testVectorOfValues(*this, {1, 2}, {1, 3}, {1, 2, 3}); - testVectorOfValues(*this, {INT64_MAX, 2}, {1, 3}, {INT64_MAX, 2, INT64_MAX}); - testVectorOfValues(*this, {1.1, 2.2}, {1.1, 3.3}, {1.1, 2.2, 3.3}); - testVectorOfValues(*this, {1, 2}, {1, 3}, {1, 2, 3}); + testSingleValue(*this, "A", "B"); } BOOST_AUTO_TEST_CASE(Clear) { BOOST_CHECK_NO_THROW(c.clear()); - std::vector vec = {"A", "B"}; - BOOST_CHECK_NO_THROW(c.set(KEY, vec)); + BOOST_CHECK_NO_THROW(c.set(KEY, "2")); + BOOST_CHECK_NO_THROW(c.set(KEY + ".0", "A")); + BOOST_CHECK_NO_THROW(c.set(KEY + ".1", "B")); BOOST_CHECK_NO_THROW(c.clear()); BOOST_CHECK(c.isEmpty()); BOOST_CHECK_NO_THROW(c.remove(KEY)); - BOOST_CHECK_THROW(c.get>(KEY), ConfigException); + BOOST_CHECK_THROW(c.get(KEY), ConfigException); BOOST_CHECK_THROW(c.get(KEY), ConfigException); } @@ -228,17 +156,17 @@ BOOST_AUTO_TEST_CASE(Transaction) { { KVStore::Transaction trans(c); - c.set(KEY, 1); + c.set(KEY, "a"); trans.commit(); } - BOOST_CHECK_EQUAL(c.get(KEY), 1); + BOOST_CHECK_EQUAL(c.get(KEY), "a"); { KVStore::Transaction trans(c); - c.set(KEY, 2); + c.set(KEY, "b"); // no commit } - BOOST_CHECK_EQUAL(c.get(KEY), 1); + BOOST_CHECK_EQUAL(c.get(KEY), "a"); { KVStore::Transaction trans(c); @@ -259,23 +187,23 @@ BOOST_AUTO_TEST_CASE(TransactionStacked) KVStore::Transaction transOuter(c); { KVStore::Transaction transInner(c); - c.set(KEY, 1); + c.set(KEY, "a"); // no inner commit } transOuter.commit(); } - BOOST_CHECK_EQUAL(c.get(KEY), 1); + BOOST_CHECK_EQUAL(c.get(KEY), "a"); { KVStore::Transaction transOuter(c); { KVStore::Transaction transInner(c); - c.set(KEY, 2); + c.set(KEY, "b"); transInner.commit(); } // no outer commit } - BOOST_CHECK_EQUAL(c.get(KEY), 1); + BOOST_CHECK_EQUAL(c.get(KEY), "a"); { KVStore::Transaction transOuter(c); @@ -306,17 +234,4 @@ BOOST_AUTO_TEST_CASE(TransactionThreads) thread2.join(); } -BOOST_AUTO_TEST_CASE(Key) -{ - BOOST_CHECK_EQUAL(key(), ""); - BOOST_CHECK_EQUAL(key<>(), ""); - BOOST_CHECK_EQUAL(key(""), ""); - BOOST_CHECK_EQUAL(key("KEY"), "KEY"); - BOOST_CHECK_EQUAL(key<>("KEY"), "KEY"); - BOOST_CHECK_EQUAL(key("KEY", "A"), "KEY.A"); - BOOST_CHECK_EQUAL(key("KEY", 1, 2.2), "KEY.1.2.2"); - BOOST_CHECK_EQUAL(key("KEY", 1, "B"), "KEY.1.B"); - BOOST_CHECK_EQUAL(key<'_'>("KEY", 1, 2.2), "KEY_1_2.2"); -} - BOOST_AUTO_TEST_SUITE_END() -- 2.7.4