diff --git a/.clang-format b/.clang-format index e64581062d0d3235e61c69eff6c5a0bc9e87d3e1..a8b3f38e016941b5f2460afd73835f21c87050f6 100755 --- a/.clang-format +++ b/.clang-format @@ -49,7 +49,7 @@ BreakConstructorInitializers: AfterColon BreakInheritanceList: AfterColon BreakStringLiterals: true -ColumnLimit: 140 # Set to 0 to turn off +ColumnLimit: 120 # Set to 0 to turn off # Need? CommentPragmas: '^ IWYU pragma:' diff --git a/src/AttributeName.cpp b/src/AttributeName.cpp index a07b1b8d7b7a9898172d5e399042828ee01b7790..fad6f8ef95ddd4169f7e2dfc2a6acd6cc1f111ac 100644 --- a/src/AttributeName.cpp +++ b/src/AttributeName.cpp @@ -99,8 +99,10 @@ const string &AttributeName::tangoHostWithDomain() if (status != 0) { - spdlog::error("Error: Unable to add domain to tango host: getaddrinfo failed with error: {}", gai_strerror(status)); - return tango_host; + spdlog::error("Error: Unable to add domain to tango host: getaddrinfo failed with error: {}", + gai_strerror(status)); + + return tangoHost(); } for (rp = result; rp != nullptr; rp = rp->ai_next) diff --git a/src/AttributeTraits.cpp b/src/AttributeTraits.cpp index 7fa4bfb921dcf4af9b6c910182b3842663d8b36a..5a68d7456a1347840eb4b758ba56cd963e5cb7d5 100644 --- a/src/AttributeTraits.cpp +++ b/src/AttributeTraits.cpp @@ -28,9 +28,9 @@ namespace hdbpp void AttributeTraits::print(ostream &os) const { os << "AttributeTraits(" - << "write_type: " << _attr_write_type << ", " - << "format_type: " << _attr_format << ", " - << "type: " << _attr_type << ")"; + << "write_type: " << _attr_write_type << "(" << static_cast<unsigned int>(_attr_write_type) << "), " + << "format_type: " << _attr_format << "(" << static_cast<unsigned int>(_attr_format) << "), " + << "type: " << _attr_type << "(" << static_cast<unsigned int>(_attr_type) << ")"; } } // namespace hdbpp diff --git a/src/AttributeTraits.hpp b/src/AttributeTraits.hpp index f7621e20a6d5ab6de565e37b7f330a572a8a9728..630d9da3004784e085b8e413e25732c4a988bb12 100644 --- a/src/AttributeTraits.hpp +++ b/src/AttributeTraits.hpp @@ -75,7 +75,8 @@ public: bool operator==(const AttributeTraits &other) const { - return _attr_write_type == other.writeType() && _attr_format == other.formatType() && _attr_type == other.type(); + return _attr_write_type == other.writeType() && _attr_format == other.formatType() && + _attr_type == other.type(); } /// @brief Print the AttributeTraits object to the stream diff --git a/src/ColumnCache.hpp b/src/ColumnCache.hpp index f7350a3dd06436682d6ed371bdbd0e16c53f16d7..5fb83e7d08ff78532f5d3bc708f12ccefdc7f6db 100644 --- a/src/ColumnCache.hpp +++ b/src/ColumnCache.hpp @@ -127,7 +127,8 @@ namespace pqxx_conn if (!tx.prepared(_fetch_all_query_name).exists()) { - tx.conn().prepare(_fetch_all_query_name, QueryBuilder::fetchAllValuesQuery(_column_name, _table_name, _reference)); + tx.conn().prepare(_fetch_all_query_name, + QueryBuilder::fetchAllValuesQuery(_column_name, _table_name, _reference)); _logger->trace("Created prepared statement for: {}", _fetch_all_query_name); } @@ -144,8 +145,8 @@ namespace pqxx_conn } catch (const pqxx::pqxx_exception &ex) { - string msg {"The database transaction failed. Unable to fetchAll for column: " + _column_name + " in table: " + _table_name + - ". Error: " + ex.base().what()}; + string msg {"The database transaction failed. Unable to fetchAll for column: " + _column_name + + " in table: " + _table_name + ". Error: " + ex.base().what()}; _logger->error("Error: An unexpected error occurred when trying to run the database query"); _logger->error("Caught error: \"{}\"", ex.base().what()); @@ -182,7 +183,8 @@ namespace pqxx_conn if (!tx.prepared(_fetch_id_query_name).exists()) { - tx.conn().prepare(_fetch_id_query_name, QueryBuilder::fetchValueQuery(_column_name, _table_name, _reference)); + tx.conn().prepare( + _fetch_id_query_name, QueryBuilder::fetchValueQuery(_column_name, _table_name, _reference)); _logger->trace("Created prepared statement for: {}", _fetch_id_query_name); } @@ -206,7 +208,8 @@ namespace pqxx_conn } else { - throw pqxx::unexpected_rows("More than one row returned for value lookup. Expected just one."); + throw pqxx::unexpected_rows( + "More than one row returned for value lookup. Expected just one."); } } @@ -215,8 +218,8 @@ namespace pqxx_conn } catch (const pqxx::pqxx_exception &ex) { - string msg {"The database transaction failed. Unable to query column: " + _column_name + " in table: " + _table_name + - ". Error: " + ex.base().what()}; + string msg {"The database transaction failed. Unable to query column: " + _column_name + + " in table: " + _table_name + ". Error: " + ex.base().what()}; _logger->error("Error: An unexpected error occurred when trying to run the database query"); _logger->error("Caught error: \"{}\"", ex.base().what()); diff --git a/src/DbConnection.cpp b/src/DbConnection.cpp old mode 100755 new mode 100644 index e70eca0a8ad08f47080cb375c638b96fd32fa04f..35dc2eb6e935f97739c567cdcdb8cfb83ed51819 --- a/src/DbConnection.cpp +++ b/src/DbConnection.cpp @@ -70,7 +70,8 @@ namespace pqxx_conn // will destroy any existing cache objects managed by the unique pointers _conf_id_cache = make_unique<ColumnCache<int, std::string>>(_conn, CONF_TABLE_NAME, CONF_COL_ID, CONF_COL_NAME); - _error_desc_id_cache = make_unique<ColumnCache<int, std::string>>(_conn, ERR_TABLE_NAME, ERR_COL_ID, ERR_COL_ERROR_DESC); + _error_desc_id_cache = make_unique<ColumnCache<int, std::string>>( + _conn, ERR_TABLE_NAME, ERR_COL_ID, ERR_COL_ERROR_DESC); _event_id_cache = make_unique<ColumnCache<int, std::string>>( _conn, HISTORY_EVENT_TABLE_NAME, HISTORY_EVENT_COL_EVENT_ID, HISTORY_EVENT_COL_EVENT); @@ -122,7 +123,8 @@ namespace pqxx_conn // this is an error case if (_conf_id_cache->valueExists(full_attr_name)) { - string msg {"This attribute [" + full_attr_name + "] already exists in the database. Unable to add it again."}; + string msg { + "This attribute [" + full_attr_name + "] already exists in the database. Unable to add it again."}; _logger->error("Error: The attribute already exists in the database and can not be added again"); _logger->error("Attribute details. Name: {} traits: {}", full_attr_name, traits); _logger->error("Throwing consistency error with message: \"{}\"", msg); @@ -143,12 +145,15 @@ namespace pqxx_conn auto row = tx.exec_prepared1(StoreAttribute, full_attr_name, + _query_builder.tableName(traits), control_system, att_domain, att_family, att_member, att_name, - _query_builder.tableName(traits)); + static_cast<unsigned int>(traits.type()), + static_cast<unsigned int>(traits.formatType()), + static_cast<unsigned int>(traits.writeType())); tx.commit(); @@ -193,8 +198,10 @@ namespace pqxx_conn if (!_event_id_cache->valueExists(event)) { - string msg {"The event [" + event + "] is missing in both the cache and database, this is an unrecoverable error."}; - _logger->error("Event found missing, this occurred when storing event: {} for attribute: {}", event, full_attr_name); + string msg { + "The event [" + event + "] is missing in both the cache and database, this is an unrecoverable error."}; + _logger->error( + "Event found missing, this occurred when storing event: {} for attribute: {}", event, full_attr_name); _logger->error("Throwing consistency error with message: \"{}\"", msg); Tango::Except::throw_exception("Consistency Error", msg, LOCATION_INFO); } @@ -281,15 +288,15 @@ namespace pqxx_conn tx.exec_prepared0(StoreParameterEvent, _conf_id_cache->value(full_attr_name), event_time, - tx.quote(label), - tx.quote(unit), - tx.quote(standard_unit), - tx.quote(display_unit), - tx.quote(format), - tx.quote(archive_rel_change), - tx.quote(archive_abs_change), - tx.quote(archive_period), - tx.quote(description)); + label, + unit, + standard_unit, + display_unit, + format, + archive_rel_change, + archive_abs_change, + archive_period, + description); tx.commit(); }); @@ -307,8 +314,11 @@ namespace pqxx_conn //============================================================================= //============================================================================= - void DbConnection::storeDataEventError( - const std::string &full_attr_name, double event_time, int quality, const std::string &error_msg, const AttributeTraits &traits) + void DbConnection::storeDataEventError(const std::string &full_attr_name, + double event_time, + int quality, + const std::string &error_msg, + const AttributeTraits &traits) { assert(!full_attr_name.empty()); assert(!error_msg.empty()); @@ -317,7 +327,8 @@ namespace pqxx_conn assert(_error_desc_id_cache != nullptr); assert(_event_id_cache != nullptr); - _logger->trace("Storing error message event for attribute {}. Error message: \"{}\"", full_attr_name, error_msg); + _logger->trace( + "Storing error message event for attribute {}. Error message: \"{}\"", full_attr_name, error_msg); checkConnection(LOCATION_INFO); checkAttributeExists(full_attr_name, LOCATION_INFO); @@ -330,10 +341,13 @@ namespace pqxx_conn // double check it really exists.... if (!_error_desc_id_cache->valueExists(error_msg)) { - string msg {"The error message [" + error_msg + "] is missing in both the cache and database, this is an unrecoverable error."}; + string msg {"The error message [" + error_msg + + "] is missing in both the cache and database, this is an unrecoverable error."}; - _logger->error( - "Error message found missing, this occurred when storing msg: \"{}\" for attribute: {}", error_msg, full_attr_name); + _logger->error("Error message found missing, this occurred when storing msg: \"{}\" for attribute: {}", + error_msg, + full_attr_name); + _logger->error("Throwing consistency error with message: \"{}\"", msg); Tango::Except::throw_exception("Consistency Error", msg, LOCATION_INFO); } @@ -346,8 +360,10 @@ namespace pqxx_conn if (!tx.prepared(_query_builder.storeDataEventErrorName(traits)).exists()) { - tx.conn().prepare(_query_builder.storeDataEventErrorName(traits), _query_builder.storeDataEventErrorQuery(traits)); - _logger->trace("Created prepared statement for: {}", _query_builder.storeDataEventErrorName(traits)); + tx.conn().prepare(_query_builder.storeDataEventErrorName(traits), + _query_builder.storeDataEventErrorQuery(traits)); + _logger->trace( + "Created prepared statement for: {}", _query_builder.storeDataEventErrorName(traits)); } _logger->warn("{}", _error_desc_id_cache->value(error_msg)); @@ -446,7 +462,8 @@ namespace pqxx_conn return row.at(0).as<int>(); }); - _logger->debug("Stored event {} for attribute {} and got database id for it: {}", event, full_attr_name, event_id); + _logger->debug( + "Stored event {} for attribute {} and got database id for it: {}", event, full_attr_name, event_id); // cache the new event id for future use _event_id_cache->cacheValue(event_id, event); @@ -464,7 +481,8 @@ namespace pqxx_conn //============================================================================= void DbConnection::storeErrorMsg(const std::string &full_attr_name, const std::string &error_msg) { - _logger->debug("Error message \"{}\" needs adding to the database, by request of attribute {}", error_msg, full_attr_name); + _logger->debug( + "Error message \"{}\" needs adding to the database, by request of attribute {}", error_msg, full_attr_name); try { @@ -485,8 +503,10 @@ namespace pqxx_conn return row.at(0).as<int>(); }); - _logger->debug( - "Stored error message \"{}\" for attribute {} and got database id for it: {}", error_msg, full_attr_name, error_id); + _logger->debug("Stored error message \"{}\" for attribute {} and got database id for it: {}", + error_msg, + full_attr_name, + error_id); // cache the new error id for future use _error_desc_id_cache->cacheValue(error_id, error_msg); @@ -524,8 +544,10 @@ namespace pqxx_conn { if (isClosed()) { - string msg {"Connection to database is closed. Ensure it has been opened before trying to use the connection."}; - _logger->error("Error: The DbConnection is showing a closed connection status, open it before using store functions"); + string msg { + "Connection to database is closed. Ensure it has been opened before trying to use the connection."}; + _logger->error( + "Error: The DbConnection is showing a closed connection status, open it before using store functions"); _logger->error("Throwing connection error with message: \"{}\"", msg); Tango::Except::throw_exception("Connecion Error", msg, location); } @@ -533,7 +555,8 @@ namespace pqxx_conn //============================================================================= //============================================================================= - void DbConnection::handlePqxxError(const string &msg, const string &what, const string &query, const std::string &location) + void DbConnection::handlePqxxError( + const string &msg, const string &what, const string &query, const std::string &location) { string full_msg {"The database transaction failed. " + msg}; _logger->error("Error: An unexpected error occurred when trying to run the database query"); diff --git a/src/DbConnection.hpp b/src/DbConnection.hpp index 804af4b3c417866d995aeb9fb47facb75304eaf4..c3597ad7ee54730386df5d6a46842b3a030d5af4 100644 --- a/src/DbConnection.hpp +++ b/src/DbConnection.hpp @@ -84,8 +84,11 @@ namespace pqxx_conn std::unique_ptr<vector<T>> value_w, const AttributeTraits &traits); - void storeDataEventError( - const std::string &full_attr_name, double event_time, int quality, const std::string &error_msg, const AttributeTraits &traits); + void storeDataEventError(const std::string &full_attr_name, + double event_time, + int quality, + const std::string &error_msg, + const AttributeTraits &traits); // fetch API std::string fetchLastHistoryEvent(const std::string &full_attr_name); @@ -97,7 +100,8 @@ namespace pqxx_conn void checkAttributeExists(const std::string &full_attr_name, const std::string &location); void checkConnection(const std::string &location); - void handlePqxxError(const std::string &msg, const std::string &what, const std::string &query, const std::string &location); + void handlePqxxError( + const std::string &msg, const std::string &what, const std::string &query, const std::string &location); // this object builds and caches queries for the database QueryBuilder _query_builder; diff --git a/src/DbConnection.tpp b/src/DbConnection.tpp old mode 100755 new mode 100644 index 1ad978ceb684557e442ba0d3e8cb9c6727cf5b54..068589343b8028890bfd60a750feda8b53ef2955 --- a/src/DbConnection.tpp +++ b/src/DbConnection.tpp @@ -26,6 +26,27 @@ namespace hdbpp { namespace pqxx_conn { + namespace store_data_utils + { + template<typename T> + struct Preprocess + { + static void run(std::unique_ptr<std::vector<T>> &, pqxx::work &) {} + }; + + //============================================================================= + //============================================================================= + template<> + struct Preprocess<std::string> + { + static void run(std::unique_ptr<std::vector<std::string>> &value, pqxx::work &tx) + { + for (auto &str : *value) + str = tx.quote(str); + } + }; + } // namespace store_data_utils + //============================================================================= //============================================================================= template<typename T> @@ -38,8 +59,11 @@ namespace pqxx_conn { assert(!full_attr_name.empty()); - _logger->trace("Storing data event for attribute {} with traits {}, value_r valid: {}, value_w valid: {}", - full_attr_name, traits, value_r->size() > 0, value_w->size() > 0); + _logger->trace("Storing data event for attribute {} with traits {}, value_r valid: {}, value_w valid: {}", + full_attr_name, + traits, + value_r->size() > 0, + value_w->size() > 0); checkConnection(LOCATION_INFO); checkAttributeExists(full_attr_name, LOCATION_INFO); @@ -53,13 +77,14 @@ namespace pqxx_conn // queries often if (!tx.prepared(_query_builder.storeDataEventName(traits)).exists()) { - tx.conn().prepare(_query_builder.storeDataEventName(traits), _query_builder.storeDataEventQuery<T>(traits)); + tx.conn().prepare( + _query_builder.storeDataEventName(traits), _query_builder.storeDataEventQuery<T>(traits)); } // get the pqxx prepared statement invocation object to allow us to // bind each parameter in turn, this gives us the flexibility to bind // conditional parameters (as long as the query string matches) - pqxx::prepare::invocation inv = tx.prepared(_query_builder.storeDataEventName(traits)); + auto inv = tx.prepared(_query_builder.storeDataEventName(traits)); // this lambda stores the data value correctly into the invocation, // we must treat scalar/spectrum in different ways, one is a single @@ -68,6 +93,9 @@ namespace pqxx_conn auto store_value = [&tx, &inv, &traits](auto &value) { if (value && value->size() > 0) { + // this ensures strings are quoted and escaped, other types are ignored + store_data_utils::Preprocess<T>::run(value, tx); + // for a scalar, store the first element of the vector, // we do not expect more than 1 element, for an array, store // the entire vector diff --git a/src/HdbppTimescaleDb.cpp b/src/HdbppTimescaleDb.cpp old mode 100755 new mode 100644 index b0b7ab45af00405da60362d340a3cae463882007..4d8c2d7bb427b2e12559d87ad53ef72f933a7fd4 --- a/src/HdbppTimescaleDb.cpp +++ b/src/HdbppTimescaleDb.cpp @@ -21,6 +21,7 @@ #include "DbConnection.hpp" #include "HdbppTxDataEvent.hpp" +#include "HdbppTxDataEventError.hpp" #include "HdbppTxHistoryEvent.hpp" #include "HdbppTxNewAttribute.hpp" #include "HdbppTxParameterEvent.hpp" @@ -36,14 +37,14 @@ namespace hdbpp // declaring this variable here removes it from the header, and keeps the header clean. // It is allocated in the constructor. It can be abstracted further to allow easy plug // in of different backends at a later point -unique_ptr<pqxx_conn::DbConnection> conn; +unique_ptr<pqxx_conn::DbConnection> Conn; // simple class to gather utility functions that were previously part of HdbppTimescaleDb, // removes them from the header and keeps it clean for includes struct HdbppTimescaleDbUtils { static string getConfigParam(const map<string, string> &conf, const string ¶m, bool mandatory); - static map<string, string> extractConfig(vector<string> str, const string &separator); + static map<string, string> extractConfig(vector<string> config, const string &separator); }; //============================================================================= @@ -119,10 +120,10 @@ HdbppTimescaleDb::HdbppTimescaleDb(const vector<string> &configuration) spdlog::info("Manatory config parameter connect_string: {}", connection_string); // allocate a connection to store data with - conn = make_unique<pqxx_conn::DbConnection>(); + Conn = make_unique<pqxx_conn::DbConnection>(); // now bring up the connection - conn->connect(connection_string); + Conn->connect(connection_string); spdlog::info("Started libhdbpp-timescale shared library successfully"); } @@ -130,8 +131,8 @@ HdbppTimescaleDb::HdbppTimescaleDb(const vector<string> &configuration) //============================================================================= HdbppTimescaleDb::~HdbppTimescaleDb() { - if (conn->isOpen()) - conn->disconnect(); + if (Conn->isOpen()) + Conn->disconnect(); } //============================================================================= @@ -147,28 +148,30 @@ void HdbppTimescaleDb::insert_Attr(Tango::EventData *event_data, HdbEventDataTyp { spdlog::trace("Event type is error for attribute: {}", event_data->attr_name); - conn->createTx<HdbppTxDataEvent>() + Conn->createTx<HdbppTxDataEventError>() .withName(event_data->attr_name) .withTraits(static_cast<Tango::AttrWriteType>(event_data_type.write_type), static_cast<Tango::AttrDataFormat>(event_data_type.data_format), event_data_type.data_type) .withError(string(event_data->errors[0].desc)) .withEventTime(event_data->attr_value->get_date()) - .withQuality(static_cast<int>(event_data->attr_value->get_quality())) + .withQuality(event_data->attr_value->get_quality()) .store(); } else { + spdlog::trace("Event type is data for attribute: {}", event_data->attr_name); + // build a data event request, this will store 0 or more data elements, // pending on type, format and quality - conn->createTx<HdbppTxDataEvent>() + Conn->createTx<HdbppTxDataEvent>() .withName(event_data->attr_name) .withTraits(static_cast<Tango::AttrWriteType>(event_data_type.write_type), static_cast<Tango::AttrDataFormat>(event_data_type.data_format), event_data_type.data_type) .withAttribute(event_data->attr_value) .withEventTime(event_data->attr_value->get_date()) - .withQuality(static_cast<int>(event_data->attr_value->get_quality())) + .withQuality(event_data->attr_value->get_quality()) .store(); } } @@ -180,7 +183,7 @@ void HdbppTimescaleDb::insert_param_Attr(Tango::AttrConfEventData *conf_event_da assert(conf_event_data); spdlog::trace("Insert parameter event request for attribute: {}", conf_event_data->attr_name); - conn->createTx<HdbppTxParameterEvent>() + Conn->createTx<HdbppTxParameterEvent>() .withName(conf_event_data->attr_name) .withEventTime(conf_event_data->get_date()) .withAttrInfo(*(conf_event_data->attr_conf)) @@ -189,7 +192,8 @@ void HdbppTimescaleDb::insert_param_Attr(Tango::AttrConfEventData *conf_event_da //============================================================================= //============================================================================= -void HdbppTimescaleDb::configure_Attr(std::string fqdn_attr_name, int type, int format, int write_type, unsigned int ttl) +void HdbppTimescaleDb::configure_Attr( + std::string fqdn_attr_name, int type, int format, int write_type, unsigned int ttl) { assert(!fqdn_attr_name.empty()); spdlog::trace("Insert new attribute request for attribute: {}", fqdn_attr_name); @@ -199,13 +203,13 @@ void HdbppTimescaleDb::configure_Attr(std::string fqdn_attr_name, int type, int // forgive the ugly casting, but for some reason we receive the enum values // already cast to ints, we cast them back to enums so they function as // enums again - conn->createTx<HdbppTxNewAttribute>() + Conn->createTx<HdbppTxNewAttribute>() .withName(fqdn_attr_name) .withTraits(static_cast<Tango::AttrWriteType>(write_type), static_cast<Tango::AttrDataFormat>(format), type) .store(); // add a start event - conn->createTx<HdbppTxHistoryEvent>().withName(fqdn_attr_name).withEvent(events::StartEvent).store(); + Conn->createTx<HdbppTxHistoryEvent>().withName(fqdn_attr_name).withEvent(events::StartEvent).store(); } //============================================================================= @@ -224,7 +228,7 @@ void HdbppTimescaleDb::event_Attr(std::string fqdn_attr_name, unsigned char even { assert(!fqdn_attr_name.empty()); spdlog::trace("History event request for attribute: {}", fqdn_attr_name); - conn->createTx<HdbppTxHistoryEvent>().withName(fqdn_attr_name).withEvent(event).store(); + Conn->createTx<HdbppTxHistoryEvent>().withName(fqdn_attr_name).withEvent(event).store(); } //============================================================================= @@ -239,6 +243,6 @@ AbstractDB *HdbppTimescaleDbFactory::create_db(vector<string> configuration) //============================================================================= DBFactory *getDBFactory() { - auto *factory = new hdbpp::HdbppTimescaleDbFactory(); - return static_cast<DBFactory*>(factory); + auto *factory = new hdbpp::HdbppTimescaleDbFactory(); + return static_cast<DBFactory *>(factory); } diff --git a/src/HdbppTxDataEvent.hpp b/src/HdbppTxDataEvent.hpp old mode 100755 new mode 100644 index 1caf99f9909d4373ec3dbe042008110ad38394b4..a1f5b23655b20bc1173a26e95758cb8a36aec48b --- a/src/HdbppTxDataEvent.hpp +++ b/src/HdbppTxDataEvent.hpp @@ -20,40 +20,21 @@ #ifndef _HDBPP_TX_DATA_EVENT_HPP #define _HDBPP_TX_DATA_EVENT_HPP -#include "AttributeTraits.hpp" -#include "HdbppTxBase.hpp" -#include "LibUtils.hpp" - -#include <iostream> -#include <string> +#include "HdbppTxDataEventBase.hpp" namespace hdbpp { template<typename Conn> -class HdbppTxDataEvent : public HdbppTxBase<Conn> +class HdbppTxDataEvent : public HdbppTxDataEventBase<Conn, HdbppTxDataEvent> { -public: - // TODO check fully tested - // TODO when storing INVALID events, i.e. empty or bad quality, do we need to generate an event time, or is the given one still valid? - // TODO Quality is an enum? - // TODO auto add new attribute feature +private: + // help clean up the code a little + using Base = HdbppTxDataEventBase<Conn, HdbppTxDataEvent>; - HdbppTxDataEvent(Conn &conn) : HdbppTxBase<Conn>(conn) {} +public: + HdbppTxDataEvent(Conn &conn) : HdbppTxDataEventBase<Conn, HdbppTxDataEvent>(conn) {} virtual ~HdbppTxDataEvent() {} - HdbppTxDataEvent<Conn> &withName(const std::string &fqdn_attr_name) - { - _attr_name = AttributeName {fqdn_attr_name}; - return *this; - } - - HdbppTxDataEvent<Conn> &withTraits(Tango::AttrWriteType write, Tango::AttrDataFormat format, unsigned int type) - { - _traits = AttributeTraits(write, format, type); - _traits_set = true; - return *this; - } - HdbppTxDataEvent<Conn> &withAttribute(Tango::DeviceAttribute *dev_attr) { // just set the pointer here, we will do a full event data extraction at @@ -63,24 +44,6 @@ public: return *this; } - HdbppTxDataEvent<Conn> &withEventTime(Tango::TimeVal tv) - { - _event_time = tv.tv_sec + tv.tv_usec / 1.0e6; - return *this; - } - - HdbppTxDataEvent<Conn> &withQuality(int quality) - { - _quality = quality; - return *this; - } - - HdbppTxDataEvent<Conn> &withError(const string &error_msg) - { - _error_msg = error_msg; - return *this; - } - HdbppTxDataEvent<Conn> &store(); /// @brief Print the HdbppTxDataEvent object to the stream @@ -93,23 +56,8 @@ private: template<typename T> void doStore(); - // store an error to the database - void doStoreError(); - - AttributeName _attr_name; - AttributeTraits _traits; - - std::string _error_msg; - int _quality = Tango::ATTR_INVALID; - // the device attribute to extract the value from Tango::DeviceAttribute *_dev_attr = nullptr; - - // time this parameter change event was generated - double _event_time = 0; - - // force user to set traits - bool _traits_set = false; }; //============================================================================= @@ -117,63 +65,63 @@ private: template<typename Conn> HdbppTxDataEvent<Conn> &HdbppTxDataEvent<Conn>::store() { - if (_attr_name.empty()) + if (Base::attributeName().empty()) { std::string msg {"AttributeName is reporting empty. Unable to complete the transaction."}; spdlog::error("Error: {}", msg); Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); } - else if (!_traits_set) + else if (!Base::traitsSet()) { std::string msg {"AttributeTraits are not set. Unable to complete the transaction."}; - spdlog::error("Error: {}", msg); + spdlog::error("Error: {} For attribute {}", msg, Base::attributeName()); Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); } else if (!_dev_attr) { std::string msg {"Device Attribute is not set. Unable to complete the transaction."}; - spdlog::error("Error: {}", msg); + spdlog::error("Error: {} For attribute {}", msg, Base::attributeName()); Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); } else if (HdbppTxBase<Conn>::connection().isClosed()) { string msg {"The connection is reporting it is closed. Unable to store data event."}; - spdlog::error("Error: {}", msg); + spdlog::error("Error: {} For attribute {}", msg, Base::attributeName()); Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); } // disable is_empty exception _dev_attr->reset_exceptions(Tango::DeviceAttribute::isempty_flag); - // check for error messages, if we have one set, assume this is an error - // event and store it as so - if (!_error_msg.empty()) + // translate the Tango Type into a C++ type via templates, inside + // doStore the data is extracted and then stored + switch (Base::attributeTraits().type()) { - // store the error message - doStoreError(); - } - else - { - // translate the Tango Type into a C++ type via templates, inside - // doStore the data is extracted and then stored - switch (_traits.type()) - { - //case Tango::DEV_BOOLEAN: this->template doStore<bool>(); break; - case Tango::DEV_SHORT: this->template doStore<int16_t>(); break; - case Tango::DEV_LONG: this->template doStore<int32_t>(); break; - case Tango::DEV_LONG64: this->template doStore<int64_t>(); break; - case Tango::DEV_FLOAT: this->template doStore<float>(); break; - case Tango::DEV_DOUBLE: this->template doStore<double>(); break; - case Tango::DEV_UCHAR: this->template doStore<uint8_t>(); break; - case Tango::DEV_USHORT: this->template doStore<uint16_t>(); break; - case Tango::DEV_ULONG: this->template doStore<uint32_t>(); break; - case Tango::DEV_ULONG64: this->template doStore<uint64_t>(); break; - case Tango::DEV_STRING: this->template doStore<std::string>(); break; - case Tango::DEV_STATE: this->template doStore<int32_t>(); break; - // TODO enable these calls. + // TODO enable commented out calls. + //case Tango::DEV_BOOLEAN: this->template doStore<bool>(); break; + case Tango::DEV_SHORT: this->template doStore<int16_t>(); break; + case Tango::DEV_LONG: this->template doStore<int32_t>(); break; + case Tango::DEV_LONG64: this->template doStore<int64_t>(); break; + case Tango::DEV_FLOAT: this->template doStore<float>(); break; + case Tango::DEV_DOUBLE: this->template doStore<double>(); break; + case Tango::DEV_UCHAR: this->template doStore<uint8_t>(); break; + case Tango::DEV_USHORT: this->template doStore<uint16_t>(); break; + case Tango::DEV_ULONG: this->template doStore<uint32_t>(); break; + case Tango::DEV_ULONG64: this->template doStore<uint64_t>(); break; + case Tango::DEV_STRING: this->template doStore<std::string>(); break; + case Tango::DEV_STATE: + this->template doStore<int32_t>(); + break; //case Tango::DEV_ENUM: this->template doStore<?>(); break; //case Tango::DEV_ENCODED: this->template doStore<vector<uint8_t>>(); break; - } + + default: + std::string msg { + "HdbppTxDataEvent built for unsupported type: " + std::to_string(Base::attributeTraits().type()) + + ", for attribute: [" + Base::attributeName().fullAttributeName() + "]"}; + + spdlog::error("Error: {}", msg); + Tango::Except::throw_exception("Runtime Error", msg, LOCATION_INFO); } // success in running the store command, so set the result as true @@ -195,19 +143,31 @@ void HdbppTxDataEvent<Conn>::doStore() // we still store the event, but with no event data, so filter them // here, and if we detect one, do not extract data, instead return // a vector with no elements in - if (has_data && !_dev_attr->is_empty() && _quality != Tango::ATTR_INVALID) + if (has_data && !_dev_attr->is_empty() && Base::quality() != Tango::ATTR_INVALID) { // attempt to extract data, if none is received then clear // the unique_ptr as a signal to following functions there is no data if (!extractor(*value)) { - std::string msg {"Failed to extract the attribute data for attribute: [" + _attr_name.fullAttributeName() + - "] and off type: [" + std::to_string(_traits.type()) + "]"}; + std::string msg {"Failed to extract the attribute data for attribute: [" + + Base::attributeName().fullAttributeName() + "] and off type: [" + + std::to_string(Base::attributeTraits().type()) + "]"}; spdlog::error("Error: {}", msg); Tango::Except::throw_exception("Runtime Error", msg, LOCATION_INFO); } } + // log some more unusual conditions + else if (Base::quality() == Tango::ATTR_INVALID) + { + spdlog::trace("Quality is {} for attribute: [{}], no data extracted", + Base::quality(), + Base::attributeName().fqdnAttributeName()); + } + else if (_dev_attr->is_empty()) + { + spdlog::trace("Attribute [{}] empty, no data extracted", Base::attributeName().fqdnAttributeName()); + } // release ownership of the unique_ptr back to the caller return std::move(value); @@ -215,23 +175,15 @@ void HdbppTxDataEvent<Conn>::doStore() // attempt to store the error in the database, any exceptions are left to // propergate to the caller - HdbppTxBase<Conn>::connection().template storeDataEvent<T>(HdbppTxBase<Conn>::attrNameForStorage(_attr_name), - _event_time, - _quality, - std::move(value([this](std::vector<T> &v) { return _dev_attr->extract_read(v); }, _traits.hasReadData())), - std::move(value([this](std::vector<T> &v) { return _dev_attr->extract_set(v); }, _traits.hasWriteData())), - _traits); -} - -//============================================================================= -//============================================================================= -template<typename Conn> -void HdbppTxDataEvent<Conn>::doStoreError() -{ - // attempt to store the error in the database, any exceptions are left to - // propergate to the caller - HdbppTxBase<Conn>::connection().storeDataEventError( - HdbppTxBase<Conn>::attrNameForStorage(_attr_name), _event_time, _quality, _error_msg, _traits); + HdbppTxBase<Conn>::connection().template storeDataEvent<T>( + HdbppTxBase<Conn>::attrNameForStorage(Base::attributeName()), + Base::eventTime(), + Base::quality(), + std::move(value( + [this](std::vector<T> &v) { return _dev_attr->extract_read(v); }, Base::attributeTraits().hasReadData())), + std::move(value( + [this](std::vector<T> &v) { return _dev_attr->extract_set(v); }, Base::attributeTraits().hasWriteData())), + Base::attributeTraits()); } //============================================================================= @@ -242,16 +194,8 @@ void HdbppTxDataEvent<Conn>::print(std::ostream &os) const noexcept // TODO can not print tango objects, the operator<< are not const correct! os << "HdbppTxDataEvent(base: "; - HdbppTxBase<Conn>::print(os); - - os << ", " - << "_event_time: " << _event_time << ", " - << "_attr_name: " << _attr_name << ", " - << "_traits: " << _traits << ", " - << "_traits_set: " << _traits_set << ", " - << "_error_msg: " << _error_msg << ", " - << "_quality: " << _quality << ", " - << "_event_time: " << _event_time << ")"; + HdbppTxDataEventBase<Conn, HdbppTxDataEvent>::print(os); + os << ")"; } } // namespace hdbpp diff --git a/src/HdbppTxDataEventBase.hpp b/src/HdbppTxDataEventBase.hpp new file mode 100644 index 0000000000000000000000000000000000000000..836474852cd374c5ae791def39f6b263d8774a06 --- /dev/null +++ b/src/HdbppTxDataEventBase.hpp @@ -0,0 +1,118 @@ +/* Copyright (C) : 2014-2019 + European Synchrotron Radiation Facility + BP 220, Grenoble 38043, FRANCE + + This file is part of libhdb++timescale. + + libhdb++timescale is free software: you can redistribute it and/or modify + it under the terms of the Lesser GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libhdb++timescale is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser + GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with libhdb++timescale. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef _HDBPP_TX_DATA_EVENT_BASE_HPP +#define _HDBPP_TX_DATA_EVENT_BASE_HPP + +#include "AttributeName.hpp" +#include "AttributeTraits.hpp" +#include "HdbppTxBase.hpp" +#include "LibUtils.hpp" + +#include <iostream> +#include <string> + +namespace hdbpp +{ +template<typename Conn, template<typename> class Derived> +class HdbppTxDataEventBase : public HdbppTxBase<Conn> +{ +public: + // TODO when storing INVALID events, i.e. empty or bad quality, do we need to generate an event time, or is the given one still valid? + // TODO auto add new attribute feature + + HdbppTxDataEventBase(Conn &conn) : HdbppTxBase<Conn>(conn) {} + virtual ~HdbppTxDataEventBase() {} + + Derived<Conn> &withName(const std::string &fqdn_attr_name) + { + _attr_name = AttributeName {fqdn_attr_name}; + return static_cast<Derived<Conn> &>(*this); + } + + Derived<Conn> &withTraits(Tango::AttrWriteType write, Tango::AttrDataFormat format, unsigned int type) + { + _traits = AttributeTraits(write, format, type); + _traits_set = true; + return static_cast<Derived<Conn> &>(*this); + } + + Derived<Conn> &withTraits(AttributeTraits &traits) + { + _traits = traits; + _traits_set = true; + return static_cast<Derived<Conn> &>(*this); + } + + Derived<Conn> &withEventTime(Tango::TimeVal tv) + { + _event_time = tv.tv_sec + tv.tv_usec / 1.0e6; + return static_cast<Derived<Conn> &>(*this); + } + + Derived<Conn> &withQuality(Tango::AttrQuality quality) + { + _quality = quality; + return static_cast<Derived<Conn> &>(*this); + } + + /// @brief Print the HdbppTxDataEventBase object to the stream + virtual void print(std::ostream &os) const noexcept override; + +protected: + // release the private data safely for the derived classes + AttributeName &attributeName() { return _attr_name; } + const AttributeTraits &attributeTraits() const { return _traits; } + Tango::AttrQuality quality() const { return _quality; } + double eventTime() const { return _event_time; } + bool traitsSet() const { return _traits_set; } + +private: + AttributeName _attr_name; + AttributeTraits _traits; + Tango::AttrQuality _quality = Tango::ATTR_INVALID; + + // time this parameter change event was generated + double _event_time = 0; + + // force user to set traits + bool _traits_set = false; +}; + +//============================================================================= +//============================================================================= +template<typename Conn, template<typename> class Derived> +void HdbppTxDataEventBase<Conn, Derived>::print(std::ostream &os) const noexcept +{ + // TODO can not print tango objects, the operator<< are not const correct! + + os << "HdbppTxDataEventBase(base: "; + HdbppTxBase<Conn>::print(os); + + os << ", " + << "_event_time: " << _event_time << ", " + << "_attr_name: " << _attr_name << ", " + << "_traits: " << _traits << ", " + << "_traits_set: " << _traits_set << ", " + << "_quality: " << _quality << ", " + << "_event_time: " << _event_time << ")"; +} + +} // namespace hdbpp +#endif // _HDBPP_TX_DATA_EVENT_BASE_HPP diff --git a/src/HdbppTxDataEventError.hpp b/src/HdbppTxDataEventError.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b4a00599929e5a1b645440f4316736a3f858e7bd --- /dev/null +++ b/src/HdbppTxDataEventError.hpp @@ -0,0 +1,109 @@ +/* Copyright (C) : 2014-2019 + European Synchrotron Radiation Facility + BP 220, Grenoble 38043, FRANCE + + This file is part of libhdb++timescale. + + libhdb++timescale is free software: you can redistribute it and/or modify + it under the terms of the Lesser GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libhdb++timescale is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser + GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with libhdb++timescale. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef _HDBPP_TX_DATA_EVENT_ERROR_HPP +#define _HDBPP_TX_DATA_EVENT_ERROR_HPP + +#include "HdbppTxDataEventBase.hpp" + +namespace hdbpp +{ +template<typename Conn> +class HdbppTxDataEventError : public HdbppTxDataEventBase<Conn, HdbppTxDataEventError> +{ +private: + // help clean up the code a little + using Base = HdbppTxDataEventBase<Conn, HdbppTxDataEventError>; + +public: + HdbppTxDataEventError(Conn &conn) : HdbppTxDataEventBase<Conn, HdbppTxDataEventError>(conn) {} + virtual ~HdbppTxDataEventError() {} + + HdbppTxDataEventError<Conn> &withError(const string &error_msg) + { + _error_msg = error_msg; + return *this; + } + + HdbppTxDataEventError<Conn> &store(); + + /// @brief Print the HdbppTxDataEventError object to the stream + void print(std::ostream &os) const noexcept override; + +private: + std::string _error_msg; +}; + +//============================================================================= +//============================================================================= +template<typename Conn> +HdbppTxDataEventError<Conn> &HdbppTxDataEventError<Conn>::store() +{ + if (Base::attributeName().empty()) + { + std::string msg {"AttributeName is reporting empty. Unable to complete the transaction."}; + spdlog::error("Error: {}", msg); + Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); + } + else if (!Base::traitsSet()) + { + std::string msg {"AttributeTraits are not set. Unable to complete the transaction."}; + spdlog::error("Error: {} For attribute {}", msg, Base::attributeName()); + Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); + } + else if (_error_msg.empty()) + { + std::string msg {"Error message is not set. Unable to complete the transaction."}; + spdlog::error("Error: {} For attribute {}", msg, Base::attributeName()); + Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); + } + else if (HdbppTxBase<Conn>::connection().isClosed()) + { + string msg {"The connection is reporting it is closed. Unable to store data event."}; + spdlog::error("Error: {} For attribute {}", msg, Base::attributeName()); + Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); + } + + // attempt to store the error in the database, any exceptions are left to + // propergate to the caller + HdbppTxBase<Conn>::connection().storeDataEventError(HdbppTxBase<Conn>::attrNameForStorage(Base::attributeName()), + Base::eventTime(), + Base::quality(), + _error_msg, + Base::attributeTraits()); + + // success in running the store command, so set the result as true + HdbppTxBase<Conn>::setResult(true); + return *this; +} + +//============================================================================= +//============================================================================= +template<typename Conn> +void HdbppTxDataEventError<Conn>::print(std::ostream &os) const noexcept +{ + os << "HdbppTxDataEventError(base: "; + HdbppTxDataEventBase<Conn, HdbppTxDataEventError>::print(os); + + os << ", " + << "_error_msg: " << _error_msg << ")"; +} + +} // namespace hdbpp +#endif // _HDBPP_TX_DATA_EVENT_ERROR_HPP diff --git a/src/HdbppTxHistoryEvent.hpp b/src/HdbppTxHistoryEvent.hpp index 497dcdc2e3098425782d9a41f377119c715c6212..dc499b7624ee6ec3de71175f4d06ebcbd2c4be1f 100644 --- a/src/HdbppTxHistoryEvent.hpp +++ b/src/HdbppTxHistoryEvent.hpp @@ -104,13 +104,13 @@ HdbppTxHistoryEvent<Conn> &HdbppTxHistoryEvent<Conn>::store() if (_event.empty()) { std::string msg {"The event string is reporting empty. Unable to complete the transaction."}; - spdlog::error("Error: {}", msg); + spdlog::error("Error: {} For attribute {}", msg, _attr_name); Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); } else if (HdbppTxBase<Conn>::connection().isClosed()) { std::string msg {"The connection is reporting it is closed. Unable to store event."}; - spdlog::error("Error: {}", msg); + spdlog::error("Error: {} For attribute {}", msg, _attr_name); Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); } @@ -119,11 +119,18 @@ HdbppTxHistoryEvent<Conn> &HdbppTxHistoryEvent<Conn>::store() // record a crash event before the next start event if (_event == events::StartEvent) { - auto last_event = HdbppTxBase<Conn>::connection().fetchLastHistoryEvent(HdbppTxBase<Conn>::attrNameForStorage(_attr_name)); + auto last_event = HdbppTxBase<Conn>::connection().fetchLastHistoryEvent( + HdbppTxBase<Conn>::attrNameForStorage(_attr_name)); // check the last event was a StartEvent if (last_event == events::StartEvent) { + spdlog::trace("Detected a double: {} event for attribute: {}, storing a {}: before second {}:", + events::StartEvent, + _attr_name.fqdnAttributeName(), + events::CrashEvent, + events::StartEvent); + // insert the crash event HdbppTxBase<Conn>::connection() .template createTx<HdbppTxHistoryEvent>() diff --git a/src/HdbppTxNewAttribute.hpp b/src/HdbppTxNewAttribute.hpp index 38091dca11e123b225a01eacfc441901a3af0335..2c9c5cca00ba47d2f65611a06d9b2a7c2f867448 100644 --- a/src/HdbppTxNewAttribute.hpp +++ b/src/HdbppTxNewAttribute.hpp @@ -76,13 +76,13 @@ HdbppTxNewAttribute<Conn> &HdbppTxNewAttribute<Conn>::store() else if (!_traits_set) { std::string msg {"AttributeTraits are not set. Unable to complete the transaction."}; - spdlog::error("Error: {}", msg); + spdlog::error("Error: {} For attribute {}", msg, _attr_name); Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); } else if (HdbppTxBase<Conn>::connection().isClosed()) { std::string msg {"The connection is reporting it is closed. Unable to store new attribute."}; - spdlog::error("Error: {}", msg); + spdlog::error("Error: {} For attribute {}", msg, _attr_name); Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); } diff --git a/src/HdbppTxParameterEvent.hpp b/src/HdbppTxParameterEvent.hpp index d9c5e027ac04290e726c73e8c267b6321957484e..1911020ed22795898a904c4ace3793aae3d69d1b 100644 --- a/src/HdbppTxParameterEvent.hpp +++ b/src/HdbppTxParameterEvent.hpp @@ -91,19 +91,19 @@ HdbppTxParameterEvent<Conn> &HdbppTxParameterEvent<Conn>::store() else if (!_attr_info_ex_set) { std::string msg {"AttributeInfo is not set. Unable to complete the transaction."}; - spdlog::error("Error: {}", msg); + spdlog::error("Error: {} For attribute {}", msg, _attr_name); Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); } else if (_event_time == 0) { std::string msg {"Event time is not set. Unable to complete the transaction."}; - spdlog::error("Error: {}", msg); + spdlog::error("Error: {} For attribute {}", msg, _attr_name); Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); } else if (HdbppTxBase<Conn>::connection().isClosed()) { string msg {"The connection is reporting it is closed. Unable to store parameter event."}; - spdlog::error("Error: {}", msg); + spdlog::error("Error: {} For attribute {}", msg, _attr_name); Tango::Except::throw_exception("Invalid Argument", msg, LOCATION_INFO); } diff --git a/src/LibUtils.cpp b/src/LibUtils.cpp old mode 100755 new mode 100644 index 54e5219e32d5f9fb6e72a1db4023fea14ab70ecb..414f04fe8e5b23a9f433a53000cdcf448b15362e --- a/src/LibUtils.cpp +++ b/src/LibUtils.cpp @@ -84,7 +84,22 @@ ostream &operator<<(ostream &os, Tango::AttributeDataType type) return os; } -const int LoggerThreadCount = 1; +//============================================================================= +//============================================================================= +ostream &operator<<(ostream &os, Tango::AttrQuality quality) +{ + switch (quality) + { + case Tango::ATTR_VALID: os << "ATTR_VALID"; return os; + case Tango::ATTR_INVALID: os << "ATTR_INVALID"; return os; + case Tango::ATTR_ALARM: os << "ATTR_ALARM"; return os; + case Tango::ATTR_CHANGING: os << "ATTR_CHANGING"; return os; + case Tango::ATTR_WARNING: os << "ATTR_WARNING"; return os; + } + + os << "UNKNOWN"; + return os; +} //============================================================================= //============================================================================= @@ -92,7 +107,7 @@ void LogConfigurator::initLogging(bool enable_file, bool enable_console, const s { try { - spdlog::init_thread_pool(8192, LoggerThreadCount); + spdlog::init_thread_pool(8192, 1); vector<spdlog::sink_ptr> sinks; @@ -105,8 +120,11 @@ void LogConfigurator::initLogging(bool enable_file, bool enable_console, const s if (sinks.empty()) sinks.push_back(make_shared<spdlog::sinks::null_sink_mt>()); - auto logger = make_shared<spdlog::async_logger>( - LibLoggerName, sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::overrun_oldest); + auto logger = make_shared<spdlog::async_logger>(LibLoggerName, + sinks.begin(), + sinks.end(), + spdlog::thread_pool(), + spdlog::async_overflow_policy::overrun_oldest); spdlog::register_logger(logger); spdlog::flush_every(std::chrono::seconds(1)); diff --git a/src/LibUtils.hpp b/src/LibUtils.hpp old mode 100755 new mode 100644 index 988192d11866247b24a08e69141c88b18a52d0b9..b569ac879b74338e1bb85c62dcc75dbf3510d0a6 --- a/src/LibUtils.hpp +++ b/src/LibUtils.hpp @@ -50,6 +50,7 @@ auto operator<<(std::ostream &os, const T &t) -> decltype(t.print(os), static_ca std::ostream &operator<<(std::ostream &os, Tango::AttrWriteType write_type); std::ostream &operator<<(std::ostream &os, Tango::AttrDataFormat format); std::ostream &operator<<(std::ostream &os, Tango::AttributeDataType type); +std::ostream &operator<<(std::ostream &os, Tango::AttrQuality quality); // SPDLOG config and setup const string LibLoggerName = "hdbpp"; diff --git a/src/PqxxExtension.hpp b/src/PqxxExtension.hpp old mode 100755 new mode 100644 index c99ca56b27d4ecb85ec180de4664c2c546d3f355..c4d7961583864203ca19fdca52120ffa26ac89d1 --- a/src/PqxxExtension.hpp +++ b/src/PqxxExtension.hpp @@ -30,8 +30,6 @@ namespace pqxx { namespace internal { - // these specialisations help pull the data from results - // using the name of the traits specialisation. template<> struct type_name<uint8_t> { diff --git a/src/QueryBuilder.cpp b/src/QueryBuilder.cpp index 35aa828feca402b6ca4961f74958b98246727749..aff4ba424b888b6b24169a24cde4fc865e7361e6 100644 --- a/src/QueryBuilder.cpp +++ b/src/QueryBuilder.cpp @@ -30,7 +30,7 @@ namespace pqxx_conn { namespace query_utils { - // these specialisation just return the correct postgres cast for the insert queries, + // these specializations just return the correct postgres cast for the insert queries, // this is important for the custom types, since the library libpqxx and postgres will // not know how to store them. template<> @@ -130,16 +130,29 @@ namespace pqxx_conn static string query = "INSERT INTO " + CONF_TABLE_NAME + " (" + CONF_COL_NAME + "," + - CONF_COL_DATA_TYPE_ID + "," + + CONF_COL_TYPE_ID + "," + + CONF_COL_FORMAT_TYPE_ID + "," + + CONF_COL_WRITE_TYPE_ID + "," + + CONF_COL_TABLE_NAME + "," + CONF_COL_CS_NAME + "," + CONF_COL_DOMAIN + "," + CONF_COL_FAMILY + "," + CONF_COL_MEMBER + "," + CONF_COL_LAST_NAME + ") (" + - "SELECT " + - "$1," + CONF_TYPE_COL_TYPE_ID + ",$2,$3,$4,$5,$6 " + - "FROM " + CONF_TYPE_TABLE_NAME + " " + - "WHERE " + CONF_TYPE_COL_TYPE + " = $7) RETURNING " + CONF_COL_ID; + "SELECT " + + "$1," + + CONF_TYPE_COL_TYPE_ID + "," + + CONF_FORMAT_COL_FORMAT_ID + "," + + CONF_WRITE_COL_WRITE_ID + + ",$2,$3,$4,$5,$6,$7 " + + "FROM " + + CONF_TYPE_TABLE_NAME + ", " + + CONF_FORMAT_TABLE_NAME + ", " + + CONF_WRITE_TABLE_NAME + " " + + "WHERE " + CONF_TYPE_TABLE_NAME + "." + CONF_TYPE_COL_TYPE_NUM + " = $8 " + + "AND " + CONF_FORMAT_TABLE_NAME + "." + CONF_FORMAT_COL_FORMAT_NUM + " = $9 " + + "AND " + CONF_WRITE_TABLE_NAME + "." + CONF_WRITE_COL_WRITE_NUM + " = $10) " + + "RETURNING " + CONF_COL_ID; // clang-format on return query; @@ -216,7 +229,8 @@ namespace pqxx_conn auto query = "INSERT INTO " + QueryBuilder::tableName(traits) + " (" + DAT_COL_ID + "," + DAT_COL_DATA_TIME; // split to ensure increments are in the correct order - query = query + "," + DAT_COL_QUALITY + "," + DAT_COL_ERROR_DESC_ID + ") VALUES ($" + to_string(++param_number); + query = query + "," + DAT_COL_QUALITY + "," + DAT_COL_ERROR_DESC_ID + ") VALUES ($" + + to_string(++param_number); query = query + ",TO_TIMESTAMP($" + to_string(++param_number) + ")"; @@ -252,14 +266,16 @@ namespace pqxx_conn //============================================================================= //============================================================================= - const string QueryBuilder::fetchAllValuesQuery(const string &column_name, const string &table_name, const string &reference) + const string QueryBuilder::fetchAllValuesQuery( + const string &column_name, const string &table_name, const string &reference) { return "SELECT " + column_name + ", " + reference + " " + "FROM " + table_name; } //============================================================================= //============================================================================= - const string QueryBuilder::fetchValueQuery(const string &column_name, const string &table_name, const string &reference) + const string QueryBuilder::fetchValueQuery( + const string &column_name, const string &table_name, const string &reference) { return "SELECT " + column_name + " " + "FROM " + table_name + " WHERE " + reference + "=$1"; } @@ -297,8 +313,7 @@ namespace pqxx_conn return string("Unknown"); }() + - "_" + - [&traits]() { + "_" + [&traits]() { switch (traits.type()) { case Tango::DEV_DOUBLE: return TYPE_DEV_DOUBLE; @@ -318,13 +333,13 @@ namespace pqxx_conn } return string("Unknown"); - }() + - "_" + (traits.isReadOnly() ? TYPE_RO : TYPE_RW); + }(); } //============================================================================= //============================================================================= - const string &QueryBuilder::handleCache(map<AttributeTraits, string> &cache, const AttributeTraits &traits, const string &stub) + const string &QueryBuilder::handleCache( + map<AttributeTraits, string> &cache, const AttributeTraits &traits, const string &stub) { auto result = cache.find(traits); @@ -353,7 +368,8 @@ namespace pqxx_conn { os << "QueryBuilder(cached " << "data_event: name/query " << _data_event_query_names.size() << "/" << _data_event_queries.size() << ", " - << "data_event_error: name/query " << _data_event_error_query_names.size() << "/" << _data_event_error_queries.size() << ")"; + << "data_event_error: name/query " << _data_event_error_query_names.size() << "/" + << _data_event_error_queries.size() << ")"; } } // namespace pqxx_conn } // namespace hdbpp diff --git a/src/QueryBuilder.hpp b/src/QueryBuilder.hpp index f9687bdeb8d7299e34ef211b0eb29bb85f10d4a7..38c7fb2b0d3056c28b2fc1ed2784fe87d2ecac28 100644 --- a/src/QueryBuilder.hpp +++ b/src/QueryBuilder.hpp @@ -118,7 +118,8 @@ namespace pqxx_conn private: // generic function to handle caching items into the cache maps - const string &handleCache(std::map<AttributeTraits, std::string> &cache, const AttributeTraits &traits, const std::string &stub); + const string &handleCache( + std::map<AttributeTraits, std::string> &cache, const AttributeTraits &traits, const std::string &stub); // cached query names, these are built from the traits object std::map<AttributeTraits, std::string> _data_event_query_names; @@ -160,11 +161,13 @@ namespace pqxx_conn // add the read parameter with cast if (traits.hasReadData()) - query = query + "," + "$" + to_string(++param_number) + "::" + query_utils::postgresCast<T>(traits.isArray()); + query = query + "," + "$" + to_string(++param_number) + + "::" + query_utils::postgresCast<T>(traits.isArray()); // add the write parameter with cast if (traits.hasWriteData()) - query = query + "," + "$" + to_string(++param_number) + "::" + query_utils::postgresCast<T>(traits.isArray()); + query = query + "," + "$" + to_string(++param_number) + + "::" + query_utils::postgresCast<T>(traits.isArray()); query = query + "," + "$" + to_string(++param_number) + ")"; diff --git a/src/TimescaleSchema.hpp b/src/TimescaleSchema.hpp index 2a7cc1f02e9502d01a9fb4074b3bb21d7a8f40b8..8bdf14ae96465c335e3db1e82dbabccc2fbbab14 100644 --- a/src/TimescaleSchema.hpp +++ b/src/TimescaleSchema.hpp @@ -47,17 +47,15 @@ namespace pqxx_conn const std::string TYPE_DEV_STATE = "devstate"; const std::string TYPE_DEV_ENCODED = "devencoded"; const std::string TYPE_DEV_ENUM = "devenum"; - const std::string TYPE_RO = "ro"; - const std::string TYPE_RW = "rw"; // att_conf table const std::string CONF_TABLE_NAME = "att_conf"; const std::string CONF_COL_ID = "att_conf_id"; const std::string CONF_COL_NAME = "att_name"; - const std::string CONF_COL_DATA_TYPE_ID = "att_conf_data_type_id"; - const std::string CONF_COL_TYPE = "type"; - const std::string CONF_COL_WRITE_TYPE = "write_type"; - const std::string CONF_COL_FORMAT_TYPE = "format_type"; + const std::string CONF_COL_TYPE_ID = "att_conf_type_id"; + const std::string CONF_COL_FORMAT_TYPE_ID = "att_conf_format_id"; + const std::string CONF_COL_WRITE_TYPE_ID = "att_conf_write_id"; + const std::string CONF_COL_TABLE_NAME = "table_name"; const std::string CONF_COL_CS_NAME = "cs_name"; const std::string CONF_COL_DOMAIN = "domain"; const std::string CONF_COL_FAMILY = "family"; @@ -65,16 +63,30 @@ namespace pqxx_conn const std::string CONF_COL_LAST_NAME = "name"; const std::string CONF_COL_TTL = "ttl"; - // att_conf_data_type table - const std::string CONF_TYPE_TABLE_NAME = "att_conf_data_type"; - const std::string CONF_TYPE_COL_TYPE_ID = "att_conf_data_type_id"; - const std::string CONF_TYPE_COL_TYPE = "data_type"; + // att_conf_type table + const std::string CONF_TYPE_TABLE_NAME = "att_conf_type"; + const std::string CONF_TYPE_COL_TYPE_ID = "att_conf_type_id"; + const std::string CONF_TYPE_COL_TYPE = "type"; + const std::string CONF_TYPE_COL_TYPE_NUM = "type_num"; + + // att_conf_format table + const std::string CONF_FORMAT_TABLE_NAME = "att_conf_format"; + const std::string CONF_FORMAT_COL_FORMAT_ID = "att_conf_format_id"; + const std::string CONF_FORMAT_COL_FORMAT = "format"; + const std::string CONF_FORMAT_COL_FORMAT_NUM = "format_num"; + + // att_conf_write table + const std::string CONF_WRITE_TABLE_NAME = "att_conf_write"; + const std::string CONF_WRITE_COL_WRITE_ID = "att_conf_write_id"; + const std::string CONF_WRITE_COL_WRITE = "write"; + const std::string CONF_WRITE_COL_WRITE_NUM = "write_num"; // att_history table const std::string HISTORY_TABLE_NAME = "att_history"; const std::string HISTORY_COL_ID = "att_conf_id"; const std::string HISTORY_COL_EVENT_ID = "att_history_event_id"; const std::string HISTORY_COL_TIME = "event_time"; + const std::string HISTORY_COL_DETAILS = "details"; // att_history_event table const std::string HISTORY_EVENT_TABLE_NAME = "att_history_event"; @@ -95,6 +107,7 @@ namespace pqxx_conn const std::string PARAM_COL_ARCHIVEABSCHANGE = "archive_abs_change"; const std::string PARAM_COL_ARCHIVEPERIOD = "archive_period"; const std::string PARAM_COL_DESCRIPTION = "description"; + const std::string PARAM_COL_DETAILS = "details"; // att_error_desc table const std::string ERR_TABLE_NAME = "att_error_desc"; @@ -110,13 +123,17 @@ namespace pqxx_conn const std::string DAT_COL_VALUE_W = "value_w"; const std::string DAT_COL_QUALITY = "quality"; const std::string DAT_COL_ERROR_DESC_ID = "att_error_desc_id"; + const std::string DAT_COL_DETAILS = "details"; + + // special fields for enums + const std::string DAT_COL_DAT_COL_VALUE_R_LABEL = "value_r_label"; + const std::string DAT_COL_DAT_COL_VALUE_W_LABEL = "value_w_label"; // special fields for image tables const std::string DAT_IMG_COL_DIMX_R = "dim_x_r"; const std::string DAT_IMG_COL_DIMY_R = "dim_y_r"; const std::string DAT_IMG_COL_DIMX_W = "dim_x_w"; const std::string DAT_IMG_COL_DIMY_W = "dim_y_w"; - } // namespace pqxx_conn } // namespace hdbpp #endif // _TIMESCALE_SCHEMA_HPP diff --git a/test/AttributeNameTests.cpp b/test/AttributeNameTests.cpp index a6606f6069440b9d3af7b1162c07c48647812a46..dd970688446659b85b1919c49149084eed59350a 100644 --- a/test/AttributeNameTests.cpp +++ b/test/AttributeNameTests.cpp @@ -39,7 +39,10 @@ SCENARIO("AttributeName supports fully qualified attribute name", "[attribute-na } WHEN("Full attribute name is requested") { - THEN("Valid full attribute name returned") { REQUIRE(attribute_name.fullAttributeName() == TestAttrFullAttrName); } + THEN("Valid full attribute name returned") + { + REQUIRE(attribute_name.fullAttributeName() == TestAttrFullAttrName); + } } WHEN("Tango host is requested") { @@ -79,11 +82,17 @@ SCENARIO("AttributeName supports fully qualified attribute name missing tango pr WHEN("Fully qualified domain name requested") { - THEN("Valid fqdn is returned") { REQUIRE(attribute_name.fqdnAttributeName() == TestAttrFQDNameNoTangoQual); } + THEN("Valid fqdn is returned") + { + REQUIRE(attribute_name.fqdnAttributeName() == TestAttrFQDNameNoTangoQual); + } } WHEN("Full attribute name is requested") { - THEN("Valid full attribute name returned") { REQUIRE(attribute_name.fullAttributeName() == TestAttrFullAttrName); } + THEN("Valid full attribute name returned") + { + REQUIRE(attribute_name.fullAttributeName() == TestAttrFullAttrName); + } } WHEN("Tango host is requested") { @@ -127,7 +136,10 @@ SCENARIO("AttributeName supports fully qualified attribute name but no network d } WHEN("Full attribute name is requested") { - THEN("Valid full attribute name returned") { REQUIRE(attribute_name.fullAttributeName() == TestAttrFullAttrName); } + THEN("Valid full attribute name returned") + { + REQUIRE(attribute_name.fullAttributeName() == TestAttrFullAttrName); + } } WHEN("Tango host is requested") { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9f75b08c9b0fd721e2754b3e2b51fa1765e2ceb8..ee7f2f576a3a76e633797cdb9b27bd6c1c149fef 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -16,6 +16,9 @@ set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/AttributeTraitsTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ColumnCacheTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/DbConnectionTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/HdbppTxBaseTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/HdbppTxDataEventTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/HdbppTxDataEventErrorTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/HdbppTxNewAttributeTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/HdbppTxHistoryEventTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/HdbppTxParameterEventTests.cpp diff --git a/test/ColumnCacheTests.cpp b/test/ColumnCacheTests.cpp index 20555dfaf34b418d88087143402373a3cc56a5ef..bb59050d0cc0b82871d7c75f01629c8672d705f0 100644 --- a/test/ColumnCacheTests.cpp +++ b/test/ColumnCacheTests.cpp @@ -45,8 +45,8 @@ const int NewValue2Id = 12; void createColumnCacheTestDb(pqxx::work &tx) { - tx.exec("CREATE TEMP TABLE " + TableName + " (" + IdCol + " serial, " + ReferenceCol + " text, " + "PRIMARY KEY (" + IdCol + - ")) ON COMMIT PRESERVE ROWS;"); + tx.exec("CREATE TEMP TABLE " + TableName + " (" + IdCol + " serial, " + ReferenceCol + " text, " + "PRIMARY KEY (" + + IdCol + ")) ON COMMIT PRESERVE ROWS;"); tx.exec("INSERT INTO " + TableName + "(" + ReferenceCol + ") VALUES (" + tx.quote(Ref1) + ");"); tx.exec("INSERT INTO " + TableName + "(" + ReferenceCol + ") VALUES (" + tx.quote(Ref2) + ");"); @@ -255,7 +255,8 @@ SCENARIO("Clearing the cache does not stop entries being cached again", "[db-acc conn->disconnect(); } -SCENARIO("ColumnCache will fetch values from db and cache them as they are requested by reference", "[db-access][column-cache][psql]") +SCENARIO("ColumnCache will fetch values from db and cache them as they are requested by reference", + "[db-access][column-cache][psql]") { auto conn = connectDb(); diff --git a/test/DbConnectionTests.cpp b/test/DbConnectionTests.cpp old mode 100755 new mode 100644 index db21b09fb39632806b95932d93dda92d1f32ba19..166c81dde6df0a38bcc3783262706d8b3dadae84 --- a/test/DbConnectionTests.cpp +++ b/test/DbConnectionTests.cpp @@ -20,6 +20,7 @@ #include "DbConnection.hpp" #include "HdbppDefines.hpp" #include "LibUtils.hpp" +#include "QueryBuilder.hpp" #include "TestHelpers.hpp" #include "TimescaleSchema.hpp" #include "catch2/catch.hpp" @@ -55,8 +56,8 @@ void clearTable(pqxx::connection &conn, const string &table_name) // wrapper to store an attribute void storeTestAttribute(DbConnection &conn, const AttributeTraits &traits) { - REQUIRE_NOTHROW( - conn.storeAttribute(TestAttrFinalName, TestAttrCs, TestAttrDomain, TestAttrFamily, TestAttrMember, TestAttrName, traits)); + REQUIRE_NOTHROW(conn.storeAttribute( + TestAttrFinalName, TestAttrCs, TestAttrDomain, TestAttrFamily, TestAttrMember, TestAttrName, traits)); } // wrapper to store some event data, and return the data for comparison @@ -157,11 +158,13 @@ bool compareVector<double>(const vector<double> &lhs, const vector<double> &rhs) // taking the original data as a reference, this function loads the last line of data and compares // it to the reference data as a test template<typename T> -void checkStoreTestEventData(pqxx::connection &test_conn, const AttributeTraits &traits, const tuple<vector<T>, vector<T>> &data) +void checkStoreTestEventData( + pqxx::connection &test_conn, const AttributeTraits &traits, const tuple<vector<T>, vector<T>> &data) { pqxx::work tx {test_conn}; - auto data_row(tx.exec1("SELECT * FROM " + query_builder.tableName(traits) + " ORDER BY " + DAT_COL_DATA_TIME + " LIMIT 1")); + auto data_row( + tx.exec1("SELECT * FROM " + query_builder.tableName(traits) + " ORDER BY " + DAT_COL_DATA_TIME + " LIMIT 1")); auto attr_row(tx.exec1("SELECT * FROM " + CONF_TABLE_NAME)); tx.commit(); @@ -192,7 +195,8 @@ void checkStoreTestEventData(pqxx::connection &test_conn, const AttributeTraits } }; // namespace psql_conn_test -SCENARIO("The DbConnection class can open a valid connection to a postgres node", "[db-access][hdbpp-db-access][db-connection][psql]") +SCENARIO("The DbConnection class can open a valid connection to a postgres node", + "[db-access][hdbpp-db-access][db-connection][psql]") { GIVEN("An unconnected DbConnection object") { @@ -213,7 +217,8 @@ SCENARIO("The DbConnection class can open a valid connection to a postgres node" } } -SCENARIO("The DbConnection class handles a bad connection attempt with an exception", "[db-access][hdbpp-db-access][db-connection][psql]") +SCENARIO("The DbConnection class handles a bad connection attempt with an exception", + "[db-access][hdbpp-db-access][db-connection][psql]") { GIVEN("An unconnected DbConnection object") { @@ -264,24 +269,44 @@ SCENARIO("Storing Attributes in the database", "[db-access][hdbpp-db-access][db- { { pqxx::work tx {test_conn}; - auto r(tx.exec1("SELECT * FROM " + CONF_TABLE_NAME)); + auto attr_row(tx.exec1("SELECT * FROM " + CONF_TABLE_NAME)); + + auto type_row(tx.exec1("SELECT " + CONF_TYPE_COL_TYPE_ID + " FROM " + CONF_TYPE_TABLE_NAME + + " WHERE " + CONF_TYPE_COL_TYPE_NUM + " = " + std::to_string(traits.type()))); + + auto format_row(tx.exec1("SELECT " + CONF_FORMAT_COL_FORMAT_ID + " FROM " + CONF_FORMAT_TABLE_NAME + + " WHERE " + CONF_FORMAT_COL_FORMAT_NUM + " = " + std::to_string(traits.formatType()))); + + auto access_row(tx.exec1("SELECT " + CONF_WRITE_COL_WRITE_ID + " FROM " + CONF_WRITE_TABLE_NAME + + " WHERE " + CONF_WRITE_COL_WRITE_NUM + " = " + std::to_string(traits.writeType()))); + tx.commit(); - REQUIRE(r.at(CONF_COL_NAME).as<string>() == TestAttrFQDName); - REQUIRE(r.at(CONF_COL_CS_NAME).as<string>() == TestAttrCs); - REQUIRE(r.at(CONF_COL_DOMAIN).as<string>() == TestAttrDomain); - REQUIRE(r.at(CONF_COL_FAMILY).as<string>() == TestAttrFamily); - REQUIRE(r.at(CONF_COL_MEMBER).as<string>() == TestAttrMember); - REQUIRE(r.at(CONF_COL_LAST_NAME).as<string>() == TestAttrName); + REQUIRE(attr_row.at(CONF_COL_NAME).as<string>() == TestAttrFQDName); + REQUIRE(attr_row.at(CONF_COL_CS_NAME).as<string>() == TestAttrCs); + REQUIRE(attr_row.at(CONF_COL_DOMAIN).as<string>() == TestAttrDomain); + REQUIRE(attr_row.at(CONF_COL_FAMILY).as<string>() == TestAttrFamily); + REQUIRE(attr_row.at(CONF_COL_MEMBER).as<string>() == TestAttrMember); + REQUIRE(attr_row.at(CONF_COL_LAST_NAME).as<string>() == TestAttrName); + REQUIRE(attr_row.at(CONF_COL_TABLE_NAME).as<string>() == QueryBuilder().tableName(traits)); + REQUIRE(attr_row.at(CONF_COL_TYPE_ID).as<int>() == type_row.at(CONF_TYPE_COL_TYPE_ID).as<int>()); + REQUIRE(attr_row.at(CONF_COL_FORMAT_TYPE_ID).as<int>() == + format_row.at(CONF_FORMAT_COL_FORMAT_ID).as<int>()); + REQUIRE(attr_row.at(CONF_COL_WRITE_TYPE_ID).as<int>() == + access_row.at(CONF_WRITE_COL_WRITE_ID).as<int>()); } } AND_WHEN("Trying to store the attribute again") { THEN("An exception is throw as the entry already exists in the database") { - REQUIRE_THROWS_AS( - conn.storeAttribute( - TestAttrFinalName, TestAttrCs, TestAttrDomain, TestAttrFamily, TestAttrMember, TestAttrName, traits), + REQUIRE_THROWS_AS(conn.storeAttribute(TestAttrFinalName, + TestAttrCs, + TestAttrDomain, + TestAttrFamily, + TestAttrMember, + TestAttrName, + traits), Tango::DevFailed); } } @@ -314,8 +339,13 @@ SCENARIO("Storing Attributes in a disconnected state", "[db-access][hdbpp-db-acc THEN("An exception is throw as the database connection is down") { - REQUIRE_THROWS_AS(conn.storeAttribute( - TestAttrFinalName, TestAttrCs, TestAttrDomain, TestAttrFamily, TestAttrMember, TestAttrName, traits), + REQUIRE_THROWS_AS(conn.storeAttribute(TestAttrFinalName, + TestAttrCs, + TestAttrDomain, + TestAttrFamily, + TestAttrMember, + TestAttrName, + traits), Tango::DevFailed); } } @@ -362,7 +392,8 @@ SCENARIO("Storing History Events in the database", "[db-access][hdbpp-db-access] REQUIRE(event_row.at(HISTORY_EVENT_COL_EVENT).as<string>() == events::PauseEvent); // check event id matches event table id - REQUIRE(event_row.at(HISTORY_EVENT_COL_EVENT_ID).as<int>() == history_row.at(HISTORY_COL_EVENT_ID).as<int>()); + REQUIRE(event_row.at(HISTORY_EVENT_COL_EVENT_ID).as<int>() == + history_row.at(HISTORY_COL_EVENT_ID).as<int>()); // check attribute id match REQUIRE(attr_row.at(CONF_COL_ID).as<int>() == history_row.at(HISTORY_COL_ID).as<int>()); @@ -389,7 +420,8 @@ SCENARIO("Storing History Events in the database", "[db-access][hdbpp-db-access] REQUIRE(attr_row.at(CONF_COL_ID).as<int>() == row.at(HISTORY_COL_ID).as<int>()); // check event id matches event table id - REQUIRE(row.at(HISTORY_COL_EVENT_ID).as<int>() == event_result.at(HISTORY_COL_EVENT_ID).as<int>()); + REQUIRE(row.at(HISTORY_COL_EVENT_ID).as<int>() == + event_result.at(HISTORY_COL_EVENT_ID).as<int>()); } } } @@ -666,8 +698,8 @@ SCENARIO("Storing event data which is invalid", "[db-access][hdbpp-db-access][db { { pqxx::work tx {test_conn}; - auto data_row(tx.exec1("SELECT * FROM " + psql_conn_test::query_builder.tableName(traits) + " ORDER BY " + - DAT_COL_DATA_TIME + " LIMIT 1")); + auto data_row(tx.exec1("SELECT * FROM " + psql_conn_test::query_builder.tableName(traits) + + " ORDER BY " + DAT_COL_DATA_TIME + " LIMIT 1")); auto attr_row(tx.exec1("SELECT * FROM " + CONF_TABLE_NAME)); tx.commit(); @@ -692,8 +724,8 @@ SCENARIO("Storing event data which is invalid", "[db-access][hdbpp-db-access][db { { pqxx::work tx {test_conn}; - auto data_row(tx.exec1("SELECT * FROM " + psql_conn_test::query_builder.tableName(traits) + " ORDER BY " + - DAT_COL_DATA_TIME + " LIMIT 1")); + auto data_row(tx.exec1("SELECT * FROM " + psql_conn_test::query_builder.tableName(traits) + + " ORDER BY " + DAT_COL_DATA_TIME + " LIMIT 1")); auto attr_row(tx.exec1("SELECT * FROM " + CONF_TABLE_NAME)); tx.commit(); @@ -712,7 +744,8 @@ SCENARIO("Storing event data which is invalid", "[db-access][hdbpp-db-access][db test_conn.disconnect(); } -TEST_CASE("Storing event data of all Tango type combinations in the database", "[db-access][hdbpp-db-access][db-connection][psql]") +TEST_CASE("Storing event data of all Tango type combinations in the database", + "[db-access][hdbpp-db-access][db-connection][psql]") { DbConnection conn; REQUIRE_NOTHROW(conn.connect(postgres_db::HdbppConnectionString)); @@ -756,6 +789,7 @@ TEST_CASE("Storing event data of all Tango type combinations in the database", " switch (traits.type()) { + // TODO enable commented out calls. //case Tango::DEV_BOOLEAN: //psql_conn_test::checkStoreTestEventData( //test_conn, traits, psql_conn_test::storeTestEventData<Tango::DEV_BOOLEAN>(conn, traits)); @@ -810,8 +844,9 @@ TEST_CASE("Storing event data of all Tango type combinations in the database", " break; case Tango::DEV_ULONG64: - psql_conn_test::checkStoreTestEventData( - test_conn, traits, psql_conn_test::storeTestEventData<Tango::DEV_ULONG64>(conn, traits)); + psql_conn_test::checkStoreTestEventData(test_conn, + traits, + psql_conn_test::storeTestEventData<Tango::DEV_ULONG64>(conn, traits)); break; @@ -917,15 +952,16 @@ SCENARIO("Storing data events as errors", "[db-access][hdbpp-db-access][db-conne WHEN("Storing a new error message in the database") { - REQUIRE_NOTHROW(conn.storeDataEventError(TestAttrFinalName, event_time, Tango::ATTR_VALID, error_msg, traits)); + REQUIRE_NOTHROW( + conn.storeDataEventError(TestAttrFinalName, event_time, Tango::ATTR_VALID, error_msg, traits)); THEN("Then both the event and history event exists in the database, and can be read back and verified") { { pqxx::work tx {test_conn}; - auto data_row(tx.exec1("SELECT * FROM " + psql_conn_test::query_builder.tableName(traits) + " ORDER BY " + - DAT_COL_DATA_TIME + " LIMIT 1")); + auto data_row(tx.exec1("SELECT * FROM " + psql_conn_test::query_builder.tableName(traits) + + " ORDER BY " + DAT_COL_DATA_TIME + " LIMIT 1")); auto attr_row(tx.exec1("SELECT * FROM " + CONF_TABLE_NAME)); auto error_row(tx.exec1("SELECT * FROM " + ERR_TABLE_NAME)); @@ -939,15 +975,19 @@ SCENARIO("Storing data events as errors", "[db-access][hdbpp-db-access][db-conne } AND_WHEN("A second error is stored with the same message") { - REQUIRE_NOTHROW(conn.storeDataEventError(TestAttrFinalName, event_time, Tango::ATTR_VALID, error_msg, traits)); + gettimeofday(&tv, NULL); + event_time = tv.tv_sec + tv.tv_usec / 1.0e6; + + REQUIRE_NOTHROW( + conn.storeDataEventError(TestAttrFinalName, event_time, Tango::ATTR_VALID, error_msg, traits)); THEN("The same error id is used in the event data") { { pqxx::work tx {test_conn}; - auto data_row(tx.exec1("SELECT * FROM " + psql_conn_test::query_builder.tableName(traits) + " ORDER BY " + - DAT_COL_DATA_TIME + " LIMIT 1")); + auto data_row(tx.exec1("SELECT * FROM " + psql_conn_test::query_builder.tableName(traits) + + " ORDER BY " + DAT_COL_DATA_TIME + " LIMIT 1")); auto attr_row(tx.exec1("SELECT * FROM " + CONF_TABLE_NAME)); auto error_row(tx.exec1("SELECT * FROM " + ERR_TABLE_NAME)); @@ -1014,7 +1054,8 @@ SCENARIO("Fetching the last event after it has just been stored", "[db-access][h test_conn.disconnect(); } -SCENARIO("When no exevts have been stored, no error is thrown requesting the last event", "[hdbpp-tx][hdbpp-tx-history-event]") +SCENARIO("When no events have been stored, no error is thrown requesting the last event", + "[db-access][hdbpp-db-access][db-connection][psql]") { DbConnection conn; REQUIRE_NOTHROW(conn.connect(postgres_db::HdbppConnectionString)); @@ -1024,6 +1065,9 @@ SCENARIO("When no exevts have been stored, no error is thrown requesting the las GIVEN("A valid DbConnection connected to a hdbpp database with no attribute nor history event stored in it") { + psql_conn_test::clearTable(test_conn, HISTORY_TABLE_NAME); + psql_conn_test::clearTable(test_conn, HISTORY_EVENT_TABLE_NAME); + WHEN("Requesting the last event") { string event; diff --git a/test/HdbppTxBaseTests.cpp b/test/HdbppTxBaseTests.cpp index 3fdf9bcdf5df6a5b15cd9faf4166625d860181d2..aac37b22581f5a38fcf0bc1d6bf0a48f6860a574 100644 --- a/test/HdbppTxBaseTests.cpp +++ b/test/HdbppTxBaseTests.cpp @@ -37,7 +37,10 @@ class MockConnection class TestHdbppTxBase : public HdbppTxBase<MockConnection> { public: - static string testAttrNameForStorage(AttributeName &attr_name) { return HdbppTxBase<MockConnection>::attrNameForStorage(attr_name); } + static string testAttrNameForStorage(AttributeName &attr_name) + { + return HdbppTxBase<MockConnection>::attrNameForStorage(attr_name); + } }; }; // namespace hdbpp_base_test diff --git a/test/HdbppTxDataEventErrorTests.cpp b/test/HdbppTxDataEventErrorTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b032e1ad3d2a8508815ec147d0e321d29f2ff770 --- /dev/null +++ b/test/HdbppTxDataEventErrorTests.cpp @@ -0,0 +1,227 @@ +/* Copyright (C) : 2014-2019 + European Synchrotron Radiation Facility + BP 220, Grenoble 38043, FRANCE + + This file is part of libhdb++timescale. + + libhdb++timescale is free software: you can redistribute it and/or modify + it under the terms of the Lesser GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libhdb++timescale is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser + GNU General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with libhdb++timescale. If not, see <http://www.gnu.org/licenses/>. */ + +#include "ConnectionBase.hpp" +#include "HdbppTxDataEventError.hpp" +#include "HdbppTxFactory.hpp" +#include "TestHelpers.hpp" +#include "catch2/catch.hpp" + +using namespace std; +using namespace hdbpp; +using namespace hdbpp_test::attr_name; + +namespace hdbpp_data_event_test_error +{ +const string TestError = "A test error message"; + +// Mock connection to test the HdbppTxDataEvent class, only +// implements the functions that storeAttribute use, nothing more +class MockConnection : public ConnectionBase, public HdbppTxFactory<MockConnection> +{ +public: + // Enforced connection API from ConnectionBase + void connect(const string &) override { _conn_state = true; } + void disconnect() override { _conn_state = false; } + bool isOpen() const noexcept { return _conn_state; } + bool isClosed() const noexcept { return !isOpen(); } + + void storeDataEventError(const std::string &full_attr_name, + double event_time, + int quality, + const std::string &error_msg, + const AttributeTraits &traits) + { + if (store_attribute_triggers_ex) + throw runtime_error("A test exception"); + + // record the data for comparison + att_name = full_attr_name; + att_event_time = event_time; + // TODO make it Tango::AttrQuality for API + att_quality = (Tango::AttrQuality)quality; + att_traits = traits; + att_error_msg = error_msg; + } + + // expose the results of the store function so they can be checked + // in the results + + // storeDataEvent/storeDataEventError results + string att_name; + double att_event_time = 0; + Tango::AttrQuality att_quality; + AttributeTraits att_traits; + string att_error_msg; + bool store_attribute_triggers_ex = false; + +private: + // connection is always open unless test specifies closed + bool _conn_state = true; +}; +}; // namespace hdbpp_data_event_test_error + +SCENARIO("Construct a valid HdbppTxDataEventError error event for storage", "[hdbpp-tx][hdbpp-tx-data-event-error]") +{ + hdbpp_data_event_test_error::MockConnection conn; + + // ugly, how is this dealt with in Tango!?! + struct timeval tv; + struct Tango::TimeVal tango_tv; + gettimeofday(&tv, NULL); + tango_tv.tv_sec = tv.tv_sec; + tango_tv.tv_usec = tv.tv_usec; + tango_tv.tv_nsec = 0; + + GIVEN("An HdbppTxDataEventError object with no data set") + { + auto tx = conn.createTx<HdbppTxDataEventError>(); + auto traits = AttributeTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE); + + WHEN("Configuring an HdbppTxDataEventError object with an error") + { + REQUIRE_NOTHROW(tx.withName(TestAttrFQDName) + .withTraits(traits) + .withEventTime(tango_tv) + .withQuality(Tango::ATTR_VALID) + .withError(hdbpp_data_event_test_error::TestError)); + + REQUIRE_NOTHROW(tx.store()); + + THEN("The data is the same as that passed via method chaining, and there is no data") + { + REQUIRE(conn.att_name == TestAttrFinalName); + REQUIRE(conn.att_event_time == (tango_tv.tv_sec + tango_tv.tv_usec / 1.0e6)); + REQUIRE(conn.att_quality == Tango::ATTR_VALID); + REQUIRE(conn.att_traits == traits); + REQUIRE(conn.att_error_msg == hdbpp_data_event_test_error::TestError); + } + } + } +} + +SCENARIO("When attempting to store invalid HdbppTxDataEventError states, errors are thrown", + "[hdbpp-tx][hdbpp-tx-data-event-error]") +{ + hdbpp_data_event_test_error::MockConnection conn; + + // ugly, how is this dealt with in Tango!?! + struct timeval tv; + struct Tango::TimeVal tango_tv; + gettimeofday(&tv, NULL); + tango_tv.tv_sec = tv.tv_sec; + tango_tv.tv_usec = tv.tv_usec; + tango_tv.tv_nsec = 0; + + GIVEN("An HdbppTxDataEventError object with no data set") + { + auto traits = AttributeTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE); + auto tx = conn.createTx<HdbppTxDataEventError>(); + + WHEN("Attempting to store without setting data") + { + THEN("An exception is raised and result is false") + { + REQUIRE_THROWS(tx.store()); + REQUIRE(!tx.result()); + } + } + WHEN("Attempting to store with just name set") + { + tx.withName(TestAttrFQDName); + + THEN("An exception is raised and result is false") + { + REQUIRE_THROWS(tx.store()); + REQUIRE(!tx.result()); + } + AND_WHEN("Setting the AtributeTraits and trying again") + { + tx.withTraits(traits); + + THEN("An exception is raised and result is false") + { + REQUIRE_THROWS(tx.store()); + REQUIRE(!tx.result()); + } + AND_WHEN("Setting the error message and trying again") + { + tx.withError(hdbpp_data_event_test_error::TestError); + + THEN("No exception is raised") + { + REQUIRE_NOTHROW(tx.store()); + REQUIRE(tx.result()); + } + } + } + } + WHEN("Attempting to store with valid data, but disconnected connection") + { + conn.disconnect(); + REQUIRE(conn.isClosed()); + + auto tx = conn.createTx<HdbppTxDataEventError>(); + + REQUIRE_NOTHROW(tx.withName(TestAttrFQDName) + .withTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE) + .withEventTime(tango_tv) + .withQuality(Tango::ATTR_VALID) + .withError(hdbpp_data_event_test_error::TestError)); + + THEN("An exception is raised and result is false") + { + REQUIRE_THROWS(tx.store()); + REQUIRE(!tx.result()); + } + } + } +} + +SCENARIO("HdbppTxDataEventError Simulated exception received", "[hdbpp-tx][hdbpp-tx-data-event-error]") +{ + hdbpp_data_event_test_error::MockConnection conn; + + // ugly, how is this dealt with in Tango!?! + struct timeval tv; + struct Tango::TimeVal tango_tv; + gettimeofday(&tv, NULL); + tango_tv.tv_sec = tv.tv_sec; + tango_tv.tv_usec = tv.tv_usec; + tango_tv.tv_nsec = 0; + + GIVEN("An HdbppTxDataEventError object with name and traits set") + { + auto traits = AttributeTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE); + auto tx = conn.createTx<HdbppTxDataEventError>(); + + REQUIRE_NOTHROW(tx.withName(TestAttrFQDName) + .withTraits(traits) + .withEventTime(tango_tv) + .withQuality(Tango::ATTR_VALID) + .withError(hdbpp_data_event_test_error::TestError)); + + WHEN("Storing the transaction with a triggered exception set") + { + conn.store_attribute_triggers_ex = true; + + THEN("An exception is raised") { REQUIRE_THROWS_AS(tx.store(), runtime_error); } + } + } +} \ No newline at end of file diff --git a/test/HdbppTxDataEventTests.cpp b/test/HdbppTxDataEventTests.cpp index dff837641939ae28738d84eb65ee98d687af18a9..cdc2378ef1bb3e8db927ad6c05c30300078193e6 100644 --- a/test/HdbppTxDataEventTests.cpp +++ b/test/HdbppTxDataEventTests.cpp @@ -18,43 +18,105 @@ along with libhdb++timescale. If not, see <http://www.gnu.org/licenses/>. */ #include "ConnectionBase.hpp" -#include "HdbppTxEventData.hpp" +#include "HdbppTxDataEvent.hpp" #include "HdbppTxFactory.hpp" #include "TestHelpers.hpp" #include "catch2/catch.hpp" using namespace std; -using namespace tango; using namespace hdbpp; using namespace hdbpp_test::attr_name; +using namespace hdbpp_test::data_gen; namespace hdbpp_data_event_test { -DeviceAttribute createDeviceAttribute(const AttributeTrits &traits) +Tango::DeviceAttribute createDeviceAttribute(const AttributeTraits &traits) { - switch (traits.type()) - { - //case Tango::DEV_BOOLEAN: return DeviceAttribute attr(TestAttrFQDName, *generateData<Tango::DEV_BOOLEAN>(traits)); - case Tango::DEV_SHORT: return DeviceAttribute attr(TestAttrFQDName, *generateData<Tango::DEV_SHORT>(traits)); - case Tango::DEV_LONG: return DeviceAttribute attr(TestAttrFQDName, *generateData<Tango::DEV_LONG>(traits)); - case Tango::DEV_LONG64: return DeviceAttribute attr(TestAttrFQDName, *generateData<Tango::DEV_LONG64>(traits)); - case Tango::DEV_FLOAT: return DeviceAttribute attr(TestAttrFQDName, *generateData<Tango::DEV_FLOAT>(traits)); - case Tango::DEV_DOUBLE: return DeviceAttribute attr(TestAttrFQDName, *generateData<Tango::DEV_DOUBLE>(traits)); - case Tango::DEV_UCHAR: return DeviceAttribute attr(TestAttrFQDName, *generateData<Tango::DEV_UCHAR>(traits)); - case Tango::DEV_USHORT: return DeviceAttribute attr(TestAttrFQDName, *generateData<Tango::DEV_USHORT>(traits)); - case Tango::DEV_ULONG: return DeviceAttribute attr(TestAttrFQDName, *generateData<Tango::DEV_ULONG>(traits)); - case Tango::DEV_ULONG64: return DeviceAttribute attr(TestAttrFQDName, *generateData<Tango::DEV_ULONG64>(traits)); - case Tango::DEV_STRING: return DeviceAttribute attr(TestAttrFQDName, *generateData<Tango::DEV_STRING>(traits)); - case Tango::DEV_STATE: - return DeviceAttribute attr(TestAttrFQDName, *generateData<Tango::DEV_STATE>(traits)); - //case Tango::DEV_ENUM: return DeviceAttribute attr(TestAttrFQDName, *generateData<Tango::DEV_BOOLEAN>(traits)); - //case Tango::DEV_ENCODED: return DeviceAttribute attr(TestAttrFQDName, *generateData<Tango::DEV_BOOLEAN>(traits)); - } + // this is all one messy hack as Tango is not easy to use in a unit test, in this case, we can not + // set up and create a DeviceAttribute easy. Instead we use the publicly (!!) available variables + auto attr_gen = [&traits](int size_x, int size_y) { + switch (traits.type()) + { + // TODO enable commented out calls. + case Tango::DEV_BOOLEAN: + return Tango::DeviceAttribute( + TestAttrFQDName.c_str(), *generateSpectrumData<Tango::DEV_BOOLEAN>(false, size_x + size_y)); + + case Tango::DEV_SHORT: + return Tango::DeviceAttribute( + TestAttrFQDName.c_str(), *generateSpectrumData<Tango::DEV_SHORT>(false, size_x + size_y)); + + case Tango::DEV_LONG: + return Tango::DeviceAttribute( + TestAttrFQDName.c_str(), *generateSpectrumData<Tango::DEV_LONG>(false, size_x + size_y)); + + case Tango::DEV_LONG64: + return Tango::DeviceAttribute( + TestAttrFQDName.c_str(), *generateSpectrumData<Tango::DEV_LONG64>(false, size_x + size_y)); + + case Tango::DEV_FLOAT: + return Tango::DeviceAttribute( + TestAttrFQDName.c_str(), *generateSpectrumData<Tango::DEV_FLOAT>(false, size_x + size_y)); + + case Tango::DEV_DOUBLE: + return Tango::DeviceAttribute( + TestAttrFQDName.c_str(), *generateSpectrumData<Tango::DEV_DOUBLE>(false, size_x + size_y)); + + case Tango::DEV_UCHAR: + return Tango::DeviceAttribute( + TestAttrFQDName.c_str(), *generateSpectrumData<Tango::DEV_UCHAR>(false, size_x + size_y)); + + case Tango::DEV_USHORT: + return Tango::DeviceAttribute( + TestAttrFQDName.c_str(), *generateSpectrumData<Tango::DEV_USHORT>(false, size_x + size_y)); + + case Tango::DEV_ULONG: + return Tango::DeviceAttribute( + TestAttrFQDName.c_str(), *generateSpectrumData<Tango::DEV_ULONG>(false, size_x + size_y)); + + case Tango::DEV_ULONG64: + return Tango::DeviceAttribute( + TestAttrFQDName.c_str(), *generateSpectrumData<Tango::DEV_ULONG64>(false, size_x + size_y)); + + case Tango::DEV_STRING: + return Tango::DeviceAttribute( + TestAttrFQDName.c_str(), *generateSpectrumData<Tango::DEV_STRING>(false, size_x + size_y)); + + case Tango::DEV_STATE: + return Tango::DeviceAttribute( + TestAttrFQDName.c_str(), *generateSpectrumData<Tango::DEV_STATE>(false, size_x + size_y)); + + //case Tango::DEV_ENUM: + //return Tango::DeviceAttribute(TestAttrFQDName.c_str(), *generateSpectrumData<Tango::DEV_ENUM>(false, size_x + size_y)); + + //case Tango::DEV_ENCODED: + //return Tango::DeviceAttribute(TestAttrFQDName.c_str(), *generateSpectrumData<Tango::DEV_ENCODED>(false, size_x + size_y)); + } + + throw runtime_error("Control should not reach here"); + return Tango::DeviceAttribute(); + }; + + auto select_size = [](bool has_data, int size) { + if (has_data) + return size; + + return 1; + }; + + // hack hack hack.... this mess of forced public (!) variable setting actually + // sets up the consitions for valid extracts inside the HdbppTxDataEvent class. + // Should the Tango API ever be improved then this hack is doomed. + auto attr = attr_gen(select_size(traits.hasReadData(), 1), select_size(traits.hasWriteData(), 1)); + attr.dim_x = select_size(traits.hasReadData(), 1); + attr.dim_y = select_size(traits.hasWriteData(), 1); + attr.w_dim_x = select_size(traits.hasReadData(), 1); + attr.w_dim_y = select_size(traits.hasWriteData(), 1); + return attr; } // Mock connection to test the HdbppTxDataEvent class, only // implements the functions that storeAttribute use, nothing more -template<typename T> class MockConnection : public ConnectionBase, public HdbppTxFactory<MockConnection> { public: @@ -64,7 +126,7 @@ public: bool isOpen() const noexcept { return _conn_state; } bool isClosed() const noexcept { return !isOpen(); } - template<typename U> + template<typename T> void storeDataEvent(const std::string &full_attr_name, double event_time, int quality, @@ -72,32 +134,30 @@ public: std::unique_ptr<vector<T>> value_w, const AttributeTraits &traits) { - att_name = full_attr_name; - att_event_time = event_time; - att_quality = quality; - att_traits = traits; - att_value_r = *value_r; - attr_value_w = *value_w; - } + if (store_attribute_triggers_ex) + throw runtime_error("A test exception"); - void storeDataEventError( - const std::string &full_attr_name, double event_time, int quality, const std::string &error_msg, const AttributeTraits &traits); - { + // record the data for comparison att_name = full_attr_name; att_event_time = event_time; - att_quality = quality; + // TODO make it Tango::AttrQuality for API + att_quality = (Tango::AttrQuality)quality; att_traits = traits; - att_error_msg = error_msg; + data_size_r = value_r->size(); + data_size_w = value_w->size(); } + // expose the results of the store function so they can be checked + // in the results + + // storeDataEvent/storeDataEventError results string att_name; - double att_event_time; - int att_quality; + double att_event_time = 0; + Tango::AttrQuality att_quality; AttributeTraits att_traits; - string att_error_msg; - - vector<T> attr_value_r; - vector<T> attr_value_w; + int data_size_r = -1; + int data_size_w = -1; + bool store_attribute_triggers_ex = false; private: // connection is always open unless test specifies closed @@ -105,92 +165,171 @@ private: }; }; // namespace hdbpp_data_event_test -SCENARIO("Construct a valid HdbppTxEventData data event for storage", "[hdbpp-tx][hdbpp-txt-data-event]") +SCENARIO("Construct a valid HdbppTxDataEvent data event for storage", "[hdbpp-tx][hdbpp-tx-data-event]") { hdbpp_data_event_test::MockConnection conn; - // seriously ugly, how is this dealt with in Tango!?! + // ugly, how is this dealt with in Tango!?! + struct timeval tv; + struct Tango::TimeVal tango_tv; gettimeofday(&tv, NULL); tango_tv.tv_sec = tv.tv_sec; tango_tv.tv_usec = tv.tv_usec; tango_tv.tv_nsec = 0; - GIVEN("An empty HdbppTxEventData object") + GIVEN("Traits for a device attribute") { - auto attr = createDeviceAttribute(AttributeTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE)); - auto tx = conn.createTx<HdbppTxEventData>(); + auto traits = AttributeTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE); - WHEN("Configuring the HdbppTxEventData object with event chaining") + WHEN("Configuring an HdbppTxDataEvent object as a scalar data event and storing") { - tx.withName(TestAttrFQDName).withTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE); - .withEventTime(tango_tv).withQuality(Tango::ATTR_VALID).withAttribute(&attr); + auto attr = hdbpp_data_event_test::createDeviceAttribute(traits); + auto tx = conn.createTx<HdbppTxDataEvent>(); + + REQUIRE_NOTHROW(tx.withName(TestAttrFQDName) + .withTraits(traits) + .withEventTime(tango_tv) + .withQuality(Tango::ATTR_VALID) + .withAttribute(&attr)); + + REQUIRE_NOTHROW(tx.store()); + + THEN("The data is the same as that passed via method chaining") + { + REQUIRE(conn.att_name == TestAttrFinalName); + REQUIRE(conn.att_event_time == (tango_tv.tv_sec + tango_tv.tv_usec / 1.0e6)); + REQUIRE(conn.att_quality == Tango::ATTR_VALID); + REQUIRE(conn.att_traits == traits); + REQUIRE(conn.data_size_r == 1); + REQUIRE(conn.data_size_w == 0); + } + } + WHEN("Configuring an HdbppTxDataEvent object as a spectrum data event") + { + auto attr = hdbpp_data_event_test::createDeviceAttribute(traits); + auto tx = conn.createTx<HdbppTxDataEvent>(); + + REQUIRE_NOTHROW(tx.withName(TestAttrFQDName) + .withTraits(traits) + .withEventTime(tango_tv) + .withQuality(Tango::ATTR_VALID) + .withAttribute(&attr)); - THEN("Then storing the transaction does not raise an exception") { REQUIRE_NOTHROW(tx.store()); } - AND_WHEN("The result of the store is examined after storing") + REQUIRE_NOTHROW(tx.store()); + + THEN("The data is the same as that passed via method chaining") { - THEN("The data is the same as that passed via method chaining") {} + REQUIRE(conn.att_name == TestAttrFinalName); + REQUIRE(conn.att_event_time == (tango_tv.tv_sec + tango_tv.tv_usec / 1.0e6)); + REQUIRE(conn.att_quality == Tango::ATTR_VALID); + REQUIRE(conn.att_traits == traits); + REQUIRE(conn.data_size_r > 0); + REQUIRE(conn.data_size_w == 0); } } } } -SCENARIO("Construct a valid HdbppTxEventData error event for storage", "[hdbpp-tx][hdbpp-txt-data-event]") +SCENARIO("An invalid quality results in an HdbppTxDataEvent event with no data", "[hdbpp-tx][hdbpp-tx-data-event]") { - string error_msg = "Test error"; hdbpp_data_event_test::MockConnection conn; - // seriously ugly, how is this dealt with in Tango!?! + // ugly, how is this dealt with in Tango!?! + struct timeval tv; + struct Tango::TimeVal tango_tv; gettimeofday(&tv, NULL); tango_tv.tv_sec = tv.tv_sec; tango_tv.tv_usec = tv.tv_usec; tango_tv.tv_nsec = 0; - GIVEN("An empty HdbppTxEventData object") + GIVEN("An HdbppTxDataEvent object with no data set") { - auto attr = createDeviceAttribute(AttributeTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE)); - auto tx = conn.createTx<HdbppTxEventData>(); + auto traits = AttributeTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE); + auto attr = hdbpp_data_event_test::createDeviceAttribute(traits); + auto tx = conn.createTx<HdbppTxDataEvent>(); - WHEN("Configuring the HdbppTxEventData object with event chaining") + WHEN("Configuring an HdbppTxDataEvent object with an invalid quality") { - tx.withName(TestAttrFQDName).withTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE); - .withEventTime(tango_tv).withQuality(Tango::ATTR_VALID).withError(error_msg); + REQUIRE_NOTHROW(tx.withName(TestAttrFQDName) + .withTraits(traits) + .withEventTime(tango_tv) + .withQuality(Tango::ATTR_INVALID) + .withAttribute(&attr)); + + REQUIRE_NOTHROW(tx.store()); - THEN("Then storing the transaction does not raise an exception") { REQUIRE_NOTHROW(tx.store()); } - AND_WHEN("The result of the store is examined after storing") + THEN("The data is the same as that passed via method chaining") { - THEN("The data is the same as that passed via method chaining") - { - REQUIRE(conn.att_name == TestAttrFinalName); - REQUIRE(conn.att_event_time == (tango_tv.tv_sec + tango_tv.tv_usec / 1.0e6)); - REQUIRE(conn.att_quality == Tango::ATTR_VALID); - REQUIRE(conn.att_error_msg == error_msg); - REQUIRE(conn.att_traits == AttributeTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE)); - } + REQUIRE(conn.att_name == TestAttrFinalName); + REQUIRE(conn.att_event_time == (tango_tv.tv_sec + tango_tv.tv_usec / 1.0e6)); + REQUIRE(conn.att_quality == Tango::ATTR_INVALID); + REQUIRE(conn.att_traits == traits); + REQUIRE(conn.data_size_r == 0); + REQUIRE(conn.data_size_w == 0); } } } } -SCENARIO("An invalid quality results in an HdbppTxEventData event with no data", "[hdbpp-tx][hdbpp-txt-data-event]") +SCENARIO("A DeviceAttribute with no data results in an HdbppTxDataEvent event with no data", + "[hdbpp-tx][hdbpp-tx-data-event]") { hdbpp_data_event_test::MockConnection conn; - GIVEN("An AttributeName object with valid name") + // ugly, how is this dealt with in Tango!?! + struct timeval tv; + struct Tango::TimeVal tango_tv; + gettimeofday(&tv, NULL); + tango_tv.tv_sec = tv.tv_sec; + tango_tv.tv_usec = tv.tv_usec; + tango_tv.tv_nsec = 0; + + GIVEN("An HdbppTxDataEvent object with no data set") { - WHEN("Requesting the name for storage") + Tango::DeviceAttribute attr; + auto traits = AttributeTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE); + auto tx = conn.createTx<HdbppTxDataEvent>(); + + WHEN("Configuring an HdbppTxDataEvent object with an empty DeviceAttribute") { - THEN("The name is valid and as expected") {} + REQUIRE_NOTHROW(tx.withName(TestAttrFQDName) + .withTraits(traits) + .withEventTime(tango_tv) + .withQuality(Tango::ATTR_VALID) + .withAttribute(&attr)); + + REQUIRE_NOTHROW(tx.store()); + + THEN("The data is the same as that passed via method chaining") + { + REQUIRE(conn.att_name == TestAttrFinalName); + REQUIRE(conn.att_event_time == (tango_tv.tv_sec + tango_tv.tv_usec / 1.0e6)); + REQUIRE(conn.att_quality == Tango::ATTR_VALID); + REQUIRE(conn.att_traits == traits); + REQUIRE(conn.data_size_r == 0); + REQUIRE(conn.data_size_w == 0); + } } } } -SCENARIO("When attempting to store invalid HdbppTxEventData states, errors are thrown", "[hdbpp-tx][hdbpp-txt-data-event]") +SCENARIO( + "When attempting to store invalid HdbppTxDataEvent states, errors are thrown", "[hdbpp-tx][hdbpp-tx-data-event]") { hdbpp_data_event_test::MockConnection conn; - GIVEN("An HdbppTxNewAttribute object with no data set") + // ugly, how is this dealt with in Tango!?! + struct timeval tv; + struct Tango::TimeVal tango_tv; + gettimeofday(&tv, NULL); + tango_tv.tv_sec = tv.tv_sec; + tango_tv.tv_usec = tv.tv_usec; + tango_tv.tv_nsec = 0; + + GIVEN("An HdbppTxDataEvent object with no data set") { - auto tx = conn.createTx<HdbppTxNewAttribute>(); + auto traits = AttributeTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE); + auto tx = conn.createTx<HdbppTxDataEvent>(); WHEN("Attempting to store without setting data") { @@ -202,18 +341,48 @@ SCENARIO("When attempting to store invalid HdbppTxEventData states, errors are t } WHEN("Attempting to store with just name set") { + tx.withName(TestAttrFQDName); + THEN("An exception is raised and result is false") { - REQUIRE_THROWS(tx.withName(TestAttrFQDName).store()); + REQUIRE_THROWS(tx.store()); REQUIRE(!tx.result()); } + AND_WHEN("Setting the AtributeTraits and trying again") + { + tx.withTraits(traits); + + THEN("An exception is raised and result is false") + { + REQUIRE_THROWS(tx.store()); + REQUIRE(!tx.result()); + } + AND_WHEN("Setting the DeviceAttribute and trying again") + { + auto attr = hdbpp_data_event_test::createDeviceAttribute(traits); + tx.withAttribute(&attr); + + THEN("No exception is raised") + { + REQUIRE_NOTHROW(tx.store()); + REQUIRE(tx.result()); + } + } + } } WHEN("Attempting to store with valid data, but disconnected connection") { conn.disconnect(); REQUIRE(conn.isClosed()); - REQUIRE_NOTHROW(tx.withName(TestAttrFQDName).withTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE)); + auto attr = hdbpp_data_event_test::createDeviceAttribute(traits); + auto tx = conn.createTx<HdbppTxDataEvent>(); + + REQUIRE_NOTHROW(tx.withName(TestAttrFQDName) + .withTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE) + .withEventTime(tango_tv) + .withQuality(Tango::ATTR_VALID) + .withAttribute(&attr)); THEN("An exception is raised and result is false") { @@ -224,26 +393,98 @@ SCENARIO("When attempting to store invalid HdbppTxEventData states, errors are t } } -SCENARIO("Can store every tango data type", "[hdbpp-tx][hdbpp-txt-data-event]") +TEST_CASE("Creating HdbppTxDataEvents for each tango type and storing them", "[db-access][hdbpp-tx-data-event]") { hdbpp_data_event_test::MockConnection conn; - GIVEN("An AttributeName object with valid name") + // ugly, how is this dealt with in Tango!?! + struct timeval tv; + struct Tango::TimeVal tango_tv; + gettimeofday(&tv, NULL); + tango_tv.tv_sec = tv.tv_sec; + tango_tv.tv_usec = tv.tv_usec; + tango_tv.tv_nsec = 0; + + // TODO remaining types + vector<unsigned int> types { + //Tango::DEV_BOOLEAN#, + Tango::DEV_DOUBLE, + Tango::DEV_FLOAT, + Tango::DEV_STRING, + Tango::DEV_LONG, + Tango::DEV_ULONG, + Tango::DEV_LONG64, + Tango::DEV_ULONG64, + Tango::DEV_SHORT, + Tango::DEV_USHORT, + Tango::DEV_UCHAR, + Tango::DEV_STATE, + /* Tango::DEV_ENCODED, + Tango::DEV_ENUM */}; + + vector<Tango::AttrWriteType> write_types {Tango::READ, Tango::WRITE, Tango::READ_WRITE, Tango::READ_WITH_WRITE}; + vector<Tango::AttrDataFormat> format_types {Tango::SCALAR, Tango::SPECTRUM}; + + // loop for every combination of type in Tango + for (auto &type : types) { - WHEN("Requesting the name for storage") + for (auto &format : format_types) { - THEN("The name is valid and as expected") {} + for (auto &write : write_types) + { + AttributeTraits traits {write, format, type}; + + DYNAMIC_SECTION("Storing with traits: " << traits) + { + auto attr = hdbpp_data_event_test::createDeviceAttribute(traits); + auto tx = conn.createTx<HdbppTxDataEvent>(); + + REQUIRE_NOTHROW(tx.withName(TestAttrFQDName) + .withTraits(traits) + .withEventTime(tango_tv) + .withQuality(Tango::ATTR_VALID) + .withAttribute(&attr) + .store()); + + REQUIRE(conn.att_name == TestAttrFinalName); + REQUIRE(conn.att_event_time == (tango_tv.tv_sec + tango_tv.tv_usec / 1.0e6)); + REQUIRE(conn.att_quality == Tango::ATTR_VALID); + REQUIRE(conn.att_traits == traits); + + if (traits.hasReadData()) + REQUIRE(conn.data_size_r > 0); + + if (traits.hasWriteData()) + REQUIRE(conn.data_size_w > 0); + } + } } } } -SCENARIO("HdbppTxHistoryEvent Simulated exception received", "[hdbpp-tx][hdbpp-txt-data-event]") +SCENARIO("HdbppTxDataEvent Simulated exception received", "[hdbpp-tx][hdbpp-tx-data-event]") { hdbpp_data_event_test::MockConnection conn; - GIVEN("An HdbppTxHistoryEvent object with name and traits set") + // ugly, how is this dealt with in Tango!?! + struct timeval tv; + struct Tango::TimeVal tango_tv; + gettimeofday(&tv, NULL); + tango_tv.tv_sec = tv.tv_sec; + tango_tv.tv_usec = tv.tv_usec; + tango_tv.tv_nsec = 0; + + GIVEN("An HdbppTxHisHdbppTxDatatoryEventEvent object with name and traits set") { - auto tx = conn.createTx<HdbppTxHistoryEvent>().withName(TestAttrFQDName).withEvent(events::StartEvent); + auto traits = AttributeTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE); + auto attr = hdbpp_data_event_test::createDeviceAttribute(traits); + auto tx = conn.createTx<HdbppTxDataEvent>(); + + REQUIRE_NOTHROW(tx.withName(TestAttrFQDName) + .withTraits(traits) + .withEventTime(tango_tv) + .withQuality(Tango::ATTR_VALID) + .withAttribute(&attr)); WHEN("Storing the transaction with a triggered exception set") { diff --git a/test/HdbppTxHistoryEventTests.cpp b/test/HdbppTxHistoryEventTests.cpp old mode 100755 new mode 100644 index 76bcae50f5a999fbec7bc39687cfa835564b748d..2c627f46fb1ff60c65286c3349d7663a871a1c46 --- a/test/HdbppTxHistoryEventTests.cpp +++ b/test/HdbppTxHistoryEventTests.cpp @@ -103,7 +103,8 @@ SCENARIO("Construct and store HdbppTxHistoryEvent data without error", "[hdbpp-t } } -SCENARIO("When attempting to store invalid HdbppTxHistoryEvent states, errors are thrown", "[hdbpp-tx][hdbpp-tx-history-event]") +SCENARIO("When attempting to store invalid HdbppTxHistoryEvent states, errors are thrown", + "[hdbpp-tx][hdbpp-tx-history-event]") { hdbpp_hist_event_test::MockConnection conn; @@ -158,7 +159,8 @@ SCENARIO("When attempting to store invalid HdbppTxHistoryEvent states, errors ar } } -SCENARIO("HdbppTxHistoryEvent's overloaded functions return identical results without error", "[hdbpp-tx][hdbpp-tx-history-event]") +SCENARIO("HdbppTxHistoryEvent's overloaded functions return identical results without error", + "[hdbpp-tx][hdbpp-tx-history-event]") { hdbpp_hist_event_test::MockConnection conn; diff --git a/test/HdbppTxNewAttributeTests.cpp b/test/HdbppTxNewAttributeTests.cpp index baf80bc0321029c1ad36cd00acc64843f15daef9..1a4f78668a2632fff3229d590c6aa37fa2d1d48b 100644 --- a/test/HdbppTxNewAttributeTests.cpp +++ b/test/HdbppTxNewAttributeTests.cpp @@ -72,7 +72,6 @@ public: string new_att_member; string new_att_name; AttributeTraits att_traits; - bool store_attribute_triggers_ex = false; private: @@ -117,7 +116,8 @@ SCENARIO("Construct and store HdbppTxNewAttribute data without error", "[hdbpp-t } } -SCENARIO("When attempting to store invalid HdbppTxNewAttribute states, errors are thrown", "[hdbpp-tx][hdbpp-tx-new-attribute]") +SCENARIO("When attempting to store invalid HdbppTxNewAttribute states, errors are thrown", + "[hdbpp-tx][hdbpp-tx-new-attribute]") { hdbpp_new_attr_test::MockConnection conn; @@ -163,7 +163,9 @@ SCENARIO("HdbppTxNewAttribute Simulated exception received", "[hdbpp-tx][hdbpp-t GIVEN("An HdbppTxNewAttribute object with name and traits set") { - auto tx = conn.createTx<HdbppTxNewAttribute>().withName(TestAttrFQDName).withTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE); + auto tx = conn.createTx<HdbppTxNewAttribute>() + .withName(TestAttrFQDName) + .withTraits(Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE); WHEN("Storing the transaction with a triggered exception set") { diff --git a/test/HdbppTxParameterEventTests.cpp b/test/HdbppTxParameterEventTests.cpp index c9f12f6f554e4633600aec0763dbf80feac26116..f8819423a71fc74ba25f328a99e83eb3268a3036 100644 --- a/test/HdbppTxParameterEventTests.cpp +++ b/test/HdbppTxParameterEventTests.cpp @@ -122,7 +122,7 @@ SCENARIO("Construct and store HdbppTxParameterEvent data without error", "[hdbpp struct timeval tv; struct Tango::TimeVal tango_tv; - // seriously ugly, how is this dealt with in Tango!?! + // ugly, how is this dealt with in Tango!?! gettimeofday(&tv, NULL); tango_tv.tv_sec = tv.tv_sec; tango_tv.tv_usec = tv.tv_usec; @@ -134,7 +134,9 @@ SCENARIO("Construct and store HdbppTxParameterEvent data without error", "[hdbpp WHEN("Passing a valid configuration with method chaining") { - tx.withName(TestAttrFQDName).withAttrInfo(hdbpp_param_test::createAttributeInfoEx()).withEventTime(tango_tv); + tx.withName(TestAttrFQDName) + .withAttrInfo(hdbpp_param_test::createAttributeInfoEx()) + .withEventTime(tango_tv); THEN("No exception is raised when storing the transaction") { REQUIRE_NOTHROW(tx.store()); } AND_WHEN("The result of the store is examined after storing") @@ -161,14 +163,15 @@ SCENARIO("Construct and store HdbppTxParameterEvent data without error", "[hdbpp } } -SCENARIO("When attempting to store invalid HdbppTxParameterEvent states, errors are thrown", "[hdbpp-tx][hdbpp-tx-parameter-event]") +SCENARIO("When attempting to store invalid HdbppTxParameterEvent states, errors are thrown", + "[hdbpp-tx][hdbpp-tx-parameter-event]") { hdbpp_param_test::MockConnection conn; struct timeval tv; struct Tango::TimeVal tango_tv; - // seriously ugly, how is this dealt with in Tango!?! + // ugly, how is this dealt with in Tango!?! gettimeofday(&tv, NULL); tango_tv.tv_sec = tv.tv_sec; tango_tv.tv_usec = tv.tv_usec; @@ -221,7 +224,9 @@ SCENARIO("When attempting to store invalid HdbppTxParameterEvent states, errors conn.disconnect(); REQUIRE(conn.isClosed()); - tx.withName(TestAttrFQDName).withEventTime(tango_tv).withAttrInfo(hdbpp_param_test::createAttributeInfoEx()); + tx.withName(TestAttrFQDName) + .withEventTime(tango_tv) + .withAttrInfo(hdbpp_param_test::createAttributeInfoEx()); THEN("An exception is raised and result is false") { @@ -239,7 +244,7 @@ SCENARIO("HdbppTxParameterEvent Simulated exception received", "[hdbpp-tx][hdbpp struct timeval tv; struct Tango::TimeVal tango_tv; - // seriously ugly, how is this dealt with in Tango!?! + // ugly, how is this dealt with in Tango!?! gettimeofday(&tv, NULL); tango_tv.tv_sec = tv.tv_sec; tango_tv.tv_usec = tv.tv_usec; diff --git a/test/QueryBuilderTests.cpp b/test/QueryBuilderTests.cpp index 23d2494660eb1998a361826fb8cc5680893e086c..21befb7fc54679b4e185c8149005e0bca01de8f9 100644 --- a/test/QueryBuilderTests.cpp +++ b/test/QueryBuilderTests.cpp @@ -26,96 +26,72 @@ using namespace hdbpp; using namespace hdbpp::pqxx_conn; using namespace Catch::Matchers; -SCENARIO("Creating valid database table names for SCALAR", "[query-string]") +SCENARIO("storeDataEventQuery() returns the correct Value fields for the given traits", "[query-string]") { - QueryBuilder query_builder; - - GIVEN("An Attribute traits configured for scalar") + GIVEN("A query builder object with nothing cached") { - AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE}; + QueryBuilder query_builder; - WHEN("Requesting a table name for the traits") + WHEN("Requesting a query string for traits configured for Tango::READ") { - auto result = query_builder.tableName(traits); - - THEN("The result must include the TYPE_SCALAR from the schema") { REQUIRE_THAT(result, Contains(TYPE_SCALAR)); } + AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE}; + auto result = query_builder.storeDataEventQuery<double>(traits); + + THEN("The result must include the DAT_COL_VALUE_R field only") + { + REQUIRE_THAT(result, Contains(DAT_COL_VALUE_R)); + REQUIRE_THAT(result, !Contains(DAT_COL_VALUE_W)); + REQUIRE_THAT(result, Contains("$4")); + REQUIRE_THAT(result, !Contains("$5")); + } } - } -} - -SCENARIO("Creating valid database table names for SPECTRUM", "[query-string]") -{ - QueryBuilder query_builder; - - GIVEN("An Attribute traits configured for spectrum") - { - AttributeTraits traits {Tango::READ, Tango::SPECTRUM, Tango::DEV_DOUBLE}; - - WHEN("Requesting a table name for the traits") + WHEN("Requesting a query string for traits configured for Tango::WRITE") { - auto result = query_builder.tableName(traits); - - THEN("The result must include the TYPE_ARRAY from the schema") { REQUIRE_THAT(result, Contains(TYPE_ARRAY)); } + AttributeTraits traits {Tango::WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; + auto result = query_builder.storeDataEventQuery<double>(traits); + + THEN("The result must include the DAT_COL_VALUE_W field only") + { + REQUIRE_THAT(result, !Contains(DAT_COL_VALUE_R)); + REQUIRE_THAT(result, Contains(DAT_COL_VALUE_W)); + REQUIRE_THAT(result, Contains("$4")); + REQUIRE_THAT(result, !Contains("$5")); + } } - } -} - -SCENARIO("Creating valid database table names for IMAGE", "[query-string]") -{ - QueryBuilder query_builder; - - GIVEN("An Attribute traits configured for image") - { - AttributeTraits traits {Tango::READ, Tango::IMAGE, Tango::DEV_DOUBLE}; - - WHEN("Requesting a table name for the traits") + WHEN("Requesting a query string for traits configured for Tango::READ_WRITE") { - auto result = query_builder.tableName(traits); - - THEN("The result must include the TYPE_IMAGE from the schema") { REQUIRE_THAT(result, Contains(TYPE_IMAGE)); } + AttributeTraits traits {Tango::READ_WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; + auto result = query_builder.storeDataEventQuery<double>(traits); + + THEN("The result must include both the DAT_COL_VALUE_R and DAT_COL_VALUE_W field") + { + REQUIRE_THAT(result, Contains(DAT_COL_VALUE_R)); + REQUIRE_THAT(result, Contains(DAT_COL_VALUE_W)); + REQUIRE_THAT(result, Contains("$4")); + REQUIRE_THAT(result, Contains("$5")); + } } - } -} - -SCENARIO("Creating valid database table names for DEV_BOOLEAN", "[query-string]") -{ - QueryBuilder query_builder; - - GIVEN("An Attribute traits configured for boolean") - { - AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_BOOLEAN}; - - WHEN("Requesting a table name for the traits") + WHEN("Requesting a query string for traits configured for Tango::READ_WITH_WRITE") { - auto result = query_builder.tableName(traits); - - THEN("The result must include the TYPE_DEV_BOOLEAN from the schema") { REQUIRE_THAT(result, Contains(TYPE_DEV_BOOLEAN)); } + AttributeTraits traits {Tango::READ_WITH_WRITE, Tango::SCALAR, Tango::DEV_DOUBLE}; + auto result = query_builder.storeDataEventQuery<double>(traits); + + THEN("The result must include both the DAT_COL_VALUE_R and DAT_COL_VALUE_W field") + { + REQUIRE_THAT(result, Contains(DAT_COL_VALUE_R)); + REQUIRE_THAT(result, Contains(DAT_COL_VALUE_W)); + REQUIRE_THAT(result, Contains("$4")); + REQUIRE_THAT(result, Contains("$5")); + } } } } -SCENARIO("Creating valid database table names for DEV_UCHAR", "[query-string]") +SCENARIO("Creating valid insert queries with storeDataEventErrorQuery()", "[query-string]") { QueryBuilder query_builder; - GIVEN("An Attribute traits configured for unsigned char") - { - AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_UCHAR}; - - WHEN("Requesting a table name for the traits") - { - auto result = query_builder.tableName(traits); - - THEN("The result must include the TYPE_DEV_UCHAR from the schema") { REQUIRE_THAT(result, Contains(TYPE_DEV_UCHAR)); } - } - } -} - -SCENARIO("Creating valid database table names for DEV_DOUBLE", "[query-string]") -{ - QueryBuilder query_builder; - - GIVEN("An Attribute traits configured for unsigned char") + GIVEN("An Attribute traits configured for scalar") { AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_DOUBLE}; @@ -123,75 +99,68 @@ SCENARIO("Creating valid database table names for DEV_DOUBLE", "[query-string]") { auto result = query_builder.tableName(traits); - THEN("The result must include the TYPE_DEV_DOUBLE from the schema") { REQUIRE_THAT(result, Contains(TYPE_DEV_DOUBLE)); } + THEN("The result must include the TYPE_SCALAR from the schema") + { + REQUIRE_THAT(result, Contains(TYPE_SCALAR)); + } } } } -SCENARIO("Creating valid database table names for DEV_FLOAT", "[query-string]") +TEST_CASE("Creating valid database table names for types", "[query-string]") { - QueryBuilder query_builder; - - GIVEN("An Attribute traits configured for unsigned char") + vector<unsigned int> types {Tango::DEV_DOUBLE, + Tango::DEV_FLOAT, + Tango::DEV_STRING, + Tango::DEV_LONG, + Tango::DEV_ULONG, + Tango::DEV_LONG64, + Tango::DEV_ULONG64, + Tango::DEV_SHORT, + Tango::DEV_USHORT, + Tango::DEV_BOOLEAN, + Tango::DEV_UCHAR, + Tango::DEV_STATE, + Tango::DEV_ENCODED, + Tango::DEV_ENUM}; + + vector<Tango::AttrWriteType> write_types {Tango::READ, Tango::WRITE, Tango::READ_WRITE, Tango::READ_WITH_WRITE}; + vector<Tango::AttrDataFormat> format_types {Tango::SCALAR, Tango::SPECTRUM, Tango::IMAGE}; + + vector<string> types_str {TYPE_DEV_DOUBLE, + TYPE_DEV_FLOAT, + TYPE_DEV_STRING, + TYPE_DEV_LONG, + TYPE_DEV_ULONG, + TYPE_DEV_LONG64, + TYPE_DEV_ULONG64, + TYPE_DEV_SHORT, + TYPE_DEV_USHORT, + TYPE_DEV_BOOLEAN, + TYPE_DEV_UCHAR, + TYPE_DEV_STATE, + TYPE_DEV_ENCODED, + TYPE_DEV_ENUM}; + + vector<string> format_types_str {TYPE_SCALAR, TYPE_ARRAY, TYPE_IMAGE}; + + // loop for every combination of type in Tango + for (unsigned int t = 0; t < types.size(); ++t) { - AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_FLOAT}; - - WHEN("Requesting a table name for the traits") + for (unsigned int f = 0; f < format_types.size(); ++f) { - auto result = query_builder.tableName(traits); - - THEN("The result must include the TYPE_DEV_FLOAT from the schema") { REQUIRE_THAT(result, Contains(TYPE_DEV_FLOAT)); } - } - } -} - -SCENARIO("Creating valid database table names for DEV_STRING", "[query-string]") -{ - QueryBuilder query_builder; - - GIVEN("An Attribute traits configured for unsigned char") - { - AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_STRING}; - - WHEN("Requesting a table name for the traits") - { - auto result = query_builder.tableName(traits); - - THEN("The result must include the TYPE_DEV_STRING from the schema") { REQUIRE_THAT(result, Contains(TYPE_DEV_STRING)); } - } - } -} - -SCENARIO("Creating valid database table names for DEV_LONG", "[query-string]") -{ - QueryBuilder query_builder; - - GIVEN("An Attribute traits configured for unsigned char") - { - AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_LONG}; - - WHEN("Requesting a table name for the traits") - { - auto result = query_builder.tableName(traits); - - THEN("The result must include the TYPE_DEV_LONG from the schema") { REQUIRE_THAT(result, Contains(TYPE_DEV_LONG)); } - } - } -} - -SCENARIO("Creating valid database table names for DEV_ULONG", "[query-string]") -{ - QueryBuilder query_builder; - - GIVEN("An Attribute traits configured for unsigned char") - { - AttributeTraits traits {Tango::READ, Tango::SCALAR, Tango::DEV_ULONG}; - - WHEN("Requesting a table name for the traits") - { - auto result = query_builder.tableName(traits); - - THEN("The result must include the TYPE_DEV_ULONG from the schema") { REQUIRE_THAT(result, Contains(TYPE_DEV_ULONG)); } + for (unsigned int w = 0; w < write_types.size(); ++w) + { + QueryBuilder query_builder; + AttributeTraits traits {write_types[w], format_types[f], types[t]}; + + DYNAMIC_SECTION("Testing table name for traits: " << traits) + { + auto result = query_builder.tableName(traits); + REQUIRE_THAT(result, Contains(types_str[t])); + REQUIRE_THAT(result, Contains(format_types_str[f])); + } + } } } } \ No newline at end of file diff --git a/test/TestHelpers.hpp b/test/TestHelpers.hpp index f1be72358ee5c4fe154a487e0da22d65012222c3..b4ad8e35bb0729a55798fb428a7558c5bb998ccb 100644 --- a/test/TestHelpers.hpp +++ b/test/TestHelpers.hpp @@ -45,7 +45,8 @@ namespace attr_name { // mock test data const std::string TestAttrFQDName = "tango://localhost.server.com:10000/test-domain/test-family/test-member/test"; - const std::string TestAttrFQDNameNoTangoQual = "localhost.server.com:10000/test-domain/test-family/test-member/test"; + const std::string TestAttrFQDNameNoTangoQual = + "localhost.server.com:10000/test-domain/test-family/test-member/test"; const std::string TestAttrFQDNameNoDomain = "tango://localhost:10000/test-domain/test-family/test-member/test"; const std::string TestAttrCs = "new_cs"; diff --git a/test/main.cpp b/test/main.cpp index d8d04122e74129644740bb9f5899a9ddd6a2e357..2a7e0b16f87e03f7caeebbcacaa094c1999e92fe 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -26,7 +26,7 @@ int main(int argc, char *argv[]) { // console only warning level logging hdbpp::LogConfigurator::initLogging(false, true); - hdbpp::LogConfigurator::setLoggingLevel(spdlog::level::warn); + hdbpp::LogConfigurator::setLoggingLevel(spdlog::level::trace); int result = Catch::Session().run(argc, argv);