From 319b33eeb16a04086f54cba09d2f8a9540adeb23 Mon Sep 17 00:00:00 2001 From: Lomtev Dmytro Date: Thu, 10 Aug 2017 10:59:32 +0300 Subject: [PATCH] Added API to nmlib for applications uninstallation. MqHandler refactored. Tests added. --- device_core/CMakeLists.txt | 5 +- device_core/ctrl_app_lib/inc/iotchilddevice_impl.h | 2 + device_core/ctrl_app_lib/inc/mq_topic.h | 74 ++++++ device_core/ctrl_app_lib/inc/nmlib.h | 9 + device_core/ctrl_app_lib/src/ctrl_app_support.cpp | 26 ++- .../ctrl_app_lib/src/iotchilddevice_impl.cpp | 5 + device_core/ctrl_app_lib/src/securitycontext.cpp | 12 +- device_core/iotivity_lib/inc/device_commands.h | 11 + device_core/iotivity_lib/inc/iotdevice.h | 6 + device_core/iotivity_lib/inc/iotdevice_impl.h | 9 +- device_core/iotivity_lib/inc/mqhandler.h | 25 +- device_core/iotivity_lib/inc/resource_callbacks.h | 3 +- device_core/iotivity_lib/src/iotdevice_impl.cpp | 79 +++---- device_core/iotivity_lib/src/iotivity.cpp | 1 + device_core/iotivity_lib/src/mq_topic.cpp | 55 +++++ device_core/iotivity_lib/src/mqhandler.cpp | 223 ++++++++++-------- .../iotivity_lib/src/resource_callbacks.cpp | 6 +- device_core/nmdaemon/application_service.cpp | 5 + device_core/nmdaemon/commandhandler.cpp | 94 ++++++++ device_core/nmdaemon/commandhandler.h | 58 +++++ device_core/nmdaemon/control_resource.cpp | 19 +- device_core/nmdaemon/control_resource.h | 13 +- device_core/nmdaemon/icommandhandler.h | 29 +++ device_core/nmdaemon/main_thread.cpp | 11 +- device_core/nmdaemon/notification_handler_mq.cpp | 4 +- device_core/nmdaemon/policyhandlermq.cpp | 22 +- device_core/secserver/secserver.cpp | 2 - device_core/utest/CMakeLists.txt | 2 + device_core/utest/local_test_resources_init.cpp | 22 +- device_core/utest/test_IoT.cpp | 100 +------- device_core/utest/test_all.cpp | 32 ++- device_core/utest/test_commandhandler.cpp | 255 +++++++++++++++++++++ device_core/utest/test_controlresource.cpp | 24 +- device_core/utest/test_iot_dev_manager.cpp | 6 +- device_core/utest/test_iot_notification.cpp | 3 +- device_core/utest/test_iotdevice_impl.cpp | 97 ++++++++ device_core/utest/test_jsonutils.cpp | 2 +- device_core/utest/test_mq.cpp | 51 +++-- device_core/utest/test_mq_topic.cpp | 72 ++++++ device_core/utest/test_rest.cpp | 59 +---- 40 files changed, 1130 insertions(+), 403 deletions(-) create mode 100644 device_core/ctrl_app_lib/inc/mq_topic.h create mode 100644 device_core/iotivity_lib/inc/device_commands.h create mode 100644 device_core/iotivity_lib/src/mq_topic.cpp create mode 100644 device_core/nmdaemon/commandhandler.cpp create mode 100644 device_core/nmdaemon/commandhandler.h create mode 100644 device_core/nmdaemon/icommandhandler.h create mode 100644 device_core/utest/test_commandhandler.cpp create mode 100644 device_core/utest/test_iotdevice_impl.cpp create mode 100644 device_core/utest/test_mq_topic.cpp diff --git a/device_core/CMakeLists.txt b/device_core/CMakeLists.txt index 31dc61c..242407a 100644 --- a/device_core/CMakeLists.txt +++ b/device_core/CMakeLists.txt @@ -37,7 +37,8 @@ IF("${FLAVOR}" STREQUAL "UBUNTU") SET (LIBDIR "${INSTALL_DIR}/usr/lib/") SET (TESTS_DIR "${INSTALL_DIR}/usr/apps/network-manager") SET (MANIFESTDIR "${INSTALL_DIR}/usr/share/packages") - SET (GTEST_LIB gtest gtest_main) + #SET (GTEST_LIB gtest gtest_main) + SET (GTEST_LIB gmock gmock_main) if (NOT DEFINED ENV{IOTIVITY_HOME}) set(ENV{IOTIVITY_HOME} "$ENV{HOME}/iotivity/") @@ -149,4 +150,4 @@ else() COMMAND genhtml -o ${COV_FOLDER} ${COV_FOLDER}/utest_filtered.info COMMAND echo "Coverage calculated." COMMENT "Coverage stat with LCOV.\n") -endif() \ No newline at end of file +endif() diff --git a/device_core/ctrl_app_lib/inc/iotchilddevice_impl.h b/device_core/ctrl_app_lib/inc/iotchilddevice_impl.h index 7c91128..9f3d959 100644 --- a/device_core/ctrl_app_lib/inc/iotchilddevice_impl.h +++ b/device_core/ctrl_app_lib/inc/iotchilddevice_impl.h @@ -74,6 +74,8 @@ public: void unOwnDevice() override; + void deleteApp(const std::string& app_name) override; + void setState(bool is_online) override { online = is_online; diff --git a/device_core/ctrl_app_lib/inc/mq_topic.h b/device_core/ctrl_app_lib/inc/mq_topic.h new file mode 100644 index 0000000..4b724cc --- /dev/null +++ b/device_core/ctrl_app_lib/inc/mq_topic.h @@ -0,0 +1,74 @@ +/** + * @brief Class used to simplify work with topic names + * @date Created 10.08.2017 + * @author Created 2017 in Samsung Ukraine R&D Center (SURC) under a contract + * between LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) + * and "Samsung Electronics Co", Ltd (Seoul, Republic of Korea). + * Copyright: (c) Samsung Electronics Co, Ltd 2017. All rights reserved. + * @author Mail to: Dmytro Lomtev, d.lomtev@samsung.com + */ +#ifndef MQ_TOPIC_H +#define MQ_TOPIC_H + +#include +#include + +namespace NetworkManager +{ + +class MqTopic +{ +public: + typedef std::vector Parts; + + /** + * @brief Base topic name used by iotivity MQ + */ + static const std::string DEFAULT_MQ_BROKER_URI_ROOT; + + /** + * @brief Construct from std::string (Ex.: /subtopic/topic), base topic will be added at the beginning of the name + * if not present + * @param name [in] topic name + */ + MqTopic(const std::string& name); + + /** + * @brief Construct from vector (Ex.: {"/subtopic", "/topic"}), base topic will be added at the beginning of the + * name if not present + * @param first [in] iterator pointing to first part of topic name + * @param last [in] end iterator - pointing to position after the last part of topic name + */ + MqTopic(Parts::const_iterator first, Parts::const_iterator last); + + /** + * @brief Returns full topic name + * @return topic name as string + */ + const std::string& getName() const + { + return topic; + } + + /** + * @brief Returns rest of the topic name without base topic part + * @return topic name as string + */ + std::string getSubName() const + { + return topic.substr(DEFAULT_MQ_BROKER_URI_ROOT.length()); + } + + /** + * @brief Returns topic name splitted to parts first of which is the base topic and others are topic name parts + * prepended with "\" + * @return vector containing the topic name parts + */ + Parts split() const; +private: + std::string topic; +}; + +} // namespace NetworkManager + +#endif // MQ_TOPIC_H diff --git a/device_core/ctrl_app_lib/inc/nmlib.h b/device_core/ctrl_app_lib/inc/nmlib.h index 2fabfa5..9c917b7 100644 --- a/device_core/ctrl_app_lib/inc/nmlib.h +++ b/device_core/ctrl_app_lib/inc/nmlib.h @@ -321,6 +321,15 @@ NM_ErrorCode NM_setDevicePolicy(NM_hContext ctx, const char* dev_id, const char* NM_ErrorCode NM_getDeviceAgents(NM_hContext ctx, const char* dev_id, char** agents); /** + * @brief NM_deleteApp sends application uninstall request to specified device + * @param ctx [in] handle to context + * @param dev_id [in] device id + * @param app_name [in] application name to delete + * @return error code + */ +NM_ErrorCode NM_deleteApp(NM_hContext ctx, const char* dev_id, const char* app_name); + +/** * @brief subscribeNotifications subscribes to secure server notifications. * Triggered by secure server to notify user. * @param ctx context handle for internal data storage diff --git a/device_core/ctrl_app_lib/src/ctrl_app_support.cpp b/device_core/ctrl_app_lib/src/ctrl_app_support.cpp index 97ecab3..54e5bcc 100644 --- a/device_core/ctrl_app_lib/src/ctrl_app_support.cpp +++ b/device_core/ctrl_app_lib/src/ctrl_app_support.cpp @@ -250,7 +250,7 @@ NM_ErrorCode NM_signIn(NM_hContext ctx, const char* login, const char* pass) FN_VISIT if (ctx == nullptr) return EC_NULL_POINTER; try - { //FIXME: remove hardcoded value + { ctx->context->login(getCloudHost(), getDSMURI(), login, pass); } catch (HTTPError& e) @@ -796,6 +796,30 @@ NM_ErrorCode NM_getDeviceAgents(NM_hContext ctx, const char* dev_id, char** agen return EC_OK; } +NM_ErrorCode NM_deleteApp(NM_hContext ctx, const char* dev_id, const char* app_name) +{ + FN_VISIT + if (ctx == nullptr || dev_id == nullptr || app_name) return EC_NULL_POINTER; + + try + { + auto dev = ctx->context->getIoTDevice(dev_id); + dev->deleteApp(app_name); + } + catch(NMexception& e) + { + LOG_E(TAG, "Delete applicatio error: %s, error code %d", e.what(), e.errorCode()); + return static_cast(e.errorCode()); + } + catch (std::exception& e) + { + LOG_E(TAG, "Delete applicatio error: %s", e.what()); + return EC_INTERNAL_ERROR; + } + + return EC_OK; +} + void NM_freeCharBuffer(char* buffer) { if (buffer != nullptr) delete[] buffer; diff --git a/device_core/ctrl_app_lib/src/iotchilddevice_impl.cpp b/device_core/ctrl_app_lib/src/iotchilddevice_impl.cpp index 5b1a42d..77b15e1 100644 --- a/device_core/ctrl_app_lib/src/iotchilddevice_impl.cpp +++ b/device_core/ctrl_app_lib/src/iotchilddevice_impl.cpp @@ -52,4 +52,9 @@ void IoTChildDevice_impl::unOwnDevice() dev->unOwnDevice(uuid); } +void IoTChildDevice_impl::deleteApp(const std::string& app_name) +{ + throw NMexception("IoTChildDevice_impl::deleteApp not implemeted", EC_NOT_IMPLEMENTED_YET); +} + } diff --git a/device_core/ctrl_app_lib/src/securitycontext.cpp b/device_core/ctrl_app_lib/src/securitycontext.cpp index aad3454..b25b6c1 100644 --- a/device_core/ctrl_app_lib/src/securitycontext.cpp +++ b/device_core/ctrl_app_lib/src/securitycontext.cpp @@ -292,19 +292,17 @@ void SecurityContext::subscribeNotifications(NM_NotificationCb callback, void* u }; MqHandler* mq = iotivity->getMqHandler(); - // TODO change UUID here to correct - mq->subscribe("/00000000-0000-0000-0000-000000000000/notification", observCb); + + std::string user_notification_topic = "/" + iotivity->getCloudAuthId() + "/notification"; + mq->subscribe(user_notification_topic, observCb); } void SecurityContext::unsubscribeNotifications() { FN_VISIT; MqHandler* mq = iotivity->getMqHandler(); - if (mq) - { - // TODO change UUID here to correct - mq->unsubscribe("/00000000-0000-0000-0000-000000000000/notification"); - } + std::string user_notification_topic = "/" + iotivity->getCloudAuthId() + "/notification"; + mq->unsubscribe(user_notification_topic); } //void SecurityContext::unsubscribeOwnedPresence() diff --git a/device_core/iotivity_lib/inc/device_commands.h b/device_core/iotivity_lib/inc/device_commands.h new file mode 100644 index 0000000..6f5e56c --- /dev/null +++ b/device_core/iotivity_lib/inc/device_commands.h @@ -0,0 +1,11 @@ +#ifndef DEVICE_COMMANDS_H +#define DEVICE_COMMANDS_H + +enum class DeviceCommands : int +{ + WRONG_COMMAND = 0, + UNOWN = 1, + UNINSTALL = 2, +}; + +#endif // DEVICE_COMMANDS_H diff --git a/device_core/iotivity_lib/inc/iotdevice.h b/device_core/iotivity_lib/inc/iotdevice.h index 15a505d..990dbe7 100644 --- a/device_core/iotivity_lib/inc/iotdevice.h +++ b/device_core/iotivity_lib/inc/iotdevice.h @@ -108,6 +108,12 @@ public: virtual void unOwnDevice() = 0; /** + * @brief deleteApp performes app uninstallation on remote device + * @param app_name [in] application name + */ + virtual void deleteApp(const std::string& app_name) = 0; + + /** * @brief setState sets online/offline state for device * @param state true if device is online */ diff --git a/device_core/iotivity_lib/inc/iotdevice_impl.h b/device_core/iotivity_lib/inc/iotdevice_impl.h index 02e64eb..45260f0 100644 --- a/device_core/iotivity_lib/inc/iotdevice_impl.h +++ b/device_core/iotivity_lib/inc/iotdevice_impl.h @@ -18,8 +18,9 @@ public: const unsigned CALLBACK_WAIT_TIMEOUT_S = 3; IoTDevice_impl(std::shared_ptr device_resource, bool connected = true); - IoTDevice_impl(const std::string& host, const std::string& uid, bool connected = true); + IoTDevice_impl(const std::string& uid, const std::string& device_name, const std::string& device_type, const std::string& device_model); + ~IoTDevice_impl() override; const std::string& getUUID() const override @@ -73,6 +74,8 @@ public: void unOwnDevice() override; + void deleteApp(const std::string& app_name) override; + void setState(bool is_online) override { online = is_online; @@ -96,8 +99,10 @@ private: void ownCloudDevice(); void ownPrimitiveDevice(); void infoFromRepresentation(const OC::OCRepresentation& rep); + OC::OCResource::Ptr getControlResource(); + void sendCommand(OC::OCResource::Ptr ctrl, const OC::OCRepresentation& rep); - std::shared_ptr dev; + OC::OCResource::Ptr dev; std::string name; std::string model; std::string type; diff --git a/device_core/iotivity_lib/inc/mqhandler.h b/device_core/iotivity_lib/inc/mqhandler.h index 6776723..8156d51 100644 --- a/device_core/iotivity_lib/inc/mqhandler.h +++ b/device_core/iotivity_lib/inc/mqhandler.h @@ -14,8 +14,10 @@ #include #include "OCApi.h" #include "OCPlatform.h" +#include "mq_topic.h" - +namespace NetworkManager +{ /** * @brief The MqHandler is class wrapper for MQ functionality of iotivity */ @@ -45,13 +47,6 @@ public: void subscribe(const std::string& subTopic, OC::ObserveCallback subscribeCB); /** - * @brief subscribe to existing topic - * @param subTopic sub topic that shoud be after root topic (Ex. '/srv/policy') - * @param subscribeCB method to that should be called after publish - */ - void subscribeToExistingTopic(const std::string& subTopic, OC::ObserveCallback subscribeCB); - - /** * @brief unsubscribe method to unsubscribe from topic * @param subTopic sub topic that shoud be after root topic (Ex. '/srv/policy') */ @@ -67,7 +62,7 @@ public: * @param subTopic sub topic that shoud be after root topic (Ex. '/srv/policy') * @return resource object that represent topic */ - OC::OCResource::Ptr getTopic(const std::string& subTopic); + OC::OCResource::Ptr getTopic(const MqTopic& subTopic); /** * @brief Discovery Topic from MQ broker @@ -76,10 +71,16 @@ public: * @param subTopic sub topic that shoud be after root topic (Ex. '/srv/policy') * @return resource object that represent topic */ - OC::OCResource::Ptr discoveryTopic(const std::string& subTopic); + OC::OCResource::Ptr discoveryTopic(const MqTopic& subTopic); -private: + /** + * @brief Try to find topic or create it if not found + * @param subTopic sub topic that has to be found or created + * @return resource object shared pointer on success and empty shared pointer if fails + */ + OC::OCResource::Ptr findCreateTopic(const std::string& subTopic); +private: /** * @brief topicCache contains cached topic resources to hold * them after subscribe @@ -91,4 +92,6 @@ private: std::mutex handler_mutex; }; +} // namespace NetworkManager + #endif // MQHANDLER_H diff --git a/device_core/iotivity_lib/inc/resource_callbacks.h b/device_core/iotivity_lib/inc/resource_callbacks.h index 56cd383..f339817 100644 --- a/device_core/iotivity_lib/inc/resource_callbacks.h +++ b/device_core/iotivity_lib/inc/resource_callbacks.h @@ -50,6 +50,7 @@ struct CallbackBase bool wait() { signal.wait_for(lock, DEFAULT_TIMEOUT, [this] { return fired; }); + lock.unlock(); return fired; } @@ -214,7 +215,7 @@ struct MqDiscoveryTopicCallback : public CallbackBase { typedef std::shared_ptr Sptr; std::string topic; - std::shared_ptr resource; + std::vector topics; /** * @brief MqDiscoveryTopicCallback constructor diff --git a/device_core/iotivity_lib/src/iotdevice_impl.cpp b/device_core/iotivity_lib/src/iotdevice_impl.cpp index b99d7d6..94ee7b8 100644 --- a/device_core/iotivity_lib/src/iotdevice_impl.cpp +++ b/device_core/iotivity_lib/src/iotdevice_impl.cpp @@ -9,6 +9,7 @@ #include #include "resource_callbacks.h" #include "iotutils.h" +#include "device_commands.h" #include "EasySetup.hpp" #include "ESRichCommon.h" @@ -131,47 +132,6 @@ IoTDevice_impl::IoTDevice_impl(std::shared_ptr device_resource, bool LOG_D(TAG, "Device found [%s]:[%s] connectivity=0x%08x.", name.c_str(), uuid.c_str(), int(dev->connectivityType())); } -IoTDevice_impl::IoTDevice_impl(const std::string& host, const std::string& uid, bool connected) - : name("unknown"), model("unknown"), type("unknown"), uuid(uid), spec_ver("unknown"), host(host), online(connected), cloud_accessibility(false) -{ - if (!online) return; - - string uri{"/oic/res?di="}; - uri += uuid; - uri += "&rt=oic.wk.d"; - - FindResourceCallback::Sptr type_callback = std::make_shared("oic.wk.d"); - OCPlatform::findResource(host, uri, static_cast(CT_ADAPTER_TCP | CT_IP_USE_V4), bind_callback(type_callback, PH::_1)); - - if (type_callback->wait()) - { - dev = type_callback->resource; - auto rt = dev->getResourceTypes(); - for (auto res_type : rt) - { - if (res_type.compare(0, dev_type_resource.length(), dev_type_resource) == 0) - { - type = res_type.substr(dev_type_resource.length()); - } - } - } - else - { - LOG_D(TAG, "Fail to found device on URI: %s", uri.c_str()); - } - - if (dev) - { - GetResourceCallback::Sptr callback = std::make_shared(); - auto result = dev->get("oic.wk.d", "oic.if.baseline", QueryParamsMap{}, bind_callback(callback, PH::_1, PH::_2, PH::_3)); - - if (OC_STACK_OK == result && callback->wait()) - { - infoFromRepresentation(callback->representation); - } - } -} - IoTDevice_impl::IoTDevice_impl(const std::string& uid, const std::string& device_name, const std::string& device_type, const std::string& device_model) : dev(nullptr), name(device_name), model(device_model), type(device_type), uuid(uid), spec_ver("unknown"), host(""), online(false), cloud_accessibility(false) { @@ -181,19 +141,14 @@ IoTDevice_impl::~IoTDevice_impl() { } -void IoTDevice_impl::unOwnDevice() +OCResource::Ptr IoTDevice_impl::getControlResource() { -// if (!dev) -// { -// throw IoTInternalError("Unowning of offline device requested", EC_ITEM_NOT_FOUND); -// } if(host.empty()) { throw IoTInternalError("host is empty", EC_NOT_INITIALIZED); } auto iotinst = IoTivity::getInstance(); -// OCResource::Ptr ctrl_resource = iotinst->findResource(dev->host(), CTRL_RESOURCE_TYPE); OCResource::Ptr ctrl_resource = iotinst->findResource(host, CTRL_RESOURCE_TYPE); if(!ctrl_resource) @@ -201,9 +156,13 @@ void IoTDevice_impl::unOwnDevice() throw IoTInternalError("Control resource not found", EC_GENERIC_ERROR); } - QueryParamsMap query{{"state", "unown"}}; + return ctrl_resource; +} + +void IoTDevice_impl::sendCommand(OCResource::Ptr ctrl, const OC::OCRepresentation& representation) +{ PostResourceCallback::Sptr callback = std::make_shared(); - auto result = ctrl_resource->post(OCRepresentation{}, query, bind_callback(callback, PH::_1, PH::_2, PH::_3)); + auto result = ctrl->post(representation, QueryParamsMap{}, bind_callback(callback, PH::_1, PH::_2, PH::_3)); if (OC_STACK_OK != result) { @@ -216,6 +175,28 @@ void IoTDevice_impl::unOwnDevice() } } +void IoTDevice_impl::unOwnDevice() +{ + auto ctrl_resource = getControlResource(); + OCRepresentation request; + request.setValue("command", int(DeviceCommands::UNOWN)); + sendCommand(ctrl_resource, request); +} + +void IoTDevice_impl::deleteApp(const std::string& app_name) +{ + if (app_name.empty()) + { + throw NMexception("deleteApp called with empty pid or name", EC_BAD_PARAMETER); + } + + auto ctrl_resource = getControlResource(); + OCRepresentation request; + request.setValue("command", int(DeviceCommands::UNINSTALL)); + request.setValue("name", app_name); + sendCommand(ctrl_resource, request); +} + void IoTDevice_impl::setCloudProperties(const std::string& host, const std::string& provider, const std::string& token) { auto iotinst = IoTivity::getInstance(); diff --git a/device_core/iotivity_lib/src/iotivity.cpp b/device_core/iotivity_lib/src/iotivity.cpp index c967b3c..056e465 100644 --- a/device_core/iotivity_lib/src/iotivity.cpp +++ b/device_core/iotivity_lib/src/iotivity.cpp @@ -134,6 +134,7 @@ MqHandler* IoTivity::getMqHandler() if (handler == nullptr) { LOG_W(TAG, "getMqHandler(): MqHandler uninitialized"); + throw std::runtime_error("Failed to connect to message queue"); } return handler; } diff --git a/device_core/iotivity_lib/src/mq_topic.cpp b/device_core/iotivity_lib/src/mq_topic.cpp new file mode 100644 index 0000000..f97e5a5 --- /dev/null +++ b/device_core/iotivity_lib/src/mq_topic.cpp @@ -0,0 +1,55 @@ +#include + +namespace NetworkManager +{ + +const std::string MqTopic::DEFAULT_MQ_BROKER_URI_ROOT = "/oic/ps"; + +MqTopic::MqTopic(const std::string& name): topic(name) +{ + if (0 != topic.compare(0, DEFAULT_MQ_BROKER_URI_ROOT.length(), DEFAULT_MQ_BROKER_URI_ROOT)) + { + topic = DEFAULT_MQ_BROKER_URI_ROOT + topic; + } +} + +MqTopic::MqTopic(MqTopic::Parts::const_iterator first, MqTopic::Parts::const_iterator last) +{ + if (first == last || *first != DEFAULT_MQ_BROKER_URI_ROOT) + { + topic.append(DEFAULT_MQ_BROKER_URI_ROOT); + } + + while(first != last) + { + topic.append(*first); + ++first; + } +} + +MqTopic::Parts MqTopic::split() const +{ + Parts result; + + // split root parent + auto start = topic.cbegin(); + auto stop = start + DEFAULT_MQ_BROKER_URI_ROOT.length(); + result.emplace_back(start, stop); + + for (;stop != topic.cend();) + { + start = stop; + + do + { + ++stop; + } + while (stop != topic.cend() && *stop != '/'); + + result.emplace_back(start, stop); + } + + return result; +} + +} // namespace NetworkManager diff --git a/device_core/iotivity_lib/src/mqhandler.cpp b/device_core/iotivity_lib/src/mqhandler.cpp index 0955ac6..6677054 100644 --- a/device_core/iotivity_lib/src/mqhandler.cpp +++ b/device_core/iotivity_lib/src/mqhandler.cpp @@ -20,26 +20,28 @@ using namespace std; using namespace OC; -using namespace NetworkManager; namespace PH = std::placeholders; namespace { -static const vector& DEFAULT_MQ_TYPES = {string(OC_RSRVD_RESOURCE_TYPE_MQ_BROKER)}; -static const vector& DEFAULT_MQ_INTERFACES = {DEFAULT_INTERFACE}; +static const vector DEFAULT_MQ_TYPES{string(OC_RSRVD_RESOURCE_TYPE_MQ_BROKER)}; +static const vector DEFAULT_MQ_INTERFACES{DEFAULT_INTERFACE}; static const string DEFAULT_MQ_BROKER_URI_ROOT = "/oic/ps"; } +namespace NetworkManager +{ + MqHandler::MqHandler(const std::string& cloudHost): cloudHost(cloudHost) { brokerResource = OCPlatform::constructResourceObject( cloudHost, DEFAULT_MQ_BROKER_URI_ROOT, - CT_DEFAULT, false, + static_cast(CT_ADAPTER_TCP | CT_IP_USE_V4), false, DEFAULT_MQ_TYPES, DEFAULT_MQ_INTERFACES ); @@ -48,91 +50,122 @@ MqHandler::MqHandler(const std::string& cloudHost): throw IoTInternalError("MQ Broker resource construct error.", EC_IOTIVITY_ERROR); } -OC::OCResource::Ptr MqHandler::getTopic(const std::string& subTopic) +OC::OCResource::Ptr MqHandler::getTopic(const MqTopic& topic) { - std::string topic = DEFAULT_MQ_BROKER_URI_ROOT + subTopic; - QueryParamsMap query; - OCRepresentation rep; - MqCreateTopicCallback::Sptr callback = std::make_shared(); - - guardErrorCode(brokerResource->createMQTopic( - rep, topic, query, - bind_callback(callback, PH::_1, PH::_2, PH::_3), - QualityOfService::HighQos), "createMQTopic()"); + MqTopic::Parts parts = topic.split(); - if (!callback->wait()) + if (parts.size() < 2) { - LOG_E(TAG, "createMQTopic() cb not called."); - return nullptr; + throw IoTInternalError("Wrong topic name " + topic.getSubName(), EC_IOTIVITY_ERROR); } - switch (callback->errorCode) + auto stop = parts.begin() + 1; + OCResource::Ptr created_topic; + + std::unique_lock lock(handler_mutex); + + do { - // Topic was first time created - case OC_STACK_RESOURCE_CREATED: - return callback->resource; - - // Topic already exists - case OC_STACK_FORBIDDEN_REQ: - return OCPlatform::constructResourceObject( - cloudHost, - topic, - CT_DEFAULT, false, - DEFAULT_MQ_TYPES, - DEFAULT_MQ_INTERFACES - ); - - default: - guardErrorCode((OCStackResult)callback->errorCode, "createMQTopic() cb"); - } - LOG_E(TAG, "MqHandler::getTopic() failed"); - return nullptr; + ++stop; + MqTopic parent_topic(parts.begin(), stop); + auto it = topicCache.find(parent_topic.getName()); + + if (it == topicCache.end()) + { + lock.unlock(); + MqCreateTopicCallback::Sptr callback = std::make_shared(); + + guardErrorCode(brokerResource->createMQTopic( + OCRepresentation{}, parent_topic.getName(), QueryParamsMap{}, + bind_callback(callback, PH::_1, PH::_2, PH::_3), + QualityOfService::HighQos), "createMQTopic()"); + + if (!callback->wait()) + { + LOG_E(TAG, "createMQTopic() cb not called."); + return nullptr; + } + + switch (callback->errorCode) + { + // Topic was first time created + case OC_STACK_RESOURCE_CREATED: + created_topic = callback->resource; + break; + + // Topic already exists + case OC_STACK_FORBIDDEN_REQ: + created_topic = OCPlatform::constructResourceObject( + cloudHost, + parent_topic.getName(), + CT_DEFAULT, false, + DEFAULT_MQ_TYPES, + DEFAULT_MQ_INTERFACES + ); + break; + + default: + guardErrorCode((OCStackResult)callback->errorCode, "createMQTopic() cb"); + } + + if (!created_topic) + { + LOG_E(TAG, "MqHandler::getTopic() Failed to construct topic %s", parent_topic.getName().c_str()); + throw IoTInternalError("Failed to construct topic " + parent_topic.getName(), EC_IOTIVITY_ERROR); + } + + lock.lock(); + topicCache[parent_topic.getName()] = created_topic; + } + } while (stop != parts.end()); + + return created_topic; } -OC::OCResource::Ptr MqHandler::discoveryTopic(const std::string& subTopic) +OC::OCResource::Ptr MqHandler::discoveryTopic(const MqTopic& topic/*subTopic*/) { - std::string topic = DEFAULT_MQ_BROKER_URI_ROOT + subTopic; QueryParamsMap query; - MqDiscoveryTopicCallback::Sptr callback = std::make_shared(topic); + MqDiscoveryTopicCallback::Sptr callback = std::make_shared(topic.getName()); guardErrorCode(brokerResource->discoveryMQTopics( query, bind_callback(callback, PH::_1, PH::_2, PH::_3), QualityOfService::HighQos), "discoveryMQTopics()"); - if (!callback->wait()) - { - LOG_E(TAG, "discoveryMQTopic() cb not called."); - return nullptr; - } + callback->wait(); + + OCResource::Ptr resource; - switch (callback->errorCode) + int r = std::try_lock(callback->mtx, handler_mutex); + if (-1 == r) { - // Topic was discovered - case OC_STACK_OK: - return callback->resource; - default: - guardErrorCode((OCStackResult)callback->errorCode, "discoveryMQTopic() cb"); - } - LOG_E(TAG, "MqHandler::discoveryTopic() failed"); - return nullptr; + std::for_each(callback->topics.begin(), callback->topics.end(), + [this] (OCResource::Ptr ptr) + { + if (topicCache.find(ptr->uri()) == topicCache.end()) + { + topicCache[ptr->uri()] = ptr; + } + } + ); + + auto it = topicCache.find(topic.getName()); + if (it != topicCache.end()) + { + resource = it->second; + } + + callback->mtx.unlock(); + handler_mutex.unlock(); + } + + return resource; } void MqHandler::publish(const std::string& subTopic, const OCRepresentation& rep) { -// OC::OCResource::Ptr topicResource = getTopic(subTopic); -// if (!topicResource) -// return; - - OC::OCResource::Ptr topicResource = nullptr; - topicResource = discoveryTopic(subTopic); - if(!topicResource) - { - topicResource = getTopic(subTopic); - if (!topicResource) - return; - } + OC::OCResource::Ptr topicResource = findCreateTopic(subTopic); PostResourceCallback::Sptr callback = std::make_shared(); @@ -150,41 +183,44 @@ void MqHandler::publish(const std::string& subTopic, const OCRepresentation& rep guardPostErrorCode(callback->errorCode, "publishMQTopic()"); } -void MqHandler::subscribe(const std::string& subTopic, ObserveCallback subscribeCB) +OC::OCResource::Ptr MqHandler::findCreateTopic(const std::string& subTopic) { -// OC::OCResource::Ptr topicResource = getTopic(subTopic); -// if (!topicResource) -// return; + MqTopic topic(subTopic); + handler_mutex.lock(); - OC::OCResource::Ptr topicResource = nullptr; - topicResource = discoveryTopic(subTopic); - if(!topicResource) + auto it = topicCache.find(topic.getName()); + OC::OCResource::Ptr topicResource = it != topicCache.end() ? it->second : nullptr; + + handler_mutex.unlock(); + + if (topicResource) { - topicResource = getTopic(subTopic); - if (!topicResource) - return; + return topicResource; } + topicResource = discoveryTopic(topic); + + if(!topicResource) { - std::unique_lock lock(handler_mutex); - topicCache[subTopic] = topicResource; + topicResource = getTopic(topic); + + if (!topicResource) + { + throw IoTInternalError("Failed to find and create topic " + topic.getName(), EC_IOTIVITY_ERROR); + } + + handler_mutex.lock(); + topicCache[topic.getName()] = topicResource; + handler_mutex.unlock(); } - QueryParamsMap query; - guardErrorCode(topicResource->subscribeMQTopic(ObserveType::Observe, query, - subscribeCB, QualityOfService::HighQos), "subscribeMQTopic()"); + return topicResource; } -void MqHandler::subscribeToExistingTopic(const std::string& subTopic, OC::ObserveCallback subscribeCB) +void MqHandler::subscribe(const std::string& subTopic, ObserveCallback subscribeCB) { - OC::OCResource::Ptr topicResource = discoveryTopic(subTopic); + OC::OCResource::Ptr topicResource = findCreateTopic(subTopic); //nullptr; - if (!topicResource) - return; - { - std::unique_lock lock(handler_mutex); - topicCache[subTopic] = topicResource; - } QueryParamsMap query; guardErrorCode(topicResource->subscribeMQTopic(ObserveType::Observe, query, subscribeCB, QualityOfService::HighQos), "subscribeMQTopic()"); @@ -192,12 +228,15 @@ void MqHandler::subscribeToExistingTopic(const std::string& subTopic, OC::Observ void MqHandler::unsubscribe(const std::string& subTopic) { - std::unique_lock lock(handler_mutex); - auto it = topicCache.find(subTopic); + std::unique_lock lock(handler_mutex); + auto it = topicCache.find(DEFAULT_MQ_BROKER_URI_ROOT + subTopic); if (it != topicCache.end()) { OC::OCResource::Ptr topicResource = it->second; + topicCache.erase(it); + lock.unlock(); guardErrorCode(topicResource->unsubscribeMQTopic(QualityOfService::HighQos), "unsubscribeMQTopic()"); - topicCache.erase (it); } } + +} // namespace NetworkManager diff --git a/device_core/iotivity_lib/src/resource_callbacks.cpp b/device_core/iotivity_lib/src/resource_callbacks.cpp index 6cd5250..1606af6 100644 --- a/device_core/iotivity_lib/src/resource_callbacks.cpp +++ b/device_core/iotivity_lib/src/resource_callbacks.cpp @@ -160,10 +160,12 @@ void MqDiscoveryTopicCallback::call(std::weak_ptr ctx, { std::unique_lock ilock(p->mtx); + if (errorCode != OC_STACK_OK) return; + + p->topics.push_back(resource); + if(resource->uri() == p->topic) { - p->errorCode = errorCode; - p->resource = resource; p->signalize(); } } diff --git a/device_core/nmdaemon/application_service.cpp b/device_core/nmdaemon/application_service.cpp index 6b6a7d2..c1d7fea 100644 --- a/device_core/nmdaemon/application_service.cpp +++ b/device_core/nmdaemon/application_service.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "application_service.h" @@ -48,6 +49,10 @@ std::string ApplicationService::find_package_by_app_name(const std::string& app_ } pclose(fp); + + res.erase(std::remove_if(res.begin(), res.end(), [] (const char c){ + return c == '\r' || c == '\n'; + }), res.end()); } return res; diff --git a/device_core/nmdaemon/commandhandler.cpp b/device_core/nmdaemon/commandhandler.cpp new file mode 100644 index 0000000..d025f09 --- /dev/null +++ b/device_core/nmdaemon/commandhandler.cpp @@ -0,0 +1,94 @@ +#include "commandhandler.h" +#include "registration_mq.h" +#include "device_commands.h" +#include "application_service.h" + +namespace NMD +{ + +CommandHandler::CommandHandler(NetworkManager::IoTivity* iotivity, + std::shared_ptr hub, + std::shared_ptr report_handler, + std::shared_ptr policy_handler, + std::shared_ptr proxy_thread, + WorkingMode wmode, + ThreadBase* main_thread) + : m_iotivity(iotivity) + , m_report_handler(report_handler) + , m_policy_handler(policy_handler) + , m_proxy_thread(proxy_thread) + , m_wmode(wmode) + , m_main_thread(main_thread) +{ + assert(iotivity); + assert(report_handler); + assert(policy_handler); + assert(proxy_thread); + assert(main_thread != nullptr); +} + +bool CommandHandler::process(const OC::OCRepresentation& command) +{ + DeviceCommands action = static_cast(command.getValue("command")); + + switch (action) + { + case DeviceCommands::UNOWN: + return unOwnCommand(command); + case DeviceCommands::UNINSTALL: + return uninstallCommand(command); + default: + return false; + } +} + +bool CommandHandler::uninstallCommand(const OC::OCRepresentation& command) +{ + std::string name = command.getValue("name"); + + if (name.empty()) return false; + + if(name == "smack_test") + { + std::string pack_full_name = ApplicationService::find_package_by_app_name(name); + if(!pack_full_name.empty()) + { + return 0 == ApplicationService::uninstall(pack_full_name); + } + } + + return true; +} + +bool CommandHandler::unOwnCommand(const OC::OCRepresentation& command) +{ + m_proxy_thread->addAction(std::async(std::launch::deferred, &CommandHandler::unOwnTask, this)); + return true; +} + + +void CommandHandler::unOwnTask() +{ + if (m_wmode != WorkingMode::Primitive) + { + RegistrationMQ::unreg(m_iotivity->getDeviceID()); + } + + delete_config(); + + if (g_working_mode == WorkingMode::Hub) + { + // disable hub to send found devices list + assert(m_hub); + m_hub->setEnabled(false); + clear_hub_cache(); + m_report_handler->disable(); + m_policy_handler->disable(); + m_proxy_thread->addAction(std::async(std::launch::deferred, &NetworkManager::IoTivity::unPublishAllResources, m_iotivity)); + m_proxy_thread->addAction(std::async(std::launch::deferred, &HubResource::unownAll, m_hub.get())); + } + + m_main_thread->stop(); +} + +} // namespace NMD diff --git a/device_core/nmdaemon/commandhandler.h b/device_core/nmdaemon/commandhandler.h new file mode 100644 index 0000000..48db6fe --- /dev/null +++ b/device_core/nmdaemon/commandhandler.h @@ -0,0 +1,58 @@ +/** + * @brief Command handler implementation + * Process commands: + * - device unpairing + * - application uninstallation + * @date Created 10.08.2017 + * @author Created 2017 in Samsung Ukraine R&D Center (SURC) under a contract + * between LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) + * and "Samsung Electronics Co", Ltd (Seoul, Republic of Korea). + * Copyright: (c) Samsung Electronics Co, Ltd 2017. All rights reserved. + * @author Mail to: Dmytro Lomtev, d.lomtev@samsung.com + */ +#ifndef COMMANDHANDLER_H +#define COMMANDHANDLER_H + +#include "iotivity.h" +#include "hub_resource.h" +#include "reporthandler.h" +#include "policyhandler.h" +#include "proxythread.h" +#include "icommandhandler.h" +#include "utils.h" + +namespace NMD +{ + +class CommandHandler : public ICommandHandler +{ +public: + CommandHandler(NetworkManager::IoTivity* iotivity, + std::shared_ptr hub, + std::shared_ptr report_handler, + std::shared_ptr policy_handler, + std::shared_ptr proxy_thread, + WorkingMode wmode, + ThreadBase* main_thread); + + bool process(const OC::OCRepresentation& command) override; +private: + + bool unOwnCommand(const OC::OCRepresentation& command); + + bool uninstallCommand(const OC::OCRepresentation& command); + + void unOwnTask(); + + NetworkManager::IoTivity* m_iotivity; + std::shared_ptr m_hub; + std::shared_ptr m_report_handler; + std::shared_ptr m_policy_handler; + std::shared_ptr m_proxy_thread; + WorkingMode m_wmode; + ThreadBase* m_main_thread; +}; + +} // namespace NMD + +#endif // COMMANDHANDLER_H diff --git a/device_core/nmdaemon/control_resource.cpp b/device_core/nmdaemon/control_resource.cpp index 9d1ab3a..672434a 100644 --- a/device_core/nmdaemon/control_resource.cpp +++ b/device_core/nmdaemon/control_resource.cpp @@ -1,5 +1,5 @@ /** - * @brief Resource used for device unowning + * @brief Resource used for commands handling * @date Created 10.07.2017 * @author Created 2017 in Samsung Ukraine R&D Center (SURC) under a contract * between LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) @@ -11,6 +11,7 @@ #include "OCPlatform.h" #include "control_resource.h" #include "logging.h" +#include using namespace OC; @@ -24,10 +25,11 @@ const std::string CTRL_RESOURCE_TYPE = "device.control"; namespace NMD { -ControlResource::ControlResource(ControlCallback&& cb) +ControlResource::ControlResource(/*ControlCallback&& cb*/ICommandHandler* handler) : IotResource(CTRL_RESOURCE_URI, {CTRL_RESOURCE_TYPE}, {DEFAULT_INTERFACE}) - , callback(cb) + , m_handler(handler) { + assert(nullptr != handler); } void ControlResource::setRepresentation(OCRepresentation& rep) @@ -53,17 +55,16 @@ OCEntityHandlerResult ControlResource::entityHandler(std::shared_ptrgetQueryParameters(); - std::string state = query["state"]; + const OCRepresentation& representation = request->getResourceRepresentation(); - if (OC_STACK_OK == sendRepresentation(request)) + if (!m_handler->process(representation)) { - res = OC_EH_OK; + return res; } - if (callback) + if (OC_STACK_OK == sendRepresentation(request)) { - callback(state); + res = OC_EH_OK; } } else if(rt == "DELETE") diff --git a/device_core/nmdaemon/control_resource.h b/device_core/nmdaemon/control_resource.h index 4fe4080..5dc779e 100644 --- a/device_core/nmdaemon/control_resource.h +++ b/device_core/nmdaemon/control_resource.h @@ -1,5 +1,5 @@ /** - * @brief Resource used for device unowning + * @brief Resource used for commands handling * @date Created 10.07.2017 * @author Created 2017 in Samsung Ukraine R&D Center (SURC) under a contract * between LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) @@ -15,6 +15,7 @@ #include #include "OCApi.h" #include "iot_resource.h" +#include "icommandhandler.h" namespace NMD { @@ -22,9 +23,11 @@ namespace NMD class ControlResource : public NetworkManager::IotResource { public: - typedef std::function ControlCallback; - - ControlResource(ControlCallback&& cb); + /** + * @brief Constructor + * @param handler [in] pointer to command handler object + */ + ControlResource(ICommandHandler* handler); ControlResource(const ControlResource& obj) = delete; @@ -37,7 +40,7 @@ public: OCEntityHandlerResult entityHandler(std::shared_ptr request) override; private: - ControlCallback callback; + ICommandHandler* m_handler; }; } // namespace NMD diff --git a/device_core/nmdaemon/icommandhandler.h b/device_core/nmdaemon/icommandhandler.h new file mode 100644 index 0000000..666acaf --- /dev/null +++ b/device_core/nmdaemon/icommandhandler.h @@ -0,0 +1,29 @@ +/** + * @brief Command handler abstract class + * @date Created 10.08.2017 + * @author Created 2017 in Samsung Ukraine R&D Center (SURC) under a contract + * between LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) + * and "Samsung Electronics Co", Ltd (Seoul, Republic of Korea). + * Copyright: (c) Samsung Electronics Co, Ltd 2017. All rights reserved. + * @author Mail to: Dmytro Lomtev, d.lomtev@samsung.com + */ +#ifndef ICOMMANDHANDLER_H +#define ICOMMANDHANDLER_H + +#include + +class ICommandHandler +{ +public: + /** + * @brief process command + * @param command [in] command encapsulated in OCRepresentation object must contain "command" field + * end optional fields specific for each type of command + * @return true if success and false otherwise + */ + virtual bool process(const OC::OCRepresentation& command) = 0; + + virtual ~ICommandHandler() = default; +}; + +#endif // ICOMMANDHANDLER_H diff --git a/device_core/nmdaemon/main_thread.cpp b/device_core/nmdaemon/main_thread.cpp index 3de4170..e4d3eee 100644 --- a/device_core/nmdaemon/main_thread.cpp +++ b/device_core/nmdaemon/main_thread.cpp @@ -21,6 +21,7 @@ #endif #include "registration_mq.h" #include "application_service.h" +#include "commandhandler.h" using namespace NetworkManager; namespace PH = std::placeholders; @@ -169,13 +170,9 @@ void MainThread::routine() policy_hub_resource->registerResource(); } - ControlResource control([this, &hub, &proxy_thread, iotivity, &policy_handler, &report_handler, ¬ification_handler](const std::string& state) - { - if(state == "unown") - { - proxy_thread->addAction(std::async(std::launch::deferred, &MainThread::unregister_proc, this, iotivity, hub, report_handler, policy_handler, notification_handler, proxy_thread)); - } - }); + + CommandHandler command_handler(iotivity, hub, report_handler, policy_handler, proxy_thread, g_working_mode, this); + ControlResource control(&command_handler); if(OC_STACK_OK != control.registerResource()) { diff --git a/device_core/nmdaemon/notification_handler_mq.cpp b/device_core/nmdaemon/notification_handler_mq.cpp index 7e6689b..40ba1bf 100644 --- a/device_core/nmdaemon/notification_handler_mq.cpp +++ b/device_core/nmdaemon/notification_handler_mq.cpp @@ -18,9 +18,7 @@ bool NotificationHandlerMQ::init() { auto iotivity = NetworkManager::IoTivity::getInstance(); auto handler = iotivity->getMqHandler(); - std::string parentTopic = "/" + iotivity->getCloudAuthId(); - handler->getTopic(parentTopic); - std::string topic = parentTopic + "/notification"; + std::string topic = "/" + iotivity->getCloudAuthId() + "/notification"; handler->subscribe(topic, std::bind(&NotificationHandler::observeCallback, this, PH::_1, PH::_2, PH::_3, PH::_4)); LOG_D(TAG, "Suscribed to topic [%s]", topic.c_str()); diff --git a/device_core/nmdaemon/policyhandlermq.cpp b/device_core/nmdaemon/policyhandlermq.cpp index c40788b..100c394 100644 --- a/device_core/nmdaemon/policyhandlermq.cpp +++ b/device_core/nmdaemon/policyhandlermq.cpp @@ -57,29 +57,9 @@ bool PolicyHandlerMQ::init() { auto iotivity = NetworkManager::IoTivity::getInstance(); auto handler = iotivity->getMqHandler(); - std::string parentTopic = "/" + iotivity->getCloudAuthId(); - handler->getTopic(parentTopic); - std::string topic = parentTopic + "/policy"; + std::string topic = "/" + iotivity->getCloudAuthId() + "/policy"; handler->subscribe(topic, std::bind(&PolicyHandler::observeCallback, this, PH::_1, PH::_2, PH::_3, PH::_4)); LOG_D(TAG, "Suscribed to topic [%s]", topic.c_str()); return true; } - -/** - * This method was added for demo presentation only. - * For some reason the server does not see the newly created topics - * and can not connect to them. - * So for demo presentation an existing topic is used. - */ -//bool PolicyHandlerMQ::init() -//{ -// auto iotivity = NetworkManager::IoTivity::getInstance(); -// auto handler = iotivity->getMqHandler(); -// std::string parentTopic = "/2a12a5a1-dad1-438d-8141-dbe1629ea93e"; -// std::string topic = parentTopic + "/policy"; -// handler->subscribeToExistingTopic(topic, std::bind(&PolicyHandler::observeCallback, this, PH::_1, PH::_2, PH::_3, PH::_4)); - -// LOG_D(TAG, "Suscribed to topic [%s]", topic.c_str()); -// return true; -//} diff --git a/device_core/secserver/secserver.cpp b/device_core/secserver/secserver.cpp index f278623..dc78209 100644 --- a/device_core/secserver/secserver.cpp +++ b/device_core/secserver/secserver.cpp @@ -230,7 +230,6 @@ static void mainLoop() reportResouce.registerResource(); reportResouce.setNotificationResource(¬ifResource); - iot->getMqHandler()->getTopic("/srv"); iot->getMqHandler()->subscribe("/srv/report", &subscribeCB); cout << "Subscribed on topic \"/srv/report\"" << endl << flush; iot->getMqHandler()->subscribe("/srv/policy", &policyCB); @@ -248,7 +247,6 @@ static void mainLoop() g_callbackLock.wait(lock); MqHandler mqHandler(host); - mqHandler.getTopic("/00000000-0000-0000-0000-000000000000"); cout << "Server resources publish success" << endl << flush; diff --git a/device_core/utest/CMakeLists.txt b/device_core/utest/CMakeLists.txt index 21d83ef..d6d146b 100644 --- a/device_core/utest/CMakeLists.txt +++ b/device_core/utest/CMakeLists.txt @@ -23,6 +23,8 @@ FILE(GLOB SRCS *.cpp ../nmdaemon/hub_resource.cpp ../nmdaemon/registration_mq.cpp ../nmdaemon/thread_base.cpp + ../nmdaemon/commandhandler.cpp + ../nmdaemon/application_service.cpp ) add_executable (${PROJECT_NAME} ${SRCS}) diff --git a/device_core/utest/local_test_resources_init.cpp b/device_core/utest/local_test_resources_init.cpp index a1fface..250543b 100644 --- a/device_core/utest/local_test_resources_init.cpp +++ b/device_core/utest/local_test_resources_init.cpp @@ -3,10 +3,12 @@ #include #include "iotivity.h" #include "hub_resource.h" +#include "control_resource.h" #include #include #include #include +#include using namespace NetworkManager; using namespace NMD; @@ -43,6 +45,15 @@ const TestDeviceInfo test_devices[] = { } }; +class HandlerMock : public ICommandHandler +{ +public: + bool process(const OC::OCRepresentation& rep) override + { + return true; + } +}; + } int child_process_routine() @@ -76,10 +87,17 @@ int child_process_routine() return -1; } - for (;;) + HandlerMock hm; + + ControlResource res(&hm); + + if (OC_STACK_OK != res.registerResource()) { - std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << "register control resource failed" << std::endl; + return -1; } + std::this_thread::sleep_for(std::chrono::seconds(300)); + return 0; } diff --git a/device_core/utest/test_IoT.cpp b/device_core/utest/test_IoT.cpp index 46f803f..cf90981 100644 --- a/device_core/utest/test_IoT.cpp +++ b/device_core/utest/test_IoT.cpp @@ -31,6 +31,8 @@ using namespace OC; using namespace NetworkManager; extern std::string cloud_host; +extern std::string TEST_ACCOUNT_LOGIN; +extern std::string TEST_ACCOUNT_PASSWORD; class test_IoT_Fixture: public ::testing::Test { @@ -109,8 +111,9 @@ TEST(test_IoT, signInIncorrectInput) * 1. Device id should be not empty * 2. No exceptions should be thrown */ -TEST_F(test_IoT_Fixture, getDeviceIdCorrect) +TEST(test_IoT, getDeviceIdCorrect) { + IoTivity* iotivity = IoTivity::getInstance(); string duid; ASSERT_NO_THROW(duid = iotivity->getDeviceID()); ASSERT_NE("", duid); @@ -124,101 +127,6 @@ TEST(test_IoT, test_IOT_ReportFormat) ASSERT_FALSE(NMD::makeReport("1", "sim", 0, "report data").empty()); } -/** - * Test check report sender functional - */ -TEST_F(test_IoT_Fixture, test_IOT_ReportSender) -{ - try - { -// shared_ptr report_res = IotResource::findResource( iotivity->host(), -// {OC_RSRVD_WELL_KNOWN_URI}, static_cast(CT_DEFAULT), 1, "core.security" ); - shared_ptr report_res = iotivity->findResource(true, "core.security"); - if(!report_res) - throw runtime_error("Failed to find report resource"); - - OCRepresentation rep; - rep.setValue("report", NMD::makeReport(iotivity->getDeviceID(), "report_name", 0, "{\"text\":\"report data\"}")); - - if(IotResource::post(report_res, {"core.security"}, {DEFAULT_INTERFACE}, rep, QueryParamsMap()) != OC_STACK_OK) - throw runtime_error("Failed to post report"); - - } - catch (NMexception& e) - { - ADD_FAILURE() << e.what() << " " << e.errorCode(); - } - catch (std::exception& e) - { - ADD_FAILURE() << e.what(); - } -} - -/** - * Test check policy sender functional - */ -TEST_F(test_IoT_Fixture, test_IOT_PolicySender) -{ - bool res = false; - - try - { -// auto policy_res = IotResource::findResource(iotivity->host(), {OC_RSRVD_WELL_KNOWN_URI}, CT_DEFAULT, 1, "core.policy"); - auto policy_res = iotivity->findResource(true, "core.policy"); - ASSERT_TRUE(bool(policy_res)) << "Failed to find policy resource"; - - string device_id = iotivity->getDeviceID(); - string agent_id = "3574462a-7efa-9449-4d9f-488734c79c0a"; - string json_data = "[{\"group\": \"tv-extension\", \"policies\": [{\"name\": \"usb\", \"state\": 0, \"items\": []}, {\"name\": \"screen-capture\", \"state\": 1, \"items\": []}]}]"; - - static mutex mtx; - static mutex mtx1; - unique_lock lck(mtx); - condition_variable cvar; - auto on_observe = [&](const HeaderOptions& /*_head_options*/, const OCRepresentation & _rep, const int& _ecode, - const int& _seq_number) - { - unique_lock lambda_lock(mtx1); - if (res) return; - - // Transform representation to string - std::ostringstream oss; - for(auto it = _rep.begin(); it != _rep.end(); ++it) - oss << it->getValueToString(); - oss.str(); - - if(oss.str().find(json_data) != string::npos) - { - res = true; - cvar.notify_all(); - } - }; - - auto observ_result = policy_res->observe(ObserveType::Observe, QueryParamsMap{{"did", device_id}}, on_observe); - - ASSERT_EQ(OC_STACK_OK, observ_result) << "Failed to observe policy resource"; - - OCRepresentation rep; - rep.setValue("policy", json_data); - auto post_result = IotResource::post(policy_res, {"core.policy"}, {DEFAULT_INTERFACE}, rep, QueryParamsMap({{"did", device_id}, {"agent", agent_id}})); - ASSERT_EQ(OC_STACK_OK, post_result) << "Failed to post policy"; - - cvar.wait_for(lck, chrono::seconds(3)); - -// IOT_Resource::cancelObserve(policy_res); - } - catch (NMexception& e) - { - ADD_FAILURE() << e.what() << " " << e.errorCode(); - } - catch (std::exception& e) - { - ADD_FAILURE() << e.what(); - } - - ASSERT_TRUE(res); -} - TEST_F(test_IoT_Fixture, findResourceWork) { ASSERT_NO_THROW(iotivity->findResource(false, "", OC_RSRVD_WELL_KNOWN_URI, CT_DEFAULT)); diff --git a/device_core/utest/test_all.cpp b/device_core/utest/test_all.cpp index 55e82cb..fa8f7be 100644 --- a/device_core/utest/test_all.cpp +++ b/device_core/utest/test_all.cpp @@ -1,9 +1,15 @@ #include #include #include +#include #include +#include +#include +#include "iotivity.h" std::string cloud_host{"coap+tcp://106.125.46.44:5683"}; +std::string TEST_ACCOUNT_LOGIN{"admin@samsung.com"}; +std::string TEST_ACCOUNT_PASSWORD{"111111"}; /** * @brief Child process routing for resource initialization used in functional tests @@ -11,8 +17,26 @@ std::string cloud_host{"coap+tcp://106.125.46.44:5683"}; */ int child_process_routine(); +void signal_handler(int signal) +{ + if (signal == SIGUSR1) + { + NetworkManager::IoTivity::cleanUp(); + exit(0); + } +} + int main(int argc, char** argv) { + auto sig_result = signal(SIGUSR1, signal_handler); + if (SIG_ERR == sig_result) + { + std::cout << "Failed to setup TERM signal handler" << std::endl; + return -1; + } + + NetworkManager::IoTivity::setPersistentStoragePath("/tmp/temporary_persitent_storage.dat"); + pid_t pid = fork(); if (pid < 0) @@ -28,6 +52,8 @@ int main(int argc, char** argv) int result = -1; + std::this_thread::sleep_for(std::chrono::seconds(1)); + try { if (argc > 1 && argv[1][0] != '-') @@ -35,6 +61,7 @@ int main(int argc, char** argv) cloud_host = std::string{"coap+tcp://"} + argv[1] + std::string{":5683"}; } ::testing::InitGoogleTest(&argc, argv); +// ::testing::InitGoogleMock(&argc, argv); result = RUN_ALL_TESTS(); } catch (std::exception& e) @@ -46,9 +73,6 @@ int main(int argc, char** argv) std::cout << "Unknown exception" << std::endl; } - std::string cmd{"kill -9 "}; - cmd.append(std::to_string(int(pid))); - system(cmd.c_str()); - + kill(pid, SIGUSR1); return result; } diff --git a/device_core/utest/test_commandhandler.cpp b/device_core/utest/test_commandhandler.cpp new file mode 100644 index 0000000..f95260a --- /dev/null +++ b/device_core/utest/test_commandhandler.cpp @@ -0,0 +1,255 @@ +#include +#include +#include +#include +#include +#include "nmlib.h" +#include "commandhandler.h" +#include "device_commands.h" +#include "securitycontext.h" +#include "ctrl_app_support.h" + +using namespace NetworkManager; +using namespace NMD; +using ::testing::Return; +using ::testing::StrEq; + +extern std::string cloud_host; +extern std::string TEST_ACCOUNT_LOGIN; +extern std::string TEST_ACCOUNT_PASSWORD; + +class ReportHandlerMock: public ReportHandler +{ +public: + void pass(const OC::OCRepresentation& rep, const OC::QueryParamsMap& params) override + { + + } +}; + +class PolicyHandlerMock : public PolicyHandler +{ +public: + virtual void pass(const OC::OCRepresentation& rep, const OC::QueryParamsMap& params) + { + + } + + virtual bool init() + { + + } +}; + +class test_commandhandler_fixture : public ::testing::Test +{ +public: + static void SetUpTestCase() + { + ASSERT_EQ(EC_OK, NM_init(&ctx)); + ASSERT_EQ(EC_OK, NM_signIn(ctx, TEST_ACCOUNT_LOGIN.c_str(), TEST_ACCOUNT_PASSWORD.c_str())); + } + + static void TearDownTestCase() + { + ASSERT_NO_THROW(NM_signOut(ctx)); + ASSERT_NO_THROW(NM_cleanup(&ctx)); + } + + static NM_hContext ctx; +}; + +NM_hContext test_commandhandler_fixture::ctx = nullptr; + +class MainThreadMock : public ThreadBase +{ +public: + MOCK_METHOD0(stop, void ()); +}; + +/** + * Test CommandHandler class UNOWN command for standard working mode + */ +TEST_F(test_commandhandler_fixture, test_unOwnTask_standard) +{ + MainThreadMock main_thread; + + EXPECT_CALL(main_thread, stop()) + .Times(1); + + try + { + IoTivity* iot = ctx->instance; + EXPECT_TRUE(iot->isSignedIn()); + std::shared_ptr proxy = std::make_shared(); + std::shared_ptr hub = nullptr; + std::shared_ptr rh = std::make_shared(); + std::shared_ptr ph = std::make_shared(); + + proxy->start(); + + CommandHandler handler(iot, hub, rh, ph, proxy, WorkingMode::Standard, &main_thread); + + OC::OCRepresentation rep; + rep.setValue("command", int(DeviceCommands::UNOWN)); + handler.process(rep); + proxy->stop(); + proxy->join(); + } + catch (std::exception& e) + { + FAIL() << "Exception: " << e.what(); + } +} + +/** + * Test CommandHandler class UNOWN command for primitive working mode + */ +TEST_F(test_commandhandler_fixture, test_unOwnTask_primitive) +{ + MainThreadMock main_thread; + + EXPECT_CALL(main_thread, stop()) + .Times(1); + + try + { + IoTivity* iot = ctx->instance; + EXPECT_TRUE(iot->isSignedIn()); + std::shared_ptr proxy = std::make_shared(); + std::shared_ptr hub = nullptr; + std::shared_ptr rh = std::make_shared(); + std::shared_ptr ph = std::make_shared(); + + proxy->start(); + CommandHandler handler(iot, hub, rh, ph, proxy, WorkingMode::Primitive, &main_thread); + + OC::OCRepresentation rep; + rep.setValue("command", int(DeviceCommands::UNOWN)); + handler.process(rep); + proxy->stop(); + proxy->join(); + } + catch (std::exception& e) + { + FAIL() << "Exception: " << e.what(); + } +} + +/** + * Test CommandHandler class UNOWN command for hub working mode + */ +TEST_F(test_commandhandler_fixture, test_unOwnTask_hub) +{ + MainThreadMock main_thread; + + EXPECT_CALL(main_thread, stop()) + .Times(1); + + try + { + IoTivity* iot = ctx->instance; + EXPECT_TRUE(iot->isSignedIn()); + std::shared_ptr proxy = std::make_shared(); + std::shared_ptr hub = std::make_shared(iot, proxy, std::string{""}); + std::shared_ptr rh = std::make_shared(); + std::shared_ptr ph = std::make_shared(); + + proxy->start(); + hub->registerResource(); + + CommandHandler handler(iot, hub, rh, ph, proxy, WorkingMode::Hub, &main_thread); + + OC::OCRepresentation rep; + rep.setValue("command", int(DeviceCommands::UNOWN)); + handler.process(rep); + proxy->stop(); + proxy->join(); + } + catch (std::exception& e) + { + FAIL() << "Exception: " << e.what(); + } +} + +namespace +{ +const std::string test_executable{"smack_test"}; +const std::string test_rpm_query_stub_path{"/tmp/test_rpm_query_stub.txt"}; +const std::string test_executable_package_name = test_executable + ".1.2.3.4.rpm"; +} + +class ISystemMock +{ +public: + virtual int system(const char*) = 0; + virtual void popen(const char*, const char*) = 0; + virtual ~ISystemMock() {}; +}; + +class SystemMock : public ISystemMock +{ +public: + MOCK_METHOD1(system, int (const char* cmd)); + MOCK_METHOD2(popen, void (const char* file, const char* mode)); + ~SystemMock() {}; +}; + +SystemMock systemMock; + +int system(const char* cmd) +{ + return systemMock.system(cmd); +} + +FILE* popen(const char *command, const char *type) +{ + systemMock.popen(command, type); + return fopen(test_rpm_query_stub_path.c_str(), "r"); +} + + +TEST(test_commandhandler, test_uninstallTask) +{ + std::string system_cmd = "rpm -e " + test_executable_package_name + " > /dev/null"; + std::string popen_cmd = "rpm -qa | grep " + test_executable; + + EXPECT_CALL(systemMock, popen(StrEq(popen_cmd.c_str()), StrEq("r"))) + .Times(1); + EXPECT_CALL(systemMock, system(StrEq(system_cmd.c_str()))) + .Times(1) + .WillOnce(Return(0)); + + + std::ofstream f{test_rpm_query_stub_path}; + f << "package.1" << std::endl + << test_executable_package_name << std::endl + << "package.2" << std::endl; + f.close(); + + try + { + IoTivity* iot = IoTivity::getInstance(); + std::shared_ptr proxy = std::make_shared(); + std::shared_ptr hub = std::make_shared(iot, proxy, std::string{""}); + std::shared_ptr rh = std::make_shared(); + std::shared_ptr ph = std::make_shared(); + ThreadBase mt; + hub->registerResource(); + + CommandHandler handler(iot, hub, rh, ph, proxy, WorkingMode::Standard, &mt); + + OC::OCRepresentation rep; + rep.setValue("command", int(DeviceCommands::UNINSTALL)); + rep.setValue("pid", std::string{"1234"}); + rep.setValue("name", test_executable); + handler.process(rep); + } + catch (std::exception& e) + { + FAIL() << "Exception: " << e.what(); + } + + EXPECT_EQ(0, remove(test_rpm_query_stub_path.c_str())); +} + diff --git a/device_core/utest/test_controlresource.cpp b/device_core/utest/test_controlresource.cpp index 389fdc9..1475f49 100644 --- a/device_core/utest/test_controlresource.cpp +++ b/device_core/utest/test_controlresource.cpp @@ -16,11 +16,21 @@ #include #include #include +#include "device_commands.h" using namespace NMD; using namespace OC; using namespace NetworkManager; +class HandlerMock : public ICommandHandler +{ +public: + bool process(const OCRepresentation& rep) override + { + return true; + } +}; + /** * @brief TEST for ControlResource used in device unowning usecase * 1. IoTivity initialization @@ -35,13 +45,6 @@ TEST(test_ControlResource, test_all) { IoTivity* iot = IoTivity::getInstance(); bool cb_called = false; - ControlResource res([&](const std::string& state) - { - cb_called = true; - ASSERT_EQ("unown", state); - }); - - res.registerResource(); std::shared_ptr control; std::mutex mtx; @@ -75,7 +78,8 @@ TEST(test_ControlResource, test_all) OCRepresentation rep; - QueryParamsMap params{{"state", "unown"}}; + rep.setValue("command", int(DeviceCommands::UNOWN)); + QueryParamsMap params; flag = false; control->post(rep, params, @@ -88,10 +92,6 @@ TEST(test_ControlResource, test_all) cv.wait_for(lock, std::chrono::seconds(1), [&flag]{ return flag; }); ASSERT_TRUE(flag); - - ASSERT_NO_THROW(res.setRepresentation(rep)); - - ASSERT_TRUE(cb_called); } catch (std::exception& e) { diff --git a/device_core/utest/test_iot_dev_manager.cpp b/device_core/utest/test_iot_dev_manager.cpp index 612cbd7..a56ed27 100644 --- a/device_core/utest/test_iot_dev_manager.cpp +++ b/device_core/utest/test_iot_dev_manager.cpp @@ -21,8 +21,8 @@ using namespace NetworkManager; extern std::string cloud_host; -const std::string TEST_ACCOUNT_LOGIN{"admin@samsung.com"}; -const std::string TEST_ACCOUNT_PASSWORD{"111111"}; +extern std::string TEST_ACCOUNT_LOGIN; +extern std::string TEST_ACCOUNT_PASSWORD; class IoTDevManagerTest: public ::testing::Test { @@ -585,7 +585,7 @@ TEST_F(IoTDevManagerTestWithReg, reportTest) * 2. Get policy and compare with posted * 3. Free memory */ -TEST_F(IoTDevManagerWithOwned, policyTest) +TEST_F(IoTDevManagerWithOwned, DISABLED_policyTest) { std::string policy = R"-([ { diff --git a/device_core/utest/test_iot_notification.cpp b/device_core/utest/test_iot_notification.cpp index 676ee46..3a681dc 100644 --- a/device_core/utest/test_iot_notification.cpp +++ b/device_core/utest/test_iot_notification.cpp @@ -37,6 +37,7 @@ public: std::string host(cloud_host); ASSERT_NO_THROW(iot->signUp(host, auth_provider, password)); + ASSERT_TRUE(iot->isSignedIn()); } void TearDown() override @@ -90,7 +91,6 @@ static void notificationCb(NM_NotificationData data, void* inUserData) * 2. Check callback was called * 3. Check user data was transferd */ -#ifndef __BUILD_PRIMITIVE__ TEST_F(TestIotNotification, correct) { std::mutex notificationMtx; @@ -119,7 +119,6 @@ TEST_F(TestIotNotification, correct) ASSERT_TRUE(userDataCheck.firstCallbackFired); ASSERT_TRUE(userDataCheck.lastCallbackFired); } -#endif /* __BUILD_PRIMITIVE__ */ static void notification2Cb(NM_NotificationData data, void* inUserData) { diff --git a/device_core/utest/test_iotdevice_impl.cpp b/device_core/utest/test_iotdevice_impl.cpp new file mode 100644 index 0000000..c01f553 --- /dev/null +++ b/device_core/utest/test_iotdevice_impl.cpp @@ -0,0 +1,97 @@ +/** + * @brief Tests for IoTDevice_impl + * @date Created 8.08.2017 + * @author Created 2017 in Samsung Ukraine R&D Center (SURC) under a contract + * between LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) + * and "Samsung Electronics Co", Ltd (Seoul, Republic of Korea). + * Copyright: (c) Samsung Electronics Co, Ltd 2017. All rights reserved. + * @author Mail to: Dmytro Lomtev, d.lomtev@samsung.com + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "iotivity.h" +#include "control_resource.h" +#include "iotdevice_impl.h" + +using namespace OC; +using namespace NetworkManager; + + +class ResourceMock : public OC::OCResource +{ +public: + MOCK_CONST_METHOD0(host, std::string ()); +}; + +/** + * @brief TEST for ControlResource used in device unowning usecase + * 1. IoTivity initialization + * 2. Create and register ControlResource + * 3. ControlResource discovery + * 4. Post "unown" request + * 4. Check ControlResource entityHandler called and posted state is "unown" + */ +TEST(test_IoTDevice_Impl, test_all) +{ + try + { + IoTivity* iot = IoTivity::getInstance(); + ASSERT_NE(nullptr, iot); + const std::string uuid = "1234567890"; + const std::string name = "Test device"; + const std::string type = "iotdevice"; + const std::string model = "test model"; + IoTDevice_impl dev(uuid, name, type, model); + EXPECT_EQ(uuid, dev.getUUID()); + EXPECT_EQ(name, dev.getName()); + EXPECT_EQ(type, dev.getType()); + EXPECT_EQ(model, dev.getModel()); + EXPECT_EQ(uuid, dev.getRouting()); + EXPECT_FALSE(dev.isCloudAccessibility()); + EXPECT_FALSE(dev.isOnline()); + EXPECT_FALSE(dev.isActive()); + + OCResource::Ptr ctrl_resource = iot->findResource("", "device.control"); + + if(!ctrl_resource) + { + throw std::runtime_error("Test control resource not found"); + } + + OCConnectivityType connectivityType = CT_DEFAULT; + std::string host = ctrl_resource->host(); + std::string uri = "/resource"; + std::vector types = {"intel.rpost"}; + std::vector ifaces = {DEFAULT_INTERFACE}; + + ASSERT_FALSE(host.empty()); + + auto res = OCPlatform::constructResourceObject(host, uri, connectivityType, false, types, ifaces); + + ASSERT_FALSE(!res) << "Resource not constructed"; + + dev.activate(res); + EXPECT_EQ(host, dev.getHost()); + EXPECT_TRUE(dev.isActive()); + EXPECT_TRUE(dev.isOnline()); + + EXPECT_NO_THROW(dev.unOwnDevice()); + + EXPECT_ANY_THROW(dev.deleteApp("")); + EXPECT_NO_THROW(dev.deleteApp("app1")); + + dev.deactivate(); + EXPECT_FALSE(dev.isOnline()); + EXPECT_FALSE(dev.isActive()); + } + catch (std::exception& e) + { + FAIL() << "Exception: " << e.what(); + } +} diff --git a/device_core/utest/test_jsonutils.cpp b/device_core/utest/test_jsonutils.cpp index e9f0ba5..a56d021 100644 --- a/device_core/utest/test_jsonutils.cpp +++ b/device_core/utest/test_jsonutils.cpp @@ -40,7 +40,7 @@ TEST(JsonUtilsTest, ToDeviceTest) EXPECT_EQ("s8", device->getName()); EXPECT_EQ("phone", device->getType()); EXPECT_EQ("samsung s8 edge", device->getModel()); - EXPECT_EQ(true, device->isOnline()); + EXPECT_FALSE(device->isOnline()); // IoTDevice's created from the JSON are offline despite "status" flag } TEST(JsonUtilsTest, ToDeviceListTestBadCase) diff --git a/device_core/utest/test_mq.cpp b/device_core/utest/test_mq.cpp index 1b80610..52429e0 100644 --- a/device_core/utest/test_mq.cpp +++ b/device_core/utest/test_mq.cpp @@ -40,6 +40,7 @@ public: try { iot->signUp(host, auth_provider, password); + ASSERT_TRUE(iot->isSignedIn()); } catch(std::exception& e) { @@ -101,36 +102,42 @@ static void subscribeCB(const HeaderOptions &, */ TEST_F(TestIotMQ, publishCorrect) { - std::string stringToPublish("test"); - std::string stringToPublish2("test2"); - OCRepresentation rep; - rep["message"] = stringToPublish; + try + { + std::string stringToPublish("test"); + std::string stringToPublish2("test2"); + OCRepresentation rep; + rep["message"] = stringToPublish; + + std::mutex notificationMtx; + std::unique_lock notificationLock(notificationMtx); - std::mutex notificationMtx; - std::unique_lock notificationLock(notificationMtx); + auto mqHandler = iot->getMqHandler(); - MqHandler subMqHandler(cloud_host); - subMqHandler.getTopic("/srv"); - subMqHandler.subscribe("/srv/test2", &subscribeCB); + mqHandler->subscribe("/topic1/test2", &subscribeCB); - MqHandler pubMqHandler(cloud_host); - pubMqHandler.publish("/srv/test2", rep); + mqHandler->publish("/topic1/test2", rep); - notificationCV.wait_for( - notificationLock, - std::chrono::seconds(3) - ); + notificationCV.wait_for( + notificationLock, + std::chrono::seconds(3) + ); - subMqHandler.unsubscribe("/srv/test2"); + mqHandler->unsubscribe("/topic1/test2"); - rep["message"] = stringToPublish2; - pubMqHandler.publish("/srv/test2", rep); + rep["message"] = stringToPublish2; + mqHandler->publish("/topic1/test2", rep); - ASSERT_TRUE(firstCallbackFired); - ASSERT_TRUE(lastCallbackFired); - ASSERT_TRUE(normalCallbackFired); - ASSERT_EQ(stringToPublish, reveicedString); + ASSERT_TRUE(firstCallbackFired); + ASSERT_TRUE(lastCallbackFired); + ASSERT_TRUE(normalCallbackFired); + ASSERT_EQ(stringToPublish, reveicedString); + } + catch (std::exception& e) + { + FAIL() << "Exception: " << e.what() << std::endl; + } } /** diff --git a/device_core/utest/test_mq_topic.cpp b/device_core/utest/test_mq_topic.cpp new file mode 100644 index 0000000..07ff7df --- /dev/null +++ b/device_core/utest/test_mq_topic.cpp @@ -0,0 +1,72 @@ +#include +#include "mq_topic.h" + +using namespace NetworkManager; + + +TEST(test_MqTopic, test_constructor_string) +{ + const std::string name{"/parent/topic_mid/topic_top"}; + + MqTopic topic(name); + ASSERT_EQ(MqTopic::DEFAULT_MQ_BROKER_URI_ROOT + name, topic.getName()); +} + +TEST(test_MqTopic, test_constructor_iterator) +{ + const std::string t1 = "/parent"; + const std::string t2 = "/mid"; + const std::string t3 = "/high"; + const std::string t4 = "/top"; + MqTopic::Parts parts{t1, t2, t3, t4}; + + auto start = parts.begin(); + auto next = parts.begin(); + + ++next; + MqTopic topic1(start, next); + ASSERT_EQ(MqTopic::DEFAULT_MQ_BROKER_URI_ROOT + t1, topic1.getName()); + ASSERT_EQ(t1, topic1.getSubName()); + + ++next; + MqTopic topic2(start, next); + ASSERT_EQ(MqTopic::DEFAULT_MQ_BROKER_URI_ROOT + t1 + t2, topic2.getName()); + ASSERT_EQ(t1 + t2, topic2.getSubName()); + + ++next; + MqTopic topic3(start, next); + ASSERT_EQ(MqTopic::DEFAULT_MQ_BROKER_URI_ROOT + t1 + t2 + t3, topic3.getName()); + ASSERT_EQ(t1 + t2 + t3, topic3.getSubName()); + + ++next; + MqTopic topic4(start, next); + ASSERT_EQ(MqTopic::DEFAULT_MQ_BROKER_URI_ROOT + t1 + t2 + t3 + t4, topic4.getName()); + ASSERT_NE(MqTopic::DEFAULT_MQ_BROKER_URI_ROOT + t1 + t2 + t3 + t4, topic4.getSubName()); + ASSERT_EQ(t1 + t2 + t3 + t4, topic4.getSubName()); +} + +TEST(test_MqTopic, test_split) +{ + const std::string t1 = "/parent"; + const std::string t2 = "/mid"; + const std::string t3 = "/high"; + const std::string t4 = "/top"; + MqTopic::Parts parts{t1, t2, t3, t4}; + + const std::string ready = t1 + t2 + t3 + t4; + MqTopic topic(ready); + auto splitted = topic.split(); + + auto splitted_it = splitted.cbegin(); + auto parts_it = parts.cbegin(); + ASSERT_EQ(MqTopic::DEFAULT_MQ_BROKER_URI_ROOT, *splitted_it); + ++splitted_it; + + for (;parts_it != parts.cend(); ++parts_it, ++splitted_it) + { + ASSERT_NE(splitted_it, splitted.cend()); + EXPECT_EQ(*parts_it, *splitted_it); + } + + EXPECT_EQ(splitted.cend(), splitted_it); +} diff --git a/device_core/utest/test_rest.cpp b/device_core/utest/test_rest.cpp index 7f55e89..984b0f0 100644 --- a/device_core/utest/test_rest.cpp +++ b/device_core/utest/test_rest.cpp @@ -101,32 +101,6 @@ const TestDeviceRegInfo test_device = const std::string microsoft_agent_id{"3574462a-7efa-9449-4d9f-488734c79c0a"}; -std::string policy = R"-([ -{ - "group": "tv-extension", - "policies": - [ - { - "name": "bluetooth", - "state": 1, - "items": - [ - ] - }, - { - "name": "iptables", - "state": -1, - "items": - [ - "127.0.0.0/24|UDP|10-1024", - "1.1.1.1|TCP|80,443", - "4.4.4.4" - ] - } - ] -} -])-"; - } /** @@ -176,30 +150,21 @@ TEST(test_REST, test_policyUseCase) iotivity->getMqHandler()->publish("/srv/reg", rep); } - OC::OCRepresentation rep; - rep.setValue("duid", test_device.duid); - rep.setValue("agent_id", microsoft_agent_id); - rep.setValue("policy", policy); - - iotivity->getMqHandler()->publish("/srv/policy", rep); - std::this_thread::sleep_for(std::chrono::seconds(5)); - std::string received_policy = service.getPolicy(test_device.duid); - received_policy.erase(std::remove_if(received_policy.begin(), received_policy.end(), [](const char& c){ - return std::isspace(c); - }), received_policy.end()); - - policy.erase(std::remove_if(policy.begin(), policy.end(), [](const char& c){ - return std::isspace(c); - }), policy.end()); - - std::cout << std::endl << std::endl << policy << std::endl << std::endl << received_policy << std::endl; - ASSERT_FALSE(received_policy.empty()) << "Empty policyc received"; - ASSERT_NE(std::string::npos, received_policy.find(policy)) << "Sent and received policy differ"; + try + { + std::string received_policy = service.getPolicy(test_device.duid); + std::cout << received_policy << std::endl; + EXPECT_FALSE(received_policy.empty()) << "Empty policy received"; + } + catch(std::exception& e) + { + EXPECT_FALSE(true) << "Exception: " << e.what(); + } - rep.erase("policy"); - rep.erase("agent_id"); + OC::OCRepresentation rep; + rep.setValue("duid", test_device.duid); iotivity->getMqHandler()->publish("/srv/unreg", rep); } catch(std::exception& e) -- 2.7.4