From ca3cdcde950b28cbb756dbf663b62e80d0f604d2 Mon Sep 17 00:00:00 2001 From: parag-pv Date: Mon, 16 Sep 2024 15:11:24 -0400 Subject: [PATCH 01/11] Native sdk unit tests framework initial commit --- languages/cpp/src/shared/CMakeLists.txt | 55 +- .../cpp/src/shared/include/json_engine.h | 165 +++ languages/cpp/src/shared/src/CMakeLists.txt | 2 + .../src/shared/src/Transport/MockTransport.h | 1076 +++++++++++++++++ 4 files changed, 1296 insertions(+), 2 deletions(-) create mode 100644 languages/cpp/src/shared/include/json_engine.h create mode 100644 languages/cpp/src/shared/src/Transport/MockTransport.h diff --git a/languages/cpp/src/shared/CMakeLists.txt b/languages/cpp/src/shared/CMakeLists.txt index fe7a0085..711f298b 100644 --- a/languages/cpp/src/shared/CMakeLists.txt +++ b/languages/cpp/src/shared/CMakeLists.txt @@ -29,8 +29,57 @@ else () set(FIREBOLT_LIBRARY_TYPE SHARED) endif () +include(FetchContent) + +message("Fetching nlohmann json... ") +set(nlohmann_json_VERSION v3.11.3 CACHE STRING "Fetch nlohmann::json version") +FetchContent_Declare( + nlohmann_json + GIT_REPOSITORY https://github.com/nlohmann/json + GIT_TAG ${nlohmann_json_VERSION} +) +FetchContent_GetProperties(nlohmann_json) +if(NOT nlohmann_json) + FetchContent_Populate(nlohmann_json) + add_subdirectory(${nlohmann_json_SOURCE_DIR} ${nlohmann_json_BUILD_DIR}) +endif() +FetchContent_MakeAvailable(nlohmann_json) + +message("Fetching nlohmann json-schema-validator... ") +FetchContent_Declare( + nlohmann_json_schema_validator + GIT_REPOSITORY https://github.com/pboettch/json-schema-validator.git + GIT_TAG 2.3.0 +) +FetchContent_GetProperties(nlohmann_json_schema_validator) +if(NOT nlohmann_json_schema_validator) + FetchContent_Populate(nlohmann_json_schema_validator) + add_subdirectory(${nlohmann_json_schema_validator_SOURCE_DIR} ${nlohmann_json_schema_validator_BUILD_DIR}) +endif() +FetchContent_MakeAvailable(nlohmann_json_schema_validator) + +message("Fetching googletest... ") +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest + GIT_TAG v1.15.2 +) +FetchContent_GetProperties(googletest) +if(NOT googletest_POPULATED) + FetchContent_Populate(googletest) + add_subdirectory(${googletest_SOURCE_DIR} ${google_BUILD_DIR}) +endif() +FetchContent_MakeAvailable(googletest) + +include_directories( + ${nlohmann_json_SOURCE_DIR}/include + ${nlohmann_json_schema_validator_SOURCE_DIR}/src + ${googletest_SOURCE_DIR}/googletest/include + ${googletest_SOURCE_DIR}/googlemock/include +) + if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${SYSROOT_PATH}/usr" CACHE INTERNAL "" FORCE) + set(CMAKE_INSTALL_PREFIX ${SYSROOT_PATH}/usr CACHE INTERNAL "" FORCE) set(CMAKE_PREFIX_PATH ${SYSROOT_PATH}/usr/lib/cmake CACHE INTERNAL "" FORCE) endif() @@ -42,6 +91,8 @@ include(HelperFunctions) set(FIREBOLT_NAMESPACE ${PROJECT_NAME} CACHE STRING "Namespace of the project") +message("CMAKE_PREFIX_PATH: " ${CMAKE_PREFIX_PATH}) + find_package(WPEFramework CONFIG REQUIRED) add_subdirectory(src) @@ -53,4 +104,4 @@ endif() # make sure others can make use cmake settings of Firebolt OpenRPC configure_file( "${CMAKE_SOURCE_DIR}/cmake/project.cmake.in" "${CMAKE_BINARY_DIR}/${FIREBOLT_NAMESPACE}Config.cmake" - @ONLY) + @ONLY) \ No newline at end of file diff --git a/languages/cpp/src/shared/include/json_engine.h b/languages/cpp/src/shared/include/json_engine.h new file mode 100644 index 00000000..79ab589d --- /dev/null +++ b/languages/cpp/src/shared/include/json_engine.h @@ -0,0 +1,165 @@ +// #include +#include + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +#include +#include + +using nlohmann::json; +using nlohmann::json_schema::json_validator; +using namespace ::testing; + +#define REMOVE_QUOTES(s) (s.substr(1, s.length() - 2)) +#define STRING_TO_BOOL(s) (s == "true" ? true : false) + + +inline std::string capitalizeFirstChar(std::string str) { + if (!str.empty()) { + str[0] = std::toupper(str[0]); + } + return str; +} + + +class JsonEngine +{ + private: + std::fstream _file; + nlohmann::json _data; + + public: + + JsonEngine() + { + if (!_file.is_open()) + _file.open("../dist/firebolt-core-open-rpc.json"); + _file >> _data; + } + + ~JsonEngine(){ + if (_file.is_open()) + _file.close(); + } + + std::string get_value(const std::string& method_name) + { + for (const auto &method : _data["methods"]) + { + if (method.contains("name") && (method["name"] == method_name)) + { + auto value = method["examples"][0]["result"]["value"]; + return value.dump(); + } + } + return ""; + } + + + #ifndef UNIT_TEST + + // template + void MockRequest(const WPEFramework::Core::JSONRPC::Message* message) + { + std::cout << "Inside JSON engine MockRequest function" << std::endl; + std::string methodName = capitalizeFirstChar(message->Designator.Value().c_str()); + + /* TODO: Add a flag here that will be set to true if the method name is found in the rpc block, u + Use the flag to validate "Method not found" or other errors from SDK if applicable */ + for (const auto &method : _data["methods"]) + { + if (method.contains("name") && (method["name"] == methodName)) + { + // Method name validation + EXPECT_EQ(methodName, method["name"]); + + // ID Validation + // TODO: Check if id gets incremented by 1 for each request + std::cout << "MockRequest actual message.Id.Value(): " << message->Id.Value() << std::endl; + EXPECT_THAT(message->Id, AllOf(Ge(1),Le(std::numeric_limits::max()))); + + // Schema validation + const json requestParams = json::parse(message->Parameters.Value()); + std::cout << "Schema validator requestParams JSON: " << requestParams.dump() << std::endl; + if(method["params"].empty()) { + std::cout << "Params is empty" << std::endl; + EXPECT_EQ(requestParams, "{}"_json); + } + else { + std::cout << "Params is NOT empty" << std::endl; + const json openRPCSchema = method["params"][0]["schema"]; + std::cout << "Schema validator schema JSON: " << openRPCSchema.dump() << std::endl; + + json_validator validator; + try{ + validator.set_root_schema(openRPCSchema); + validator.validate(requestParams); + // EXPECT_NO_THROW(validator.validate(requestParams)); // For usage without try catch + std::cout << "Schema validation succeeded" << std::endl; + } + catch (const std::exception &e){ + FAIL() << "Schema validation error: " << e.what() << std::endl; + } + } + + // DUMMY SCHEMA VALIDATION - TO BE REMOVED + // const json openRPCSchema = R"( + // { + // "title": "AdConfigurationOptions", + // "type": "object", + // "properties": { + // "coppa": { + // "type": "boolean", + // "description": "Whether or not the app requires US COPPA compliance." + // }, + // "environment": { + // "type": "string", + // "enum": [ + // "prod", + // "test" + // ], + // "default": "prod", + // "description": "Whether the app is running in a production or test mode." + // }, + // "authenticationEntity": { + // "type": "string", + // "description": "The authentication provider, when it is separate entity than the app provider, e.g. an MVPD." + // } + // } + // })"_json; + // const json requestParams = json::parse(message->Parameters.Value()); + // // const json requestParams = R"({"options":{}})"_json; + // json_validator validator; + // try{ + // validator.set_root_schema(openRPCSchema); + // validator.validate(requestParams); + // // EXPECT_NO_THROW(validator.validate(requestParams)); // For usage without try catch + // std::cout << "Schema validation succeeded" << std::endl; + // } + // catch (const std::exception &e){ + // FAIL() << "Schema validation error: " << e.what() << std::endl; + // } + } + } + } + + template + Firebolt::Error MockResponse(WPEFramework::Core::JSONRPC::Message &message, RESPONSE &response) + { + std::cout << "Inside JSON engine MockResponse function" << std::endl; + std::string methodName = capitalizeFirstChar(message.Designator.Value().c_str()); + + // Loop through the methods to find the one with the given name + for (const auto &method : _data["methods"]) + { + if (method.contains("name") && (method["name"] == methodName)) + { + message.Result = method["examples"][0]["result"]["value"].dump(); + } + } + return Firebolt::Error::None; + } +#endif +}; + diff --git a/languages/cpp/src/shared/src/CMakeLists.txt b/languages/cpp/src/shared/src/CMakeLists.txt index 222ba35f..05b521e1 100644 --- a/languages/cpp/src/shared/src/CMakeLists.txt +++ b/languages/cpp/src/shared/src/CMakeLists.txt @@ -37,6 +37,8 @@ find_package(${NAMESPACE}WebSocket CONFIG REQUIRED) find_package(${NAMESPACE}WebSocket CONFIG REQUIRED) find_package(${NAMESPACE}Core CONFIG REQUIRED) +include_directories(${CMAKE_SOURCE_DIR}/build/${FIREBOLT_NAMESPACE}/usr/include/) + target_link_libraries(${TARGET} PUBLIC ${NAMESPACE}WebSocket::${NAMESPACE}WebSocket diff --git a/languages/cpp/src/shared/src/Transport/MockTransport.h b/languages/cpp/src/shared/src/Transport/MockTransport.h new file mode 100644 index 00000000..ff0d2c5b --- /dev/null +++ b/languages/cpp/src/shared/src/Transport/MockTransport.h @@ -0,0 +1,1076 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "Module.h" +#include "error.h" +#include "json_engine.h" + +// #define MY_DEBUG(message, value) +// #define MY_DEBUG(message, value) std::cout << "[MyDebug] " << __FILE__ << " "<< __func__ << "() " << message << ": "<< value << std::endl; + +namespace FireboltSDK +{ + + using namespace WPEFramework::Core::TypeTraits; + + template + class CommunicationChannel + { + public: + typedef std::function Callback; + class Entry + { + private: + Entry(const Entry &) = delete; + Entry &operator=(const Entry &rhs) = delete; + struct Synchronous + { + Synchronous() + : _signal(false, true), _response() + { + } + WPEFramework::Core::Event _signal; + std::list> _response; + }; + struct ASynchronous + { + ASynchronous(const uint32_t waitTime, const Callback &completed) + : _waitTime(WPEFramework::Core::Time::Now().Add(waitTime).Ticks()), _completed(completed) + { + } + uint64_t _waitTime; + Callback _completed; + }; + + public: + Entry() + : _synchronous(true), _info() + { + } + Entry(const uint32_t waitTime, const Callback &completed) + : _synchronous(false), _info(waitTime, completed) + { + } + ~Entry() + { + if (_synchronous == true) + { + _info.sync.~Synchronous(); + } + else + { + _info.async.~ASynchronous(); + } + } + + public: + const WPEFramework::Core::ProxyType &Response() const + { + return (*(_info.sync._response.begin())); + } + bool Signal(const WPEFramework::Core::ProxyType &response) + { + if (_synchronous == true) + { + _info.sync._response.push_back(response); + _info.sync._signal.SetEvent(); + } + else + { + _info.async._completed(*response); + } + + return (_synchronous == false); + } + const uint64_t &Expiry() const + { + return (_info.async._waitTime); + } + void Abort(const uint32_t id) + { + if (_synchronous == true) + { + _info.sync._signal.SetEvent(); + } + else + { + MESSAGETYPE message; + ToMessage(id, message, WPEFramework::Core::ERROR_ASYNC_ABORTED); + _info.async._completed(message); + } + } + bool Expired(const uint32_t id, const uint64_t ¤tTime, uint64_t &nextTime) + { + bool expired = false; + + if (_synchronous == false) + { + if (_info.async._waitTime > currentTime) + { + if (_info.async._waitTime < nextTime) + { + nextTime = _info.async._waitTime; + } + } + else + { + MESSAGETYPE message; + ToMessage(id, message, WPEFramework::Core::ERROR_TIMEDOUT); + _info.async._completed(message); + expired = true; + } + } + return (expired); + } + bool WaitForResponse(const uint32_t waitTime) + { + return (_info.sync._signal.Lock(waitTime) == WPEFramework::Core::ERROR_NONE); + } + + private: + void ToMessage(const uint32_t id, WPEFramework::Core::JSONRPC::Message &message, uint32_t error) + { + message.Id = id; + message.Error.Code = error; + switch (error) + { + case WPEFramework::Core::ERROR_ASYNC_ABORTED: + { + message.Error.Text = _T("Pending a-sync call has been aborted"); + break; + } + case WPEFramework::Core::ERROR_TIMEDOUT: + { + message.Error.Text = _T("Pending a-sync call has timed out"); + break; + } + } + } + + bool _synchronous; + union Info + { + public: + Info() + : sync() + { + } + Info(const uint32_t waitTime, const Callback &completed) + : async(waitTime, completed) + { + } + ~Info() + { + } + Synchronous sync; + ASynchronous async; + } _info; + }; + + private: + class FactoryImpl + { + private: + FactoryImpl(const FactoryImpl &) = delete; + FactoryImpl &operator=(const FactoryImpl &) = delete; + + class WatchDog + { + private: + WatchDog() = delete; + WatchDog &operator=(const WatchDog &) = delete; + + public: + WatchDog(CLIENT *client) + : _client(client) + { + } + WatchDog(const WatchDog ©) + : _client(copy._client) + { + } + ~WatchDog() + { + } + + bool operator==(const WatchDog &rhs) const + { + return (rhs._client == _client); + } + bool operator!=(const WatchDog &rhs) const + { + return (!operator==(rhs)); + } + + public: + uint64_t Timed(const uint64_t scheduledTime) + { + return (_client->Timed()); + } + + private: + CLIENT *_client; + }; + + friend WPEFramework::Core::SingletonType; + + FactoryImpl() + : _messageFactory(2), _watchDog(WPEFramework::Core::Thread::DefaultStackSize(), _T("TransportCleaner")) + { + } + + public: + static FactoryImpl &Instance() + { + return (WPEFramework::Core::SingletonType::Instance()); + } + + ~FactoryImpl() + { + } + + public: + WPEFramework::Core::ProxyType Element(const string &) + { + return (_messageFactory.Element()); + } + void Trigger(const uint64_t &time, CLIENT *client) + { + _watchDog.Trigger(time, client); + } + void Revoke(CLIENT *client) + { + _watchDog.Revoke(client); + } + + private: + WPEFramework::Core::ProxyPoolType _messageFactory; + WPEFramework::Core::TimerType _watchDog; + }; + + class ChannelImpl : public WPEFramework::Core::StreamJSONType, FactoryImpl &, INTERFACE> + { + private: + ChannelImpl(const ChannelImpl &) = delete; + ChannelImpl &operator=(const ChannelImpl &) = delete; + + typedef WPEFramework::Core::StreamJSONType, FactoryImpl &, INTERFACE> BaseClass; + + public: + ChannelImpl(CommunicationChannel *parent, const WPEFramework::Core::NodeId &remoteNode, const string &path, const string &query, const bool mask) + : BaseClass(5, FactoryImpl::Instance(), path, _T("JSON"), query, "", false, mask, false, remoteNode.AnyInterface(), remoteNode, 512, 512), _parent(*parent) + { + } + ~ChannelImpl() override = default; + + public: + void Received(WPEFramework::Core::ProxyType &response) override + { + WPEFramework::Core::ProxyType inbound(response); + + ASSERT(inbound.IsValid() == true); + if (inbound.IsValid() == true) + { + _parent.Inbound(inbound); + } + } + void Send(WPEFramework::Core::ProxyType &msg) override + { +#ifdef __DEBUG__ + string message; + ToMessage(msg, message); + TRACE_L1("Message: %s send", message.c_str()); +#endif + } + void StateChange() override + { + _parent.StateChange(); + } + bool IsIdle() const override + { + return (true); + } + + private: + void ToMessage(const WPEFramework::Core::ProxyType &jsonObject, string &message) const + { + WPEFramework::Core::ProxyType inbound(jsonObject); + + ASSERT(inbound.IsValid() == true); + if (inbound.IsValid() == true) + { + inbound->ToString(message); + } + } + void ToMessage(const WPEFramework::Core::ProxyType &jsonObject, string &message) const + { + WPEFramework::Core::ProxyType inbound(jsonObject); + + ASSERT(inbound.IsValid() == true); + if (inbound.IsValid() == true) + { + std::vector values; + inbound->ToBuffer(values); + if (values.empty() != true) + { + WPEFramework::Core::ToString(values.data(), static_cast(values.size()), false, message); + } + } + } + + private: + CommunicationChannel &_parent; + }; + + protected: + CommunicationChannel(const WPEFramework::Core::NodeId &remoteNode, const string &path, const string &query, const bool mask) + : _channel(this, remoteNode, path, query, mask), _sequence(0) + { + } + + public: + ~CommunicationChannel() = default; + static WPEFramework::Core::ProxyType Instance(const WPEFramework::Core::NodeId &remoteNode, const string &path, const string &query, const bool mask = true) + { + static WPEFramework::Core::ProxyMapType channelMap; + + string searchLine = remoteNode.HostAddress() + '@' + path; + + return (channelMap.template Instance(searchLine, remoteNode, path, query, mask)); + } + + public: + static void Trigger(const uint64_t &time, CLIENT *client) + { + FactoryImpl::Instance().Trigger(time, client); + } + static WPEFramework::Core::ProxyType Message() + { + return (FactoryImpl::Instance().Element(string())); + } + uint32_t Sequence() const + { + return (++_sequence); + } + void Register(CLIENT &client) + { + _adminLock.Lock(); + ASSERT(std::find(_observers.begin(), _observers.end(), &client) == _observers.end()); + _observers.push_back(&client); + if (true) + { + client.Opened(); + } + _adminLock.Unlock(); + } + void Unregister(CLIENT &client) + { + _adminLock.Lock(); + typename std::list::iterator index(std::find(_observers.begin(), _observers.end(), &client)); + if (index != _observers.end()) + { + _observers.erase(index); + } + FactoryImpl::Instance().Revoke(&client); + _adminLock.Unlock(); + } + +#ifdef UNIT_TEST + void Submit(const WPEFramework::Core::ProxyType &message) + { + std::cout << "Inside Transport Submit function 1" << std::endl; + _channel.Submit(message); + } +#else + void Submit(const WPEFramework::Core::ProxyType &message) + { + std::cout << "Inside Mock Transport Submit function 1" << std::endl; + const WPEFramework::Core::JSONRPC::Message* jsonRpcMessage = dynamic_cast(message.operator->()); + std::unique_ptr jsonEngine = std::make_unique(); + jsonEngine->MockRequest(jsonRpcMessage); + } +#endif + bool IsSuspended() const + { + return (_channel.IsSuspended()); + } + uint32_t Initialize() + { + return (Open(0)); + } + void Deinitialize() + { + Close(); + } + bool IsOpen() + { + // return (_channel.IsOpen() == true); + return true; + } + + protected: + void StateChange() + { + _adminLock.Lock(); + typename std::list::iterator index(_observers.begin()); + while (index != _observers.end()) + { + if (_channel.IsOpen() == true) + { + (*index)->Opened(); + } + else + { + (*index)->Closed(); + } + index++; + } + _adminLock.Unlock(); + } + bool Open(const uint32_t waitTime) + { + // bool result = true; + // if (_channel.IsClosed() == true) { + // result = (_channel.Open(waitTime) == WPEFramework::Core::ERROR_NONE); + // } + // return (result); + return true; + } + void Close() + { + _channel.Close(WPEFramework::Core::infinite); + } + + private: + int32_t Inbound(const WPEFramework::Core::ProxyType &inbound) + { + int32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; + _adminLock.Lock(); + typename std::list::iterator index(_observers.begin()); + while ((result != WPEFramework::Core::ERROR_NONE) && (index != _observers.end())) + { + result = (*index)->Submit(inbound); + index++; + } + _adminLock.Unlock(); + + return (result); + } + + private: + WPEFramework::Core::CriticalSection _adminLock; + ChannelImpl _channel; + mutable std::atomic _sequence; + std::list _observers; + }; + + class IEventHandler + { + public: + virtual Firebolt::Error ValidateResponse(const WPEFramework::Core::ProxyType &jsonResponse, bool &enabled) = 0; + virtual Firebolt::Error Dispatch(const string &eventName, const WPEFramework::Core::ProxyType &jsonResponse) = 0; + virtual ~IEventHandler() = default; + }; + + template + class Transport + { + private: + using Channel = CommunicationChannel; + using Entry = typename CommunicationChannel::Entry; + using PendingMap = std::unordered_map; + using EventMap = std::map; + typedef std::function &jsonResponse, bool &enabled)> EventResponseValidatioionFunction; + + class CommunicationJob : public WPEFramework::Core::IDispatch + { + protected: + CommunicationJob(const WPEFramework::Core::ProxyType &inbound, class Transport *parent) + : _inbound(inbound), _parent(parent) + { + } + + public: + CommunicationJob() = delete; + CommunicationJob(const CommunicationJob &) = delete; + CommunicationJob &operator=(const CommunicationJob &) = delete; + + ~CommunicationJob() = default; + + public: + static WPEFramework::Core::ProxyType Create(const WPEFramework::Core::ProxyType &inbound, class Transport *parent); + + void Dispatch() override + { + _parent->Inbound(_inbound); + } + + private: + const WPEFramework::Core::ProxyType _inbound; + class Transport *_parent; + }; + + class ConnectionJob : public WPEFramework::Core::IDispatch + { + protected: + ConnectionJob(class Transport *parent) + : _parent(parent) + { + } + + public: + ConnectionJob() = delete; + ConnectionJob(const ConnectionJob &) = delete; + ConnectionJob &operator=(const ConnectionJob &) = delete; + + ~ConnectionJob() = default; + + public: + static WPEFramework::Core::ProxyType Create(class Transport *parent); + + void Dispatch() override + { + if (Firebolt::Error::None != _parent->WaitForLinkReady()) + { + _parent->NotifyStatus(Firebolt::Error::Timedout); + } + } + + private: + const WPEFramework::Core::ProxyType _inbound; + class Transport *_parent; + }; + + protected: + static constexpr uint32_t DefaultWaitTime = 10000; + + inline void Announce() + { + _channel->Register(*this); + } + + private: + static constexpr const TCHAR *PathPrefix = _T("/"); + + public: + typedef std::function Listener; + + public: + Transport() = delete; + Transport(const Transport &) = delete; + Transport &operator=(Transport &) = delete; + Transport(const WPEFramework::Core::URL &url, const uint32_t waitTime, const Listener listener) + : _adminLock(), _connectId(WPEFramework::Core::NodeId(url.Host().Value().c_str(), url.Port().Value())), _channel(Channel::Instance(_connectId, ((url.Path().Value().rfind(PathPrefix, 0) == 0) ? url.Path().Value() : string(PathPrefix + url.Path().Value())), url.Query().Value(), true)), _eventHandler(nullptr), _pendingQueue(), _scheduledTime(0), _waitTime(waitTime), _listener(listener), _connected(false), _status(Firebolt::Error::NotConnected) + { + _channel->Register(*this); + WPEFramework::Core::ProxyType job = WPEFramework::Core::ProxyType(WPEFramework::Core::ProxyType::Create(this)); + WPEFramework::Core::IWorkerPool::Instance().Submit(job); + } + + virtual ~Transport() + { + _channel->Unregister(*this); + + for (auto &element : _pendingQueue) + { + element.second.Abort(element.first); + } + } + + public: + inline bool IsOpen() + { + // return _channel->IsOpen(); + return true; + } + + void Revoke(const string &eventName) + { + _adminLock.Lock(); + _eventMap.erase(eventName); + _adminLock.Unlock(); + } + + void SetEventHandler(IEventHandler *eventHandler) + { + _eventHandler = eventHandler; + } + +#ifdef UNIT_TEST + template + Firebolt::Error Invoke(const string& method, const PARAMETERS& parameters, RESPONSE& response) + { + std::cout << "Inside OG Transport Invoke function" << std::endl; + Entry slot; + uint32_t id = _channel->Sequence(); + Firebolt::Error result = Send(method, parameters, id); + if (result == Firebolt::Error::None) { + result = WaitForResponse(id, response, _waitTime); + } + + return (result); + } +#else + template + Firebolt::Error Invoke(const string &method, const PARAMETERS ¶meters, RESPONSE &response) + { + std::cout << "Inside Mock Transport Invoke function" << std::endl; + Entry slot; + uint32_t id = _channel->Sequence(); + std::cout << "Inside Mock Transport Invoke function - id: " << id << std::endl; + Firebolt::Error result = Send(method, parameters, id); + + WPEFramework::Core::JSONRPC::Message message; + message.Designator = method; + std::unique_ptr jsonEngine = std::make_unique(); + result = jsonEngine->MockResponse(message, response); + FromMessage((INTERFACE *)&response, message); + + // return Firebolt::Error::None; + return (result); + } +#endif + + template + Firebolt::Error InvokeAsync(const string &method, const PARAMETERS ¶meters, uint32_t &id) + { + Entry slot; + id = _channel->Sequence(); + return Send(method, parameters, id); + } + + template + Firebolt::Error WaitForResponse(const uint32_t& id, RESPONSE& response, const uint32_t waitTime) + { + int32_t result = WPEFramework::Core::ERROR_TIMEDOUT; + _adminLock.Lock(); + typename PendingMap::iterator index = _pendingQueue.find(id); + Entry& slot(index->second); + _adminLock.Unlock(); + + if (slot.WaitForResponse(waitTime) == true) { + WPEFramework::Core::ProxyType jsonResponse = slot.Response(); + + // See if we have a jsonResponse, maybe it was just the connection + // that closed? + if (jsonResponse.IsValid() == true) { + if (jsonResponse->Error.IsSet() == true) { + result = jsonResponse->Error.Code.Value(); + } + else { + result = WPEFramework::Core::ERROR_NONE; + if ((jsonResponse->Result.IsSet() == true) + && (jsonResponse->Result.Value().empty() == false)) { + FromMessage((INTERFACE*)&response, *jsonResponse); + } + } + } + } else { + result = WPEFramework::Core::ERROR_TIMEDOUT; + } + _adminLock.Lock(); + _pendingQueue.erase(id); + _adminLock.Unlock(); + return FireboltErrorValue(result); + } + + void Abort(uint32_t id) + { + _adminLock.Lock(); + typename PendingMap::iterator index = _pendingQueue.find(id); + Entry &slot(index->second); + _adminLock.Unlock(); + slot.Abort(id); + } + + template + Firebolt::Error Subscribe(const string &eventName, const string ¶meters, RESPONSE &response) + { + Entry slot; + uint32_t id = _channel->Sequence(); + Firebolt::Error result = Send(eventName, parameters, id); + if (result == Firebolt::Error::None) + { + _adminLock.Lock(); + _eventMap.emplace(std::piecewise_construct, + std::forward_as_tuple(eventName), + std::forward_as_tuple(~0)); + _adminLock.Unlock(); + + result = WaitForEventResponse(id, eventName, response, _waitTime); + } + + return (result); + } + + Firebolt::Error Unsubscribe(const string &eventName, const string ¶meters) + { + Revoke(eventName); + Entry slot; + uint32_t id = _channel->Sequence(); + + return Send(eventName, parameters, id); + } + + void NotifyStatus(Firebolt::Error status) + { + _listener(false, status); + } + + Firebolt::Error WaitForLinkReady() + { + uint32_t waiting = _waitTime; + static constexpr uint32_t SLEEPSLOT_TIME = 100; + + // Right, a wait till connection is closed is requested.. + while ((waiting > 0) && (IsOpen() == false) && (_status == Firebolt::Error::NotConnected)) + { + + uint32_t sleepSlot = (waiting > SLEEPSLOT_TIME ? SLEEPSLOT_TIME : waiting); + + // Right, lets sleep in slices of 100 ms + SleepMs(sleepSlot); + + waiting -= (waiting == WPEFramework::Core::infinite ? 0 : sleepSlot); + } + return (((waiting == 0) || (IsOpen() == true)) ? Firebolt::Error::None : Firebolt::Error::Timedout); + } + + private: + friend Channel; + inline bool IsEvent(const uint32_t id, string &eventName) + { + _adminLock.Lock(); + for (auto &event : _eventMap) + { + if (event.second == id) + { + eventName = event.first; + break; + } + } + _adminLock.Unlock(); + return (eventName.empty() != true); + } + uint64_t Timed() + { + uint64_t result = ~0; + uint64_t currentTime = WPEFramework::Core::Time::Now().Ticks(); + + // Lets see if some callback are expire. If so trigger and remove... + _adminLock.Lock(); + + typename PendingMap::iterator index = _pendingQueue.begin(); + + while (index != _pendingQueue.end()) + { + + if (index->second.Expired(index->first, currentTime, result) == true) + { + index = _pendingQueue.erase(index); + } + else + { + index++; + } + } + _scheduledTime = (result != static_cast(~0) ? result : 0); + + _adminLock.Unlock(); + + return (_scheduledTime); + } + + virtual void Opened() + { + _status = Firebolt::Error::None; + if (_connected != true) + { + _connected = true; + _listener(_connected, _status); + } + } + + void Closed() + { + // Abort any in progress RPC command: + _adminLock.Lock(); + + // See if we issued anything, if so abort it.. + while (_pendingQueue.size() != 0) + { + + _pendingQueue.begin()->second.Abort(_pendingQueue.begin()->first); + _pendingQueue.erase(_pendingQueue.begin()); + } + + _adminLock.Unlock(); + if (_connected != false) + { + _connected = false; + _listener(_connected, _status); + } + } + + int32_t Submit(const WPEFramework::Core::ProxyType &inbound) + { + std::cout << "Inside Transport Submit function 2" << std::endl; + int32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; + WPEFramework::Core::ProxyType job = WPEFramework::Core::ProxyType(WPEFramework::Core::ProxyType::Create(inbound, this)); + WPEFramework::Core::IWorkerPool::Instance().Submit(job); + return 0; + } + + int32_t Inbound(const WPEFramework::Core::ProxyType &inbound) + { + int32_t result = WPEFramework::Core::ERROR_INVALID_SIGNATURE; + + ASSERT(inbound.IsValid() == true); + + if ((inbound->Id.IsSet() == true) && (inbound->Result.IsSet() || inbound->Error.IsSet())) + { + // Looks like this is a response.. + ASSERT(inbound->Parameters.IsSet() == false); + ASSERT(inbound->Designator.IsSet() == false); + + _adminLock.Lock(); + + // See if we issued this.. + typename PendingMap::iterator index = _pendingQueue.find(inbound->Id.Value()); + + if (index != _pendingQueue.end()) + { + + if (index->second.Signal(inbound) == true) + { + _pendingQueue.erase(index); + } + + result = WPEFramework::Core::ERROR_NONE; + _adminLock.Unlock(); + } + else + { + _adminLock.Unlock(); + string eventName; + if (IsEvent(inbound->Id.Value(), eventName)) + { + _eventHandler->Dispatch(eventName, inbound); + } + } + } + + return (result); + } + + template + Firebolt::Error Send(const string &method, const PARAMETERS ¶meters, const uint32_t &id) + { + std::cout << "Inside Transport Send function" << std::endl; + int32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; + + if ((_channel.IsValid() == true) && (_channel->IsSuspended() == true)) + { + result = WPEFramework::Core::ERROR_ASYNC_FAILED; + } + else if (_channel.IsValid() == true) + { + + result = WPEFramework::Core::ERROR_ASYNC_FAILED; + + WPEFramework::Core::ProxyType message(Channel::Message()); + message->Id = id; + message->Designator = method; + ToMessage(parameters, message); + + _adminLock.Lock(); + + typename std::pair newElement = + _pendingQueue.emplace(std::piecewise_construct, + std::forward_as_tuple(id), + std::forward_as_tuple()); + ASSERT(newElement.second == true); + + if (newElement.second == true) + { + + _adminLock.Unlock(); + + _channel->Submit(WPEFramework::Core::ProxyType(message)); + + message.Release(); + result = WPEFramework::Core::ERROR_NONE; + } + } + return FireboltErrorValue(result); + } + + static constexpr uint32_t WAITSLOT_TIME = 100; + template + Firebolt::Error WaitForEventResponse(const uint32_t &id, const string &eventName, RESPONSE &response, const uint32_t waitTime) + { + Firebolt::Error result = Firebolt::Error::Timedout; + _adminLock.Lock(); + typename PendingMap::iterator index = _pendingQueue.find(id); + Entry &slot(index->second); + _adminLock.Unlock(); + + uint8_t waiting = waitTime; + do + { + uint32_t waitSlot = (waiting > WAITSLOT_TIME ? WAITSLOT_TIME : waiting); + if (slot.WaitForResponse(waitSlot) == true) + { + WPEFramework::Core::ProxyType jsonResponse = slot.Response(); + + // See if we have a jsonResponse, maybe it was just the connection + // that closed? + if (jsonResponse.IsValid() == true) + { + if (jsonResponse->Error.IsSet() == true) + { + result = FireboltErrorValue(jsonResponse->Error.Code.Value()); + } + else + { + if ((jsonResponse->Result.IsSet() == true) && (jsonResponse->Result.Value().empty() == false)) + { + bool enabled; + result = _eventHandler->ValidateResponse(jsonResponse, enabled); + if (result == Firebolt::Error::None) + { + FromMessage((INTERFACE *)&response, *jsonResponse); + if (enabled) + { + _adminLock.Lock(); + typename EventMap::iterator index = _eventMap.find(eventName); + if (index != _eventMap.end()) + { + index->second = id; + } + _adminLock.Unlock(); + } + } + } + } + } + } + else + { + result = Firebolt::Error::Timedout; + } + waiting -= (waiting == WPEFramework::Core::infinite ? 0 : waitSlot); + } while ((result != Firebolt::Error::None) && (waiting > 0)); + _adminLock.Lock(); + _pendingQueue.erase(id); + _adminLock.Unlock(); + + return result; + } + + public: + void FromMessage(WPEFramework::Core::JSON::IElement *response, const WPEFramework::Core::JSONRPC::Message &message) const + { + response->FromString(message.Result.Value()); + } + + void FromMessage(WPEFramework::Core::JSON::IMessagePack *response, const WPEFramework::Core::JSONRPC::Message &message) const + { + string value = message.Result.Value(); + std::vector result(value.begin(), value.end()); + response->FromBuffer(result); + } + + private: + void ToMessage(const string ¶meters, WPEFramework::Core::ProxyType &message) const + { + if (parameters.empty() != true) + { + message->Parameters = parameters; + } + } + + template + void ToMessage(PARAMETERS ¶meters, WPEFramework::Core::ProxyType &message) const + { + ToMessage((INTERFACE *)(¶meters), message); + return; + } + + void ToMessage(WPEFramework::Core::JSON::IMessagePack *parameters, WPEFramework::Core::ProxyType &message) const + { + std::vector values; + parameters->ToBuffer(values); + if (values.empty() != true) + { + string strValues(values.begin(), values.end()); + message->Parameters = strValues; + } + return; + } + + void ToMessage(WPEFramework::Core::JSON::IElement *parameters, WPEFramework::Core::ProxyType &message) const + { + string values; + parameters->ToString(values); + if (values.empty() != true) + { + message->Parameters = values; + } + return; + } + + Firebolt::Error FireboltErrorValue(const uint32_t error) + { + Firebolt::Error fireboltError = static_cast(error); + switch (error) + { + case WPEFramework::Core::ERROR_NONE: + fireboltError = Firebolt::Error::None; + break; + case WPEFramework::Core::ERROR_GENERAL: + case WPEFramework::Core::ERROR_UNAVAILABLE: + fireboltError = Firebolt::Error::General; + break; + case WPEFramework::Core::ERROR_TIMEDOUT: + fireboltError = Firebolt::Error::Timedout; + break; + default: + break; + } + + return fireboltError; + } + + private: + WPEFramework::Core::CriticalSection _adminLock; + WPEFramework::Core::NodeId _connectId; + WPEFramework::Core::ProxyType _channel; + IEventHandler *_eventHandler; + PendingMap _pendingQueue; + EventMap _eventMap; + uint64_t _scheduledTime; + uint32_t _waitTime; + Listener _listener; + bool _connected; + Firebolt::Error _status; + }; +} From 86d530041bfc90f542061fece562377f47252917 Mon Sep 17 00:00:00 2001 From: parag-pv Date: Wed, 18 Sep 2024 18:04:05 -0400 Subject: [PATCH 02/11] Fixing the buffer overflow in rpc dereferencing --- .../cpp/src/shared/include/json_engine.h | 124 +++++++++++------- 1 file changed, 76 insertions(+), 48 deletions(-) diff --git a/languages/cpp/src/shared/include/json_engine.h b/languages/cpp/src/shared/include/json_engine.h index 79ab589d..763a9b5d 100644 --- a/languages/cpp/src/shared/include/json_engine.h +++ b/languages/cpp/src/shared/include/json_engine.h @@ -1,4 +1,4 @@ -// #include +#include #include #include "gtest/gtest.h" @@ -26,16 +26,14 @@ inline std::string capitalizeFirstChar(std::string str) { class JsonEngine { private: - std::fstream _file; + std::ifstream _file; nlohmann::json _data; public: JsonEngine() { - if (!_file.is_open()) - _file.open("../dist/firebolt-core-open-rpc.json"); - _file >> _data; + _data = read_json_from_file("../firebolt-core-open-rpc.json"); } ~JsonEngine(){ @@ -56,6 +54,72 @@ class JsonEngine return ""; } + json read_json_from_file(const std::string &filename) + { + std::ifstream file(filename); + if (!file.is_open()) + { + throw std::runtime_error("Could not open file: " + filename); + } + + json j; + file >> j; + return j; + } + + json resolve_reference(const json &full_schema, const std::string &ref) + { + if (ref.find("#/") != 0) + { + throw std::invalid_argument("Only internal references supported"); + } + + std::string path = ref.substr(2); + std::istringstream ss(path); + std::string token; + json current = full_schema; + + while (std::getline(ss, token, '/')) + { + if (current.contains(token)) + { + current = current[token]; + } + else + { + throw std::invalid_argument("Invalid reference path: " + ref); + } + } + + return current; + } + + json process_schema(json schema, const json &full_schema) + { + if (schema.is_object()) + { + if (schema.contains("$ref")) + { + std::string ref = schema["$ref"]; + schema = resolve_reference(full_schema, ref); + } + + for (auto &el : schema.items()) + { + el.value() = process_schema(el.value(), full_schema); + } + } + else if (schema.is_array()) + { + for (auto &el : schema) + { + el = process_schema(el, full_schema); + } + } + + return schema; + } + #ifndef UNIT_TEST @@ -88,58 +152,22 @@ class JsonEngine } else { std::cout << "Params is NOT empty" << std::endl; - const json openRPCSchema = method["params"][0]["schema"]; - std::cout << "Schema validator schema JSON: " << openRPCSchema.dump() << std::endl; - + const json currentSchema = method["params"][0]["schema"]; + std::cout << "schema JSON before $ref: " << currentSchema.dump() << std::endl; + + json dereferenced_schema = process_schema(currentSchema, _data); + std::cout << "schema JSON after $ref: " << dereferenced_schema.dump() << std::endl; + json_validator validator; try{ - validator.set_root_schema(openRPCSchema); + validator.set_root_schema(dereferenced_schema); validator.validate(requestParams); - // EXPECT_NO_THROW(validator.validate(requestParams)); // For usage without try catch std::cout << "Schema validation succeeded" << std::endl; } catch (const std::exception &e){ FAIL() << "Schema validation error: " << e.what() << std::endl; } } - - // DUMMY SCHEMA VALIDATION - TO BE REMOVED - // const json openRPCSchema = R"( - // { - // "title": "AdConfigurationOptions", - // "type": "object", - // "properties": { - // "coppa": { - // "type": "boolean", - // "description": "Whether or not the app requires US COPPA compliance." - // }, - // "environment": { - // "type": "string", - // "enum": [ - // "prod", - // "test" - // ], - // "default": "prod", - // "description": "Whether the app is running in a production or test mode." - // }, - // "authenticationEntity": { - // "type": "string", - // "description": "The authentication provider, when it is separate entity than the app provider, e.g. an MVPD." - // } - // } - // })"_json; - // const json requestParams = json::parse(message->Parameters.Value()); - // // const json requestParams = R"({"options":{}})"_json; - // json_validator validator; - // try{ - // validator.set_root_schema(openRPCSchema); - // validator.validate(requestParams); - // // EXPECT_NO_THROW(validator.validate(requestParams)); // For usage without try catch - // std::cout << "Schema validation succeeded" << std::endl; - // } - // catch (const std::exception &e){ - // FAIL() << "Schema validation error: " << e.what() << std::endl; - // } } } } From 1e1752756b89eec4db19849590f679c1eaaeee8d Mon Sep 17 00:00:00 2001 From: parag-pv Date: Wed, 18 Sep 2024 21:54:37 -0400 Subject: [PATCH 03/11] Updated schema validator to loop through params in request --- .../cpp/src/shared/include/json_engine.h | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/languages/cpp/src/shared/include/json_engine.h b/languages/cpp/src/shared/include/json_engine.h index 763a9b5d..c851cd6b 100644 --- a/languages/cpp/src/shared/include/json_engine.h +++ b/languages/cpp/src/shared/include/json_engine.h @@ -152,20 +152,26 @@ class JsonEngine } else { std::cout << "Params is NOT empty" << std::endl; - const json currentSchema = method["params"][0]["schema"]; - std::cout << "schema JSON before $ref: " << currentSchema.dump() << std::endl; - - json dereferenced_schema = process_schema(currentSchema, _data); - std::cout << "schema JSON after $ref: " << dereferenced_schema.dump() << std::endl; - json_validator validator; - try{ - validator.set_root_schema(dereferenced_schema); - validator.validate(requestParams); - std::cout << "Schema validation succeeded" << std::endl; - } - catch (const std::exception &e){ - FAIL() << "Schema validation error: " << e.what() << std::endl; + const json openRPCParams = method["params"]; + for (auto& item : openRPCParams.items()) { + std::string key = item.key(); + json currentSchema = item.value(); + std::string paramName = currentSchema["name"]; + std::cout << "paramName: " << paramName << std::endl; + if (requestParams.contains(paramName)) { + std::cout << "RequestParams contain paramName in rpc" << std::endl; + json dereferenced_schema = process_schema(currentSchema, _data); + std::cout << "schema JSON after $ref: " << dereferenced_schema.dump() << std::endl; + try{ + validator.set_root_schema(dereferenced_schema["schema"]); + validator.validate(requestParams[paramName]); + std::cout << "Schema validation succeeded" << std::endl; + } + catch (const std::exception &e){ + FAIL() << "Schema validation error: " << e.what() << std::endl; + } + } } } } From d120d22c31f31dde6f1c55dcc8964866070e14b3 Mon Sep 17 00:00:00 2001 From: parag-pv Date: Sun, 22 Sep 2024 16:40:20 -0400 Subject: [PATCH 04/11] Removing commented code, adding ifdef conditions --- .../src/shared/src/Transport/MockTransport.h | 49 ++++++++++++------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/languages/cpp/src/shared/src/Transport/MockTransport.h b/languages/cpp/src/shared/src/Transport/MockTransport.h index ff0d2c5b..ec28c1ac 100644 --- a/languages/cpp/src/shared/src/Transport/MockTransport.h +++ b/languages/cpp/src/shared/src/Transport/MockTransport.h @@ -23,9 +23,6 @@ #include "error.h" #include "json_engine.h" -// #define MY_DEBUG(message, value) -// #define MY_DEBUG(message, value) std::cout << "[MyDebug] " << __FILE__ << " "<< __func__ << "() " << message << ": "<< value << std::endl; - namespace FireboltSDK { @@ -396,13 +393,11 @@ namespace FireboltSDK #ifdef UNIT_TEST void Submit(const WPEFramework::Core::ProxyType &message) { - std::cout << "Inside Transport Submit function 1" << std::endl; _channel.Submit(message); } #else void Submit(const WPEFramework::Core::ProxyType &message) { - std::cout << "Inside Mock Transport Submit function 1" << std::endl; const WPEFramework::Core::JSONRPC::Message* jsonRpcMessage = dynamic_cast(message.operator->()); std::unique_ptr jsonEngine = std::make_unique(); jsonEngine->MockRequest(jsonRpcMessage); @@ -420,12 +415,19 @@ namespace FireboltSDK { Close(); } + +#ifdef UNIT_TEST + bool IsOpen() + { + return (_channel.IsOpen() == true); + } +#else bool IsOpen() { - // return (_channel.IsOpen() == true); return true; } - +#endif + protected: void StateChange() { @@ -445,15 +447,22 @@ namespace FireboltSDK } _adminLock.Unlock(); } + +#ifdef UNIT_TEST + bool Open(const uint32_t waitTime) + { + bool result = true; + if (_channel.IsClosed() == true) { + result = (_channel.Open(waitTime) == WPEFramework::Core::ERROR_NONE); + } + return (result); + } +#else bool Open(const uint32_t waitTime) { - // bool result = true; - // if (_channel.IsClosed() == true) { - // result = (_channel.Open(waitTime) == WPEFramework::Core::ERROR_NONE); - // } - // return (result); return true; } +#endif void Close() { _channel.Close(WPEFramework::Core::infinite); @@ -596,11 +605,18 @@ namespace FireboltSDK } public: + +#ifdef UNIT_TEST + inline bool IsOpen() + { + return _channel->IsOpen(); + } +#else inline bool IsOpen() { - // return _channel->IsOpen(); return true; } +#endif void Revoke(const string &eventName) { @@ -618,7 +634,6 @@ namespace FireboltSDK template Firebolt::Error Invoke(const string& method, const PARAMETERS& parameters, RESPONSE& response) { - std::cout << "Inside OG Transport Invoke function" << std::endl; Entry slot; uint32_t id = _channel->Sequence(); Firebolt::Error result = Send(method, parameters, id); @@ -635,7 +650,6 @@ namespace FireboltSDK std::cout << "Inside Mock Transport Invoke function" << std::endl; Entry slot; uint32_t id = _channel->Sequence(); - std::cout << "Inside Mock Transport Invoke function - id: " << id << std::endl; Firebolt::Error result = Send(method, parameters, id); WPEFramework::Core::JSONRPC::Message message; @@ -643,8 +657,6 @@ namespace FireboltSDK std::unique_ptr jsonEngine = std::make_unique(); result = jsonEngine->MockResponse(message, response); FromMessage((INTERFACE *)&response, message); - - // return Firebolt::Error::None; return (result); } #endif @@ -884,7 +896,6 @@ namespace FireboltSDK template Firebolt::Error Send(const string &method, const PARAMETERS ¶meters, const uint32_t &id) { - std::cout << "Inside Transport Send function" << std::endl; int32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; if ((_channel.IsValid() == true) && (_channel->IsSuspended() == true)) @@ -1073,4 +1084,4 @@ namespace FireboltSDK bool _connected; Firebolt::Error _status; }; -} +} \ No newline at end of file From 74bea369acf915a8a9722031ee8c47173534c5e4 Mon Sep 17 00:00:00 2001 From: Keaton Sentak Date: Thu, 26 Sep 2024 11:55:30 -0400 Subject: [PATCH 05/11] feat: Update mock transport and cmakes --- languages/cpp/src/shared/CMakeLists.txt | 21 +++- languages/cpp/src/shared/src/CMakeLists.txt | 6 +- .../src/shared/src/Transport/MockTransport.h | 108 +++++++++------- languages/cpp/src/shared/test/CMakeLists.txt | 118 +++++++++++------- 4 files changed, 154 insertions(+), 99 deletions(-) diff --git a/languages/cpp/src/shared/CMakeLists.txt b/languages/cpp/src/shared/CMakeLists.txt index 711f298b..a60211ec 100644 --- a/languages/cpp/src/shared/CMakeLists.txt +++ b/languages/cpp/src/shared/CMakeLists.txt @@ -18,10 +18,15 @@ cmake_minimum_required(VERSION 3.3) project(Firebolt) +# set(CMAKE_VERBOSE_MAKEFILE ON) set(FIREBOLT_TRANSPORT_WAITTIME 1000 CACHE STRING "Maximum time to wait for Transport layer to get response") set(FIREBOLT_LOGLEVEL "Info" CACHE STRING "Log level to be enabled") + +# Default options option(FIREBOLT_ENABLE_STATIC_LIB "Create Firebolt library as Static library" OFF) -option(ENABLE_TESTS "Build openrpc native test" OFF) +option(ENABLE_TESTS "Build openrpc native test" ON) +option(ENABLE_UNIT_TESTS "Enable unit test" ON) +option(ENABLE_COVERAGE "Enable code coverage build." OFF) if (FIREBOLT_ENABLE_STATIC_LIB) set(FIREBOLT_LIBRARY_TYPE STATIC) @@ -78,10 +83,10 @@ include_directories( ${googletest_SOURCE_DIR}/googlemock/include ) -if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) +# if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) set(CMAKE_INSTALL_PREFIX ${SYSROOT_PATH}/usr CACHE INTERNAL "" FORCE) set(CMAKE_PREFIX_PATH ${SYSROOT_PATH}/usr/lib/cmake CACHE INTERNAL "" FORCE) -endif() +# endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" @@ -95,12 +100,20 @@ message("CMAKE_PREFIX_PATH: " ${CMAKE_PREFIX_PATH}) find_package(WPEFramework CONFIG REQUIRED) +if (ENABLE_TESTS AND ENABLE_COVERAGE) + include(CodeCoverage) + append_coverage_compiler_flags() +endif() + add_subdirectory(src) if (ENABLE_TESTS) - add_subdirectory(test) + enable_testing() + add_subdirectory(test) endif() + + # make sure others can make use cmake settings of Firebolt OpenRPC configure_file( "${CMAKE_SOURCE_DIR}/cmake/project.cmake.in" "${CMAKE_BINARY_DIR}/${FIREBOLT_NAMESPACE}Config.cmake" diff --git a/languages/cpp/src/shared/src/CMakeLists.txt b/languages/cpp/src/shared/src/CMakeLists.txt index 05b521e1..8bd961a5 100644 --- a/languages/cpp/src/shared/src/CMakeLists.txt +++ b/languages/cpp/src/shared/src/CMakeLists.txt @@ -36,14 +36,14 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) find_package(${NAMESPACE}WebSocket CONFIG REQUIRED) find_package(${NAMESPACE}WebSocket CONFIG REQUIRED) find_package(${NAMESPACE}Core CONFIG REQUIRED) - -include_directories(${CMAKE_SOURCE_DIR}/build/${FIREBOLT_NAMESPACE}/usr/include/) +find_package(${NAMESPACE}Cryptalgo CONFIG REQUIRED) target_link_libraries(${TARGET} PUBLIC ${NAMESPACE}WebSocket::${NAMESPACE}WebSocket ${NAMESPACE}Core::${NAMESPACE}Core ${NAMESPACE}WebSocket::${NAMESPACE}WebSocket + ${NAMESPACE}Cryptalgo::${NAMESPACE}Cryptalgo ) target_include_directories(${TARGET} @@ -78,4 +78,4 @@ install( InstallHeaders(TARGET ${TARGET} HEADERS . NAMESPACE ${FIREBOLT_NAMESPACE} DESTINATION ${FIREBOLT_NAMESPACE}SDK) InstallCMakeConfig(TARGETS ${TARGET}) -InstallPackageConfig(TARGETS ${TARGET} DESCRIPTION "Firebolt SDK Library") +InstallPackageConfig(TARGETS ${TARGET} DESCRIPTION "Firebolt SDK Library") \ No newline at end of file diff --git a/languages/cpp/src/shared/src/Transport/MockTransport.h b/languages/cpp/src/shared/src/Transport/MockTransport.h index ec28c1ac..dd12be69 100644 --- a/languages/cpp/src/shared/src/Transport/MockTransport.h +++ b/languages/cpp/src/shared/src/Transport/MockTransport.h @@ -618,10 +618,16 @@ namespace FireboltSDK } #endif - void Revoke(const string &eventName) + void Revoke(const string& eventName) { _adminLock.Lock(); - _eventMap.erase(eventName); + + // Remove from internal event map + _internalEventMap.erase(eventName); + + // Remove from external event map + _externalEventMap.erase(eventName); + _adminLock.Unlock(); } @@ -714,23 +720,30 @@ namespace FireboltSDK } template - Firebolt::Error Subscribe(const string &eventName, const string ¶meters, RESPONSE &response) + Firebolt::Error Subscribe(const string& eventName, const string& parameters, RESPONSE& response, bool updateInternal = false) { Entry slot; uint32_t id = _channel->Sequence(); Firebolt::Error result = Send(eventName, parameters, id); - if (result == Firebolt::Error::None) - { + + if (result == Firebolt::Error::None) { _adminLock.Lock(); - _eventMap.emplace(std::piecewise_construct, - std::forward_as_tuple(eventName), - std::forward_as_tuple(~0)); + + // Choose the map based on updateInternal flag + EventMap& eventMap = updateInternal ? _internalEventMap : _externalEventMap; + + // Add to the selected event map + eventMap.emplace(std::piecewise_construct, + std::forward_as_tuple(eventName), + std::forward_as_tuple(id)); + _adminLock.Unlock(); - result = WaitForEventResponse(id, eventName, response, _waitTime); + result = WaitForEventResponse(id, eventName, response, _waitTime, eventMap); + } - return (result); + return result; } Firebolt::Error Unsubscribe(const string &eventName, const string ¶meters) @@ -768,20 +781,33 @@ namespace FireboltSDK private: friend Channel; - inline bool IsEvent(const uint32_t id, string &eventName) + inline bool IsEvent(const uint32_t id, string& eventName) { _adminLock.Lock(); - for (auto &event : _eventMap) - { - if (event.second == id) - { - eventName = event.first; - break; + + bool eventExist = false; + + // List of maps to search + std::vector maps = {&_internalEventMap, &_externalEventMap}; + + // Loop through each map + for (const auto* map : maps) { + for (const auto& event : *map) { + if (event.second == id) { + eventName = event.first; + eventExist = true; + break; // Break the inner loop + } + } + if (eventExist) { + break; // Break the outer loop } } + _adminLock.Unlock(); - return (eventName.empty() != true); + return eventExist; } + uint64_t Timed() { uint64_t result = ~0; @@ -936,45 +962,36 @@ namespace FireboltSDK static constexpr uint32_t WAITSLOT_TIME = 100; template - Firebolt::Error WaitForEventResponse(const uint32_t &id, const string &eventName, RESPONSE &response, const uint32_t waitTime) + Firebolt::Error WaitForEventResponse(const uint32_t& id, const string& eventName, RESPONSE& response, const uint32_t waitTime, EventMap& _eventMap) { Firebolt::Error result = Firebolt::Error::Timedout; _adminLock.Lock(); typename PendingMap::iterator index = _pendingQueue.find(id); - Entry &slot(index->second); + Entry& slot(index->second); _adminLock.Unlock(); uint8_t waiting = waitTime; - do - { + do { uint32_t waitSlot = (waiting > WAITSLOT_TIME ? WAITSLOT_TIME : waiting); - if (slot.WaitForResponse(waitSlot) == true) - { - WPEFramework::Core::ProxyType jsonResponse = slot.Response(); + if (slot.WaitForResponse(waitSlot) == true) { + WPEFramework::Core::ProxyType jsonResponse = slot.Response(); // See if we have a jsonResponse, maybe it was just the connection // that closed? - if (jsonResponse.IsValid() == true) - { - if (jsonResponse->Error.IsSet() == true) - { + if (jsonResponse.IsValid() == true) { + if (jsonResponse->Error.IsSet() == true) { result = FireboltErrorValue(jsonResponse->Error.Code.Value()); - } - else - { - if ((jsonResponse->Result.IsSet() == true) && (jsonResponse->Result.Value().empty() == false)) - { + } else { + if ((jsonResponse->Result.IsSet() == true) + && (jsonResponse->Result.Value().empty() == false)) { bool enabled; result = _eventHandler->ValidateResponse(jsonResponse, enabled); - if (result == Firebolt::Error::None) - { - FromMessage((INTERFACE *)&response, *jsonResponse); - if (enabled) - { + if (result == Firebolt::Error::None) { + FromMessage((INTERFACE*)&response, *jsonResponse); + if (enabled) { _adminLock.Lock(); typename EventMap::iterator index = _eventMap.find(eventName); - if (index != _eventMap.end()) - { + if (index != _eventMap.end()) { index->second = id; } _adminLock.Unlock(); @@ -983,13 +1000,11 @@ namespace FireboltSDK } } } - } - else - { + } else { result = Firebolt::Error::Timedout; } waiting -= (waiting == WPEFramework::Core::infinite ? 0 : waitSlot); - } while ((result != Firebolt::Error::None) && (waiting > 0)); + } while ((result != Firebolt::Error::None) && (waiting > 0 )); _adminLock.Lock(); _pendingQueue.erase(id); _adminLock.Unlock(); @@ -1077,7 +1092,8 @@ namespace FireboltSDK WPEFramework::Core::ProxyType _channel; IEventHandler *_eventHandler; PendingMap _pendingQueue; - EventMap _eventMap; + EventMap _internalEventMap; + EventMap _externalEventMap; uint64_t _scheduledTime; uint32_t _waitTime; Listener _listener; diff --git a/languages/cpp/src/shared/test/CMakeLists.txt b/languages/cpp/src/shared/test/CMakeLists.txt index 012f1a6d..94923a79 100644 --- a/languages/cpp/src/shared/test/CMakeLists.txt +++ b/languages/cpp/src/shared/test/CMakeLists.txt @@ -16,64 +16,58 @@ cmake_minimum_required(VERSION 3.3) -project(FireboltSDKTests) -project_version(1.0.0) - -set(TESTLIB ${PROJECT_NAME}) - -message("Setup ${TESTLIB} v${PROJECT_VERSION}") - -set(CMAKE_POSITION_INDEPENDENT_CODE ON) +project(FireboltCoreSDKTests) + +if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${SYSROOT_PATH}/usr" CACHE INTERNAL "" FORCE) + set(CMAKE_PREFIX_PATH ${SYSROOT_PATH}/usr/lib/cmake CACHE INTERNAL "" FORCE) +endif() + +list(APPEND CMAKE_MODULE_PATH + "${SYSROOT_PATH}/usr/lib/cmake" + "${SYSROOT_PATH}/tools/cmake") +message("FIREBOLT_PATH inside cmake " ${FIREBOLT_PATH}) +if (FIREBOLT_PATH) + set(CMAKE_FIREBOLT_PATH + "${FIREBOLT_PATH}/usr/lib/cmake/Firebolt" + "${FIREBOLT_PATH}/usr/lib/cmake/FireboltSDK") + list(APPEND CMAKE_PREFIX_PATH ${CMAKE_FIREBOLT_PATH}) + list(APPEND CMAKE_MODULE_PATH ${CMAKE_FIREBOLT_PATH}) +else () + set(FIREBOLT_PATH "${SYSROOT_PATH}" CACHE INTERNAL "" FORCE) +endif () + +find_package(WPEFramework CONFIG REQUIRED) find_package(${NAMESPACE}Core CONFIG REQUIRED) -add_library(${TESTLIB} STATIC OpenRPCTests.cpp) - -target_link_libraries(${TESTLIB} - PUBLIC - ${NAMESPACE}Core::${NAMESPACE}Core - ${FIREBOLT_NAMESPACE}SDK -) - -target_include_directories(${TESTLIB} - PRIVATE - $ - $ -) - -set_target_properties(${TESTLIB} PROPERTIES - CXX_STANDARD 11 - CXX_STANDARD_REQUIRED YES - LINK_WHAT_YOU_USE TRUE - FRAMEWORK FALSE -) - -install( - TARGETS ${TESTLIB} EXPORT ${TESTLIB}Targets - LIBRARY DESTINATION lib COMPONENT libs - PUBLIC_HEADER DESTINATION include/${FIREBOLT_NAMESPACE}Test COMPONENT devel # headers for mac (note the different component -> different package) - INCLUDES DESTINATION include/${FIREBOLT_NAMESPACE}Test # headers -) - -InstallCMakeConfig(TARGETS ${TESTLIB}) -InstallCMakeConfigs(TARGET ${TESTLIB} DESTINATION ${FIREBOLT_NAMESPACE}) -InstallHeaders(TARGET ${TESTLIB} HEADERS . NAMESPACE ${FIREBOLT_NAMESPACE} DESTINATION FireboltTest) -InstallLibraries(TARGET ${TESTLIB} STATIC LIBRARIES ${TESTLIB} DESTINATION ${FIREBOLT_NAMESPACE}) - -set(TESTAPP "FireboltSDKTestApp") +set(TESTAPP TestFireboltCore) message("Setup ${TESTAPP}") -add_executable(${TESTAPP} Main.cpp) +add_executable(${TESTAPP} CoreSDKTest.cpp Main.cpp) target_link_libraries(${TESTAPP} PRIVATE - ${TESTLIB} + ${NAMESPACE}Core::${NAMESPACE}Core + ${FIREBOLT_NAMESPACE}SDK::${FIREBOLT_NAMESPACE}SDK + nlohmann_json_schema_validator + gtest_main ) target_include_directories(${TESTAPP} PRIVATE - $ - $ + $ +) + +if (POLYMORPHICS_REDUCER_METHODS) + target_compile_definitions(${TESTAPP} + PUBLIC + POLYMORPHICS_REDUCER_METHODS=1) +endif() + +set_target_properties(${TESTAPP} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES ) add_custom_command( @@ -84,3 +78,35 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/${TESTAPP} ${CMAKE_BINARY_DIR}/${FIREBOLT_NAMESPACE}/usr/bin ) +if(ENABLE_UNIT_TESTS) + set(UNIT_TESTS_APP FireboltCoreUnitTests) + + message("Setup ${UNIT_TESTS_APP}") + + add_definitions(-DUNIT_TEST) + + file(GLOB UNIT_TESTS "unit/*") + + add_executable(${UNIT_TESTS_APP} + CoreSDKTest.cpp + Unit.cpp + ${UNIT_TESTS} + ) + + link_directories(${CMAKE_SOURCE_DIR}/../../Thunder/install/usr/lib/) + target_link_libraries(${UNIT_TESTS_APP} + PRIVATE + ${NAMESPACE}Core::${NAMESPACE}Core + ${FIREBOLT_NAMESPACE}SDK::${FIREBOLT_NAMESPACE}SDK + nlohmann_json_schema_validator + gtest_main + ) + + target_include_directories(${UNIT_TESTS_APP} + PRIVATE + $ + ) + + include(GoogleTest) + gtest_discover_tests(${UNIT_TESTS_APP}) +endif() \ No newline at end of file From cb6ebd10ed453d36c7e1456c45bb942aabd96b04 Mon Sep 17 00:00:00 2001 From: Keaton Sentak Date: Fri, 27 Sep 2024 14:29:00 -0400 Subject: [PATCH 06/11] feat: Single file for transport logic --- .../cpp/src/shared/include/json_engine.h | 4 +- languages/cpp/src/shared/src/CMakeLists.txt | 4 + .../src/shared/src/Transport/MockTransport.h | 1103 ----------------- .../cpp/src/shared/src/Transport/Transport.h | 528 ++++---- 4 files changed, 320 insertions(+), 1319 deletions(-) delete mode 100644 languages/cpp/src/shared/src/Transport/MockTransport.h diff --git a/languages/cpp/src/shared/include/json_engine.h b/languages/cpp/src/shared/include/json_engine.h index c851cd6b..33433cc5 100644 --- a/languages/cpp/src/shared/include/json_engine.h +++ b/languages/cpp/src/shared/include/json_engine.h @@ -33,7 +33,7 @@ class JsonEngine JsonEngine() { - _data = read_json_from_file("../firebolt-core-open-rpc.json"); + _data = read_json_from_file("../../firebolt-core-open-rpc.json"); } ~JsonEngine(){ @@ -121,7 +121,7 @@ class JsonEngine } - #ifndef UNIT_TEST + #ifdef UNIT_TEST // template void MockRequest(const WPEFramework::Core::JSONRPC::Message* message) diff --git a/languages/cpp/src/shared/src/CMakeLists.txt b/languages/cpp/src/shared/src/CMakeLists.txt index 8bd961a5..1f116522 100644 --- a/languages/cpp/src/shared/src/CMakeLists.txt +++ b/languages/cpp/src/shared/src/CMakeLists.txt @@ -31,6 +31,10 @@ add_library(${TARGET} ${FIREBOLT_LIBRARY_TYPE} Async/Async.cpp ) +if(ENABLE_UNIT_TESTS) + target_compile_definitions(FireboltSDK PRIVATE UNIT_TEST) +endif() + set(CMAKE_POSITION_INDEPENDENT_CODE ON) find_package(${NAMESPACE}WebSocket CONFIG REQUIRED) diff --git a/languages/cpp/src/shared/src/Transport/MockTransport.h b/languages/cpp/src/shared/src/Transport/MockTransport.h deleted file mode 100644 index dd12be69..00000000 --- a/languages/cpp/src/shared/src/Transport/MockTransport.h +++ /dev/null @@ -1,1103 +0,0 @@ -/* - * Copyright 2023 Comcast Cable Communications Management, LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include "Module.h" -#include "error.h" -#include "json_engine.h" - -namespace FireboltSDK -{ - - using namespace WPEFramework::Core::TypeTraits; - - template - class CommunicationChannel - { - public: - typedef std::function Callback; - class Entry - { - private: - Entry(const Entry &) = delete; - Entry &operator=(const Entry &rhs) = delete; - struct Synchronous - { - Synchronous() - : _signal(false, true), _response() - { - } - WPEFramework::Core::Event _signal; - std::list> _response; - }; - struct ASynchronous - { - ASynchronous(const uint32_t waitTime, const Callback &completed) - : _waitTime(WPEFramework::Core::Time::Now().Add(waitTime).Ticks()), _completed(completed) - { - } - uint64_t _waitTime; - Callback _completed; - }; - - public: - Entry() - : _synchronous(true), _info() - { - } - Entry(const uint32_t waitTime, const Callback &completed) - : _synchronous(false), _info(waitTime, completed) - { - } - ~Entry() - { - if (_synchronous == true) - { - _info.sync.~Synchronous(); - } - else - { - _info.async.~ASynchronous(); - } - } - - public: - const WPEFramework::Core::ProxyType &Response() const - { - return (*(_info.sync._response.begin())); - } - bool Signal(const WPEFramework::Core::ProxyType &response) - { - if (_synchronous == true) - { - _info.sync._response.push_back(response); - _info.sync._signal.SetEvent(); - } - else - { - _info.async._completed(*response); - } - - return (_synchronous == false); - } - const uint64_t &Expiry() const - { - return (_info.async._waitTime); - } - void Abort(const uint32_t id) - { - if (_synchronous == true) - { - _info.sync._signal.SetEvent(); - } - else - { - MESSAGETYPE message; - ToMessage(id, message, WPEFramework::Core::ERROR_ASYNC_ABORTED); - _info.async._completed(message); - } - } - bool Expired(const uint32_t id, const uint64_t ¤tTime, uint64_t &nextTime) - { - bool expired = false; - - if (_synchronous == false) - { - if (_info.async._waitTime > currentTime) - { - if (_info.async._waitTime < nextTime) - { - nextTime = _info.async._waitTime; - } - } - else - { - MESSAGETYPE message; - ToMessage(id, message, WPEFramework::Core::ERROR_TIMEDOUT); - _info.async._completed(message); - expired = true; - } - } - return (expired); - } - bool WaitForResponse(const uint32_t waitTime) - { - return (_info.sync._signal.Lock(waitTime) == WPEFramework::Core::ERROR_NONE); - } - - private: - void ToMessage(const uint32_t id, WPEFramework::Core::JSONRPC::Message &message, uint32_t error) - { - message.Id = id; - message.Error.Code = error; - switch (error) - { - case WPEFramework::Core::ERROR_ASYNC_ABORTED: - { - message.Error.Text = _T("Pending a-sync call has been aborted"); - break; - } - case WPEFramework::Core::ERROR_TIMEDOUT: - { - message.Error.Text = _T("Pending a-sync call has timed out"); - break; - } - } - } - - bool _synchronous; - union Info - { - public: - Info() - : sync() - { - } - Info(const uint32_t waitTime, const Callback &completed) - : async(waitTime, completed) - { - } - ~Info() - { - } - Synchronous sync; - ASynchronous async; - } _info; - }; - - private: - class FactoryImpl - { - private: - FactoryImpl(const FactoryImpl &) = delete; - FactoryImpl &operator=(const FactoryImpl &) = delete; - - class WatchDog - { - private: - WatchDog() = delete; - WatchDog &operator=(const WatchDog &) = delete; - - public: - WatchDog(CLIENT *client) - : _client(client) - { - } - WatchDog(const WatchDog ©) - : _client(copy._client) - { - } - ~WatchDog() - { - } - - bool operator==(const WatchDog &rhs) const - { - return (rhs._client == _client); - } - bool operator!=(const WatchDog &rhs) const - { - return (!operator==(rhs)); - } - - public: - uint64_t Timed(const uint64_t scheduledTime) - { - return (_client->Timed()); - } - - private: - CLIENT *_client; - }; - - friend WPEFramework::Core::SingletonType; - - FactoryImpl() - : _messageFactory(2), _watchDog(WPEFramework::Core::Thread::DefaultStackSize(), _T("TransportCleaner")) - { - } - - public: - static FactoryImpl &Instance() - { - return (WPEFramework::Core::SingletonType::Instance()); - } - - ~FactoryImpl() - { - } - - public: - WPEFramework::Core::ProxyType Element(const string &) - { - return (_messageFactory.Element()); - } - void Trigger(const uint64_t &time, CLIENT *client) - { - _watchDog.Trigger(time, client); - } - void Revoke(CLIENT *client) - { - _watchDog.Revoke(client); - } - - private: - WPEFramework::Core::ProxyPoolType _messageFactory; - WPEFramework::Core::TimerType _watchDog; - }; - - class ChannelImpl : public WPEFramework::Core::StreamJSONType, FactoryImpl &, INTERFACE> - { - private: - ChannelImpl(const ChannelImpl &) = delete; - ChannelImpl &operator=(const ChannelImpl &) = delete; - - typedef WPEFramework::Core::StreamJSONType, FactoryImpl &, INTERFACE> BaseClass; - - public: - ChannelImpl(CommunicationChannel *parent, const WPEFramework::Core::NodeId &remoteNode, const string &path, const string &query, const bool mask) - : BaseClass(5, FactoryImpl::Instance(), path, _T("JSON"), query, "", false, mask, false, remoteNode.AnyInterface(), remoteNode, 512, 512), _parent(*parent) - { - } - ~ChannelImpl() override = default; - - public: - void Received(WPEFramework::Core::ProxyType &response) override - { - WPEFramework::Core::ProxyType inbound(response); - - ASSERT(inbound.IsValid() == true); - if (inbound.IsValid() == true) - { - _parent.Inbound(inbound); - } - } - void Send(WPEFramework::Core::ProxyType &msg) override - { -#ifdef __DEBUG__ - string message; - ToMessage(msg, message); - TRACE_L1("Message: %s send", message.c_str()); -#endif - } - void StateChange() override - { - _parent.StateChange(); - } - bool IsIdle() const override - { - return (true); - } - - private: - void ToMessage(const WPEFramework::Core::ProxyType &jsonObject, string &message) const - { - WPEFramework::Core::ProxyType inbound(jsonObject); - - ASSERT(inbound.IsValid() == true); - if (inbound.IsValid() == true) - { - inbound->ToString(message); - } - } - void ToMessage(const WPEFramework::Core::ProxyType &jsonObject, string &message) const - { - WPEFramework::Core::ProxyType inbound(jsonObject); - - ASSERT(inbound.IsValid() == true); - if (inbound.IsValid() == true) - { - std::vector values; - inbound->ToBuffer(values); - if (values.empty() != true) - { - WPEFramework::Core::ToString(values.data(), static_cast(values.size()), false, message); - } - } - } - - private: - CommunicationChannel &_parent; - }; - - protected: - CommunicationChannel(const WPEFramework::Core::NodeId &remoteNode, const string &path, const string &query, const bool mask) - : _channel(this, remoteNode, path, query, mask), _sequence(0) - { - } - - public: - ~CommunicationChannel() = default; - static WPEFramework::Core::ProxyType Instance(const WPEFramework::Core::NodeId &remoteNode, const string &path, const string &query, const bool mask = true) - { - static WPEFramework::Core::ProxyMapType channelMap; - - string searchLine = remoteNode.HostAddress() + '@' + path; - - return (channelMap.template Instance(searchLine, remoteNode, path, query, mask)); - } - - public: - static void Trigger(const uint64_t &time, CLIENT *client) - { - FactoryImpl::Instance().Trigger(time, client); - } - static WPEFramework::Core::ProxyType Message() - { - return (FactoryImpl::Instance().Element(string())); - } - uint32_t Sequence() const - { - return (++_sequence); - } - void Register(CLIENT &client) - { - _adminLock.Lock(); - ASSERT(std::find(_observers.begin(), _observers.end(), &client) == _observers.end()); - _observers.push_back(&client); - if (true) - { - client.Opened(); - } - _adminLock.Unlock(); - } - void Unregister(CLIENT &client) - { - _adminLock.Lock(); - typename std::list::iterator index(std::find(_observers.begin(), _observers.end(), &client)); - if (index != _observers.end()) - { - _observers.erase(index); - } - FactoryImpl::Instance().Revoke(&client); - _adminLock.Unlock(); - } - -#ifdef UNIT_TEST - void Submit(const WPEFramework::Core::ProxyType &message) - { - _channel.Submit(message); - } -#else - void Submit(const WPEFramework::Core::ProxyType &message) - { - const WPEFramework::Core::JSONRPC::Message* jsonRpcMessage = dynamic_cast(message.operator->()); - std::unique_ptr jsonEngine = std::make_unique(); - jsonEngine->MockRequest(jsonRpcMessage); - } -#endif - bool IsSuspended() const - { - return (_channel.IsSuspended()); - } - uint32_t Initialize() - { - return (Open(0)); - } - void Deinitialize() - { - Close(); - } - -#ifdef UNIT_TEST - bool IsOpen() - { - return (_channel.IsOpen() == true); - } -#else - bool IsOpen() - { - return true; - } -#endif - - protected: - void StateChange() - { - _adminLock.Lock(); - typename std::list::iterator index(_observers.begin()); - while (index != _observers.end()) - { - if (_channel.IsOpen() == true) - { - (*index)->Opened(); - } - else - { - (*index)->Closed(); - } - index++; - } - _adminLock.Unlock(); - } - -#ifdef UNIT_TEST - bool Open(const uint32_t waitTime) - { - bool result = true; - if (_channel.IsClosed() == true) { - result = (_channel.Open(waitTime) == WPEFramework::Core::ERROR_NONE); - } - return (result); - } -#else - bool Open(const uint32_t waitTime) - { - return true; - } -#endif - void Close() - { - _channel.Close(WPEFramework::Core::infinite); - } - - private: - int32_t Inbound(const WPEFramework::Core::ProxyType &inbound) - { - int32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; - _adminLock.Lock(); - typename std::list::iterator index(_observers.begin()); - while ((result != WPEFramework::Core::ERROR_NONE) && (index != _observers.end())) - { - result = (*index)->Submit(inbound); - index++; - } - _adminLock.Unlock(); - - return (result); - } - - private: - WPEFramework::Core::CriticalSection _adminLock; - ChannelImpl _channel; - mutable std::atomic _sequence; - std::list _observers; - }; - - class IEventHandler - { - public: - virtual Firebolt::Error ValidateResponse(const WPEFramework::Core::ProxyType &jsonResponse, bool &enabled) = 0; - virtual Firebolt::Error Dispatch(const string &eventName, const WPEFramework::Core::ProxyType &jsonResponse) = 0; - virtual ~IEventHandler() = default; - }; - - template - class Transport - { - private: - using Channel = CommunicationChannel; - using Entry = typename CommunicationChannel::Entry; - using PendingMap = std::unordered_map; - using EventMap = std::map; - typedef std::function &jsonResponse, bool &enabled)> EventResponseValidatioionFunction; - - class CommunicationJob : public WPEFramework::Core::IDispatch - { - protected: - CommunicationJob(const WPEFramework::Core::ProxyType &inbound, class Transport *parent) - : _inbound(inbound), _parent(parent) - { - } - - public: - CommunicationJob() = delete; - CommunicationJob(const CommunicationJob &) = delete; - CommunicationJob &operator=(const CommunicationJob &) = delete; - - ~CommunicationJob() = default; - - public: - static WPEFramework::Core::ProxyType Create(const WPEFramework::Core::ProxyType &inbound, class Transport *parent); - - void Dispatch() override - { - _parent->Inbound(_inbound); - } - - private: - const WPEFramework::Core::ProxyType _inbound; - class Transport *_parent; - }; - - class ConnectionJob : public WPEFramework::Core::IDispatch - { - protected: - ConnectionJob(class Transport *parent) - : _parent(parent) - { - } - - public: - ConnectionJob() = delete; - ConnectionJob(const ConnectionJob &) = delete; - ConnectionJob &operator=(const ConnectionJob &) = delete; - - ~ConnectionJob() = default; - - public: - static WPEFramework::Core::ProxyType Create(class Transport *parent); - - void Dispatch() override - { - if (Firebolt::Error::None != _parent->WaitForLinkReady()) - { - _parent->NotifyStatus(Firebolt::Error::Timedout); - } - } - - private: - const WPEFramework::Core::ProxyType _inbound; - class Transport *_parent; - }; - - protected: - static constexpr uint32_t DefaultWaitTime = 10000; - - inline void Announce() - { - _channel->Register(*this); - } - - private: - static constexpr const TCHAR *PathPrefix = _T("/"); - - public: - typedef std::function Listener; - - public: - Transport() = delete; - Transport(const Transport &) = delete; - Transport &operator=(Transport &) = delete; - Transport(const WPEFramework::Core::URL &url, const uint32_t waitTime, const Listener listener) - : _adminLock(), _connectId(WPEFramework::Core::NodeId(url.Host().Value().c_str(), url.Port().Value())), _channel(Channel::Instance(_connectId, ((url.Path().Value().rfind(PathPrefix, 0) == 0) ? url.Path().Value() : string(PathPrefix + url.Path().Value())), url.Query().Value(), true)), _eventHandler(nullptr), _pendingQueue(), _scheduledTime(0), _waitTime(waitTime), _listener(listener), _connected(false), _status(Firebolt::Error::NotConnected) - { - _channel->Register(*this); - WPEFramework::Core::ProxyType job = WPEFramework::Core::ProxyType(WPEFramework::Core::ProxyType::Create(this)); - WPEFramework::Core::IWorkerPool::Instance().Submit(job); - } - - virtual ~Transport() - { - _channel->Unregister(*this); - - for (auto &element : _pendingQueue) - { - element.second.Abort(element.first); - } - } - - public: - -#ifdef UNIT_TEST - inline bool IsOpen() - { - return _channel->IsOpen(); - } -#else - inline bool IsOpen() - { - return true; - } -#endif - - void Revoke(const string& eventName) - { - _adminLock.Lock(); - - // Remove from internal event map - _internalEventMap.erase(eventName); - - // Remove from external event map - _externalEventMap.erase(eventName); - - _adminLock.Unlock(); - } - - void SetEventHandler(IEventHandler *eventHandler) - { - _eventHandler = eventHandler; - } - -#ifdef UNIT_TEST - template - Firebolt::Error Invoke(const string& method, const PARAMETERS& parameters, RESPONSE& response) - { - Entry slot; - uint32_t id = _channel->Sequence(); - Firebolt::Error result = Send(method, parameters, id); - if (result == Firebolt::Error::None) { - result = WaitForResponse(id, response, _waitTime); - } - - return (result); - } -#else - template - Firebolt::Error Invoke(const string &method, const PARAMETERS ¶meters, RESPONSE &response) - { - std::cout << "Inside Mock Transport Invoke function" << std::endl; - Entry slot; - uint32_t id = _channel->Sequence(); - Firebolt::Error result = Send(method, parameters, id); - - WPEFramework::Core::JSONRPC::Message message; - message.Designator = method; - std::unique_ptr jsonEngine = std::make_unique(); - result = jsonEngine->MockResponse(message, response); - FromMessage((INTERFACE *)&response, message); - return (result); - } -#endif - - template - Firebolt::Error InvokeAsync(const string &method, const PARAMETERS ¶meters, uint32_t &id) - { - Entry slot; - id = _channel->Sequence(); - return Send(method, parameters, id); - } - - template - Firebolt::Error WaitForResponse(const uint32_t& id, RESPONSE& response, const uint32_t waitTime) - { - int32_t result = WPEFramework::Core::ERROR_TIMEDOUT; - _adminLock.Lock(); - typename PendingMap::iterator index = _pendingQueue.find(id); - Entry& slot(index->second); - _adminLock.Unlock(); - - if (slot.WaitForResponse(waitTime) == true) { - WPEFramework::Core::ProxyType jsonResponse = slot.Response(); - - // See if we have a jsonResponse, maybe it was just the connection - // that closed? - if (jsonResponse.IsValid() == true) { - if (jsonResponse->Error.IsSet() == true) { - result = jsonResponse->Error.Code.Value(); - } - else { - result = WPEFramework::Core::ERROR_NONE; - if ((jsonResponse->Result.IsSet() == true) - && (jsonResponse->Result.Value().empty() == false)) { - FromMessage((INTERFACE*)&response, *jsonResponse); - } - } - } - } else { - result = WPEFramework::Core::ERROR_TIMEDOUT; - } - _adminLock.Lock(); - _pendingQueue.erase(id); - _adminLock.Unlock(); - return FireboltErrorValue(result); - } - - void Abort(uint32_t id) - { - _adminLock.Lock(); - typename PendingMap::iterator index = _pendingQueue.find(id); - Entry &slot(index->second); - _adminLock.Unlock(); - slot.Abort(id); - } - - template - Firebolt::Error Subscribe(const string& eventName, const string& parameters, RESPONSE& response, bool updateInternal = false) - { - Entry slot; - uint32_t id = _channel->Sequence(); - Firebolt::Error result = Send(eventName, parameters, id); - - if (result == Firebolt::Error::None) { - _adminLock.Lock(); - - // Choose the map based on updateInternal flag - EventMap& eventMap = updateInternal ? _internalEventMap : _externalEventMap; - - // Add to the selected event map - eventMap.emplace(std::piecewise_construct, - std::forward_as_tuple(eventName), - std::forward_as_tuple(id)); - - _adminLock.Unlock(); - - result = WaitForEventResponse(id, eventName, response, _waitTime, eventMap); - - } - - return result; - } - - Firebolt::Error Unsubscribe(const string &eventName, const string ¶meters) - { - Revoke(eventName); - Entry slot; - uint32_t id = _channel->Sequence(); - - return Send(eventName, parameters, id); - } - - void NotifyStatus(Firebolt::Error status) - { - _listener(false, status); - } - - Firebolt::Error WaitForLinkReady() - { - uint32_t waiting = _waitTime; - static constexpr uint32_t SLEEPSLOT_TIME = 100; - - // Right, a wait till connection is closed is requested.. - while ((waiting > 0) && (IsOpen() == false) && (_status == Firebolt::Error::NotConnected)) - { - - uint32_t sleepSlot = (waiting > SLEEPSLOT_TIME ? SLEEPSLOT_TIME : waiting); - - // Right, lets sleep in slices of 100 ms - SleepMs(sleepSlot); - - waiting -= (waiting == WPEFramework::Core::infinite ? 0 : sleepSlot); - } - return (((waiting == 0) || (IsOpen() == true)) ? Firebolt::Error::None : Firebolt::Error::Timedout); - } - - private: - friend Channel; - inline bool IsEvent(const uint32_t id, string& eventName) - { - _adminLock.Lock(); - - bool eventExist = false; - - // List of maps to search - std::vector maps = {&_internalEventMap, &_externalEventMap}; - - // Loop through each map - for (const auto* map : maps) { - for (const auto& event : *map) { - if (event.second == id) { - eventName = event.first; - eventExist = true; - break; // Break the inner loop - } - } - if (eventExist) { - break; // Break the outer loop - } - } - - _adminLock.Unlock(); - return eventExist; - } - - uint64_t Timed() - { - uint64_t result = ~0; - uint64_t currentTime = WPEFramework::Core::Time::Now().Ticks(); - - // Lets see if some callback are expire. If so trigger and remove... - _adminLock.Lock(); - - typename PendingMap::iterator index = _pendingQueue.begin(); - - while (index != _pendingQueue.end()) - { - - if (index->second.Expired(index->first, currentTime, result) == true) - { - index = _pendingQueue.erase(index); - } - else - { - index++; - } - } - _scheduledTime = (result != static_cast(~0) ? result : 0); - - _adminLock.Unlock(); - - return (_scheduledTime); - } - - virtual void Opened() - { - _status = Firebolt::Error::None; - if (_connected != true) - { - _connected = true; - _listener(_connected, _status); - } - } - - void Closed() - { - // Abort any in progress RPC command: - _adminLock.Lock(); - - // See if we issued anything, if so abort it.. - while (_pendingQueue.size() != 0) - { - - _pendingQueue.begin()->second.Abort(_pendingQueue.begin()->first); - _pendingQueue.erase(_pendingQueue.begin()); - } - - _adminLock.Unlock(); - if (_connected != false) - { - _connected = false; - _listener(_connected, _status); - } - } - - int32_t Submit(const WPEFramework::Core::ProxyType &inbound) - { - std::cout << "Inside Transport Submit function 2" << std::endl; - int32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; - WPEFramework::Core::ProxyType job = WPEFramework::Core::ProxyType(WPEFramework::Core::ProxyType::Create(inbound, this)); - WPEFramework::Core::IWorkerPool::Instance().Submit(job); - return 0; - } - - int32_t Inbound(const WPEFramework::Core::ProxyType &inbound) - { - int32_t result = WPEFramework::Core::ERROR_INVALID_SIGNATURE; - - ASSERT(inbound.IsValid() == true); - - if ((inbound->Id.IsSet() == true) && (inbound->Result.IsSet() || inbound->Error.IsSet())) - { - // Looks like this is a response.. - ASSERT(inbound->Parameters.IsSet() == false); - ASSERT(inbound->Designator.IsSet() == false); - - _adminLock.Lock(); - - // See if we issued this.. - typename PendingMap::iterator index = _pendingQueue.find(inbound->Id.Value()); - - if (index != _pendingQueue.end()) - { - - if (index->second.Signal(inbound) == true) - { - _pendingQueue.erase(index); - } - - result = WPEFramework::Core::ERROR_NONE; - _adminLock.Unlock(); - } - else - { - _adminLock.Unlock(); - string eventName; - if (IsEvent(inbound->Id.Value(), eventName)) - { - _eventHandler->Dispatch(eventName, inbound); - } - } - } - - return (result); - } - - template - Firebolt::Error Send(const string &method, const PARAMETERS ¶meters, const uint32_t &id) - { - int32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; - - if ((_channel.IsValid() == true) && (_channel->IsSuspended() == true)) - { - result = WPEFramework::Core::ERROR_ASYNC_FAILED; - } - else if (_channel.IsValid() == true) - { - - result = WPEFramework::Core::ERROR_ASYNC_FAILED; - - WPEFramework::Core::ProxyType message(Channel::Message()); - message->Id = id; - message->Designator = method; - ToMessage(parameters, message); - - _adminLock.Lock(); - - typename std::pair newElement = - _pendingQueue.emplace(std::piecewise_construct, - std::forward_as_tuple(id), - std::forward_as_tuple()); - ASSERT(newElement.second == true); - - if (newElement.second == true) - { - - _adminLock.Unlock(); - - _channel->Submit(WPEFramework::Core::ProxyType(message)); - - message.Release(); - result = WPEFramework::Core::ERROR_NONE; - } - } - return FireboltErrorValue(result); - } - - static constexpr uint32_t WAITSLOT_TIME = 100; - template - Firebolt::Error WaitForEventResponse(const uint32_t& id, const string& eventName, RESPONSE& response, const uint32_t waitTime, EventMap& _eventMap) - { - Firebolt::Error result = Firebolt::Error::Timedout; - _adminLock.Lock(); - typename PendingMap::iterator index = _pendingQueue.find(id); - Entry& slot(index->second); - _adminLock.Unlock(); - - uint8_t waiting = waitTime; - do { - uint32_t waitSlot = (waiting > WAITSLOT_TIME ? WAITSLOT_TIME : waiting); - if (slot.WaitForResponse(waitSlot) == true) { - WPEFramework::Core::ProxyType jsonResponse = slot.Response(); - - // See if we have a jsonResponse, maybe it was just the connection - // that closed? - if (jsonResponse.IsValid() == true) { - if (jsonResponse->Error.IsSet() == true) { - result = FireboltErrorValue(jsonResponse->Error.Code.Value()); - } else { - if ((jsonResponse->Result.IsSet() == true) - && (jsonResponse->Result.Value().empty() == false)) { - bool enabled; - result = _eventHandler->ValidateResponse(jsonResponse, enabled); - if (result == Firebolt::Error::None) { - FromMessage((INTERFACE*)&response, *jsonResponse); - if (enabled) { - _adminLock.Lock(); - typename EventMap::iterator index = _eventMap.find(eventName); - if (index != _eventMap.end()) { - index->second = id; - } - _adminLock.Unlock(); - } - } - } - } - } - } else { - result = Firebolt::Error::Timedout; - } - waiting -= (waiting == WPEFramework::Core::infinite ? 0 : waitSlot); - } while ((result != Firebolt::Error::None) && (waiting > 0 )); - _adminLock.Lock(); - _pendingQueue.erase(id); - _adminLock.Unlock(); - - return result; - } - - public: - void FromMessage(WPEFramework::Core::JSON::IElement *response, const WPEFramework::Core::JSONRPC::Message &message) const - { - response->FromString(message.Result.Value()); - } - - void FromMessage(WPEFramework::Core::JSON::IMessagePack *response, const WPEFramework::Core::JSONRPC::Message &message) const - { - string value = message.Result.Value(); - std::vector result(value.begin(), value.end()); - response->FromBuffer(result); - } - - private: - void ToMessage(const string ¶meters, WPEFramework::Core::ProxyType &message) const - { - if (parameters.empty() != true) - { - message->Parameters = parameters; - } - } - - template - void ToMessage(PARAMETERS ¶meters, WPEFramework::Core::ProxyType &message) const - { - ToMessage((INTERFACE *)(¶meters), message); - return; - } - - void ToMessage(WPEFramework::Core::JSON::IMessagePack *parameters, WPEFramework::Core::ProxyType &message) const - { - std::vector values; - parameters->ToBuffer(values); - if (values.empty() != true) - { - string strValues(values.begin(), values.end()); - message->Parameters = strValues; - } - return; - } - - void ToMessage(WPEFramework::Core::JSON::IElement *parameters, WPEFramework::Core::ProxyType &message) const - { - string values; - parameters->ToString(values); - if (values.empty() != true) - { - message->Parameters = values; - } - return; - } - - Firebolt::Error FireboltErrorValue(const uint32_t error) - { - Firebolt::Error fireboltError = static_cast(error); - switch (error) - { - case WPEFramework::Core::ERROR_NONE: - fireboltError = Firebolt::Error::None; - break; - case WPEFramework::Core::ERROR_GENERAL: - case WPEFramework::Core::ERROR_UNAVAILABLE: - fireboltError = Firebolt::Error::General; - break; - case WPEFramework::Core::ERROR_TIMEDOUT: - fireboltError = Firebolt::Error::Timedout; - break; - default: - break; - } - - return fireboltError; - } - - private: - WPEFramework::Core::CriticalSection _adminLock; - WPEFramework::Core::NodeId _connectId; - WPEFramework::Core::ProxyType _channel; - IEventHandler *_eventHandler; - PendingMap _pendingQueue; - EventMap _internalEventMap; - EventMap _externalEventMap; - uint64_t _scheduledTime; - uint32_t _waitTime; - Listener _listener; - bool _connected; - Firebolt::Error _status; - }; -} \ No newline at end of file diff --git a/languages/cpp/src/shared/src/Transport/Transport.h b/languages/cpp/src/shared/src/Transport/Transport.h index 16205745..a0c61718 100644 --- a/languages/cpp/src/shared/src/Transport/Transport.h +++ b/languages/cpp/src/shared/src/Transport/Transport.h @@ -18,104 +18,117 @@ #pragma once +#include #include "Module.h" #include "error.h" +#include "json_engine.h" -namespace FireboltSDK { +namespace FireboltSDK +{ using namespace WPEFramework::Core::TypeTraits; - template - class CommunicationChannel { + template + class CommunicationChannel + { public: - typedef std::function Callback; - class Entry { + typedef std::function Callback; + class Entry + { private: - Entry(const Entry&) = delete; - Entry& operator=(const Entry& rhs) = delete; - struct Synchronous { + Entry(const Entry &) = delete; + Entry &operator=(const Entry &rhs) = delete; + struct Synchronous + { Synchronous() - : _signal(false, true) - , _response() + : _signal(false, true), _response() { } WPEFramework::Core::Event _signal; std::list> _response; }; - struct ASynchronous { - ASynchronous(const uint32_t waitTime, const Callback& completed) - : _waitTime(WPEFramework::Core::Time::Now().Add(waitTime).Ticks()) - , _completed(completed) + struct ASynchronous + { + ASynchronous(const uint32_t waitTime, const Callback &completed) + : _waitTime(WPEFramework::Core::Time::Now().Add(waitTime).Ticks()), _completed(completed) { } uint64_t _waitTime; Callback _completed; - }; + }; public: Entry() - : _synchronous(true) - , _info() + : _synchronous(true), _info() { } - Entry(const uint32_t waitTime, const Callback& completed) - : _synchronous(false) - , _info(waitTime, completed) + Entry(const uint32_t waitTime, const Callback &completed) + : _synchronous(false), _info(waitTime, completed) { } ~Entry() { - if (_synchronous == true) { + if (_synchronous == true) + { _info.sync.~Synchronous(); } - else { + else + { _info.async.~ASynchronous(); } } public: - const WPEFramework::Core::ProxyType& Response() const + const WPEFramework::Core::ProxyType &Response() const { return (*(_info.sync._response.begin())); } - bool Signal(const WPEFramework::Core::ProxyType& response) + bool Signal(const WPEFramework::Core::ProxyType &response) { - if (_synchronous == true) { + if (_synchronous == true) + { _info.sync._response.push_back(response); _info.sync._signal.SetEvent(); } - else { + else + { _info.async._completed(*response); } return (_synchronous == false); } - const uint64_t& Expiry() const + const uint64_t &Expiry() const { return (_info.async._waitTime); } void Abort(const uint32_t id) { - if (_synchronous == true) { + if (_synchronous == true) + { _info.sync._signal.SetEvent(); } - else { + else + { MESSAGETYPE message; ToMessage(id, message, WPEFramework::Core::ERROR_ASYNC_ABORTED); _info.async._completed(message); } } - bool Expired(const uint32_t id, const uint64_t& currentTime, uint64_t& nextTime) + bool Expired(const uint32_t id, const uint64_t ¤tTime, uint64_t &nextTime) { bool expired = false; - if (_synchronous == false) { - if (_info.async._waitTime > currentTime) { - if (_info.async._waitTime < nextTime) { + if (_synchronous == false) + { + if (_info.async._waitTime > currentTime) + { + if (_info.async._waitTime < nextTime) + { nextTime = _info.async._waitTime; } } - else { + else + { MESSAGETYPE message; ToMessage(id, message, WPEFramework::Core::ERROR_TIMEDOUT); _info.async._completed(message); @@ -130,30 +143,34 @@ namespace FireboltSDK { } private: - void ToMessage(const uint32_t id, WPEFramework::Core::JSONRPC::Message& message, uint32_t error) - { - message.Id = id; - message.Error.Code = error; - switch (error) { - case WPEFramework::Core::ERROR_ASYNC_ABORTED: { - message.Error.Text = _T("Pending a-sync call has been aborted"); - break; - } - case WPEFramework::Core::ERROR_TIMEDOUT: { - message.Error.Text = _T("Pending a-sync call has timed out"); - break; - } - } + void ToMessage(const uint32_t id, WPEFramework::Core::JSONRPC::Message &message, uint32_t error) + { + message.Id = id; + message.Error.Code = error; + switch (error) + { + case WPEFramework::Core::ERROR_ASYNC_ABORTED: + { + message.Error.Text = _T("Pending a-sync call has been aborted"); + break; + } + case WPEFramework::Core::ERROR_TIMEDOUT: + { + message.Error.Text = _T("Pending a-sync call has timed out"); + break; + } + } } bool _synchronous; - union Info { + union Info + { public: Info() : sync() { } - Info(const uint32_t waitTime, const Callback& completed) + Info(const uint32_t waitTime, const Callback &completed) : async(waitTime, completed) { } @@ -165,25 +182,25 @@ namespace FireboltSDK { } _info; }; - - private: - class FactoryImpl { + class FactoryImpl + { private: - FactoryImpl(const FactoryImpl&) = delete; - FactoryImpl& operator=(const FactoryImpl&) = delete; + FactoryImpl(const FactoryImpl &) = delete; + FactoryImpl &operator=(const FactoryImpl &) = delete; - class WatchDog { + class WatchDog + { private: WatchDog() = delete; - WatchDog& operator=(const WatchDog&) = delete; + WatchDog &operator=(const WatchDog &) = delete; public: - WatchDog(CLIENT* client) + WatchDog(CLIENT *client) : _client(client) { } - WatchDog(const WatchDog& copy) + WatchDog(const WatchDog ©) : _client(copy._client) { } @@ -191,34 +208,34 @@ namespace FireboltSDK { { } - bool operator==(const WatchDog& rhs) const + bool operator==(const WatchDog &rhs) const { return (rhs._client == _client); } - bool operator!=(const WatchDog& rhs) const + bool operator!=(const WatchDog &rhs) const { return (!operator==(rhs)); } public: - uint64_t Timed(const uint64_t scheduledTime) { + uint64_t Timed(const uint64_t scheduledTime) + { return (_client->Timed()); } private: - CLIENT* _client; + CLIENT *_client; }; friend WPEFramework::Core::SingletonType; FactoryImpl() - : _messageFactory(2) - , _watchDog(WPEFramework::Core::Thread::DefaultStackSize(), _T("TransportCleaner")) + : _messageFactory(2), _watchDog(WPEFramework::Core::Thread::DefaultStackSize(), _T("TransportCleaner")) { } public: - static FactoryImpl& Instance() + static FactoryImpl &Instance() { return (WPEFramework::Core::SingletonType::Instance()); } @@ -228,49 +245,51 @@ namespace FireboltSDK { } public: - WPEFramework::Core::ProxyType Element(const string&) + WPEFramework::Core::ProxyType Element(const string &) { return (_messageFactory.Element()); } - void Trigger(const uint64_t& time, CLIENT* client) + void Trigger(const uint64_t &time, CLIENT *client) { _watchDog.Trigger(time, client); } - void Revoke(CLIENT* client) + void Revoke(CLIENT *client) { _watchDog.Revoke(client); } + private: WPEFramework::Core::ProxyPoolType _messageFactory; WPEFramework::Core::TimerType _watchDog; }; - class ChannelImpl : public WPEFramework::Core::StreamJSONType, FactoryImpl&, INTERFACE> { + class ChannelImpl : public WPEFramework::Core::StreamJSONType, FactoryImpl &, INTERFACE> + { private: - ChannelImpl(const ChannelImpl&) = delete; - ChannelImpl& operator=(const ChannelImpl&) = delete; + ChannelImpl(const ChannelImpl &) = delete; + ChannelImpl &operator=(const ChannelImpl &) = delete; - typedef WPEFramework::Core::StreamJSONType, FactoryImpl&, INTERFACE> BaseClass; + typedef WPEFramework::Core::StreamJSONType, FactoryImpl &, INTERFACE> BaseClass; public: - ChannelImpl(CommunicationChannel* parent, const WPEFramework::Core::NodeId& remoteNode, const string& path, const string& query, const bool mask) - : BaseClass(5, FactoryImpl::Instance(), path, _T("JSON"), query, "", false, mask, false, remoteNode.AnyInterface(), remoteNode, 512, 512) - , _parent(*parent) + ChannelImpl(CommunicationChannel *parent, const WPEFramework::Core::NodeId &remoteNode, const string &path, const string &query, const bool mask) + : BaseClass(5, FactoryImpl::Instance(), path, _T("JSON"), query, "", false, mask, false, remoteNode.AnyInterface(), remoteNode, 512, 512), _parent(*parent) { } ~ChannelImpl() override = default; public: - void Received(WPEFramework::Core::ProxyType& response) override + void Received(WPEFramework::Core::ProxyType &response) override { WPEFramework::Core::ProxyType inbound(response); ASSERT(inbound.IsValid() == true); - if (inbound.IsValid() == true) { + if (inbound.IsValid() == true) + { _parent.Inbound(inbound); } } - void Send(WPEFramework::Core::ProxyType& msg) override + void Send(WPEFramework::Core::ProxyType &msg) override { #ifdef __DEBUG__ string message; @@ -288,43 +307,45 @@ namespace FireboltSDK { } private: - void ToMessage(const WPEFramework::Core::ProxyType& jsonObject, string& message) const + void ToMessage(const WPEFramework::Core::ProxyType &jsonObject, string &message) const { WPEFramework::Core::ProxyType inbound(jsonObject); ASSERT(inbound.IsValid() == true); - if (inbound.IsValid() == true) { + if (inbound.IsValid() == true) + { inbound->ToString(message); } } - void ToMessage(const WPEFramework::Core::ProxyType& jsonObject, string& message) const + void ToMessage(const WPEFramework::Core::ProxyType &jsonObject, string &message) const { WPEFramework::Core::ProxyType inbound(jsonObject); ASSERT(inbound.IsValid() == true); - if (inbound.IsValid() == true) { + if (inbound.IsValid() == true) + { std::vector values; inbound->ToBuffer(values); - if (values.empty() != true) { + if (values.empty() != true) + { WPEFramework::Core::ToString(values.data(), static_cast(values.size()), false, message); } } } private: - CommunicationChannel& _parent; + CommunicationChannel &_parent; }; protected: - CommunicationChannel(const WPEFramework::Core::NodeId& remoteNode, const string& path, const string& query, const bool mask) - : _channel(this, remoteNode, path, query, mask) - , _sequence(0) + CommunicationChannel(const WPEFramework::Core::NodeId &remoteNode, const string &path, const string &query, const bool mask) + : _channel(this, remoteNode, path, query, mask), _sequence(0) { } public: ~CommunicationChannel() = default; - static WPEFramework::Core::ProxyType Instance(const WPEFramework::Core::NodeId& remoteNode, const string& path, const string& query, const bool mask = true) + static WPEFramework::Core::ProxyType Instance(const WPEFramework::Core::NodeId &remoteNode, const string &path, const string &query, const bool mask = true) { static WPEFramework::Core::ProxyMapType channelMap; @@ -334,7 +355,7 @@ namespace FireboltSDK { } public: - static void Trigger(const uint64_t& time, CLIENT* client) + static void Trigger(const uint64_t &time, CLIENT *client) { FactoryImpl::Instance().Trigger(time, client); } @@ -346,31 +367,43 @@ namespace FireboltSDK { { return (++_sequence); } - void Register(CLIENT& client) + void Register(CLIENT &client) { _adminLock.Lock(); ASSERT(std::find(_observers.begin(), _observers.end(), &client) == _observers.end()); _observers.push_back(&client); - if (_channel.IsOpen() == true) { + if (true) + { client.Opened(); } _adminLock.Unlock(); } - void Unregister(CLIENT& client) + void Unregister(CLIENT &client) { _adminLock.Lock(); - typename std::list::iterator index(std::find(_observers.begin(), _observers.end(), &client)); - if (index != _observers.end()) { - _observers.erase(index); + typename std::list::iterator index(std::find(_observers.begin(), _observers.end(), &client)); + if (index != _observers.end()) + { + _observers.erase(index); } FactoryImpl::Instance().Revoke(&client); _adminLock.Unlock(); } - void Submit(const WPEFramework::Core::ProxyType& message) +#ifdef UNIT_TEST + void Submit(const WPEFramework::Core::ProxyType &message) + { + const WPEFramework::Core::JSONRPC::Message *jsonRpcMessage = dynamic_cast(message.operator->()); + std::unique_ptr jsonEngine = std::make_unique(); + jsonEngine->MockRequest(jsonRpcMessage); + } +#else + + void Submit(const WPEFramework::Core::ProxyType &message) { _channel.Submit(message); } +#endif bool IsSuspended() const { return (_channel.IsSuspended()); @@ -383,27 +416,40 @@ namespace FireboltSDK { { Close(); } + +#ifdef UNIT_TEST bool IsOpen() { return (_channel.IsOpen() == true); } - +#else + bool IsOpen() + { + return true; + } +#endif + protected: void StateChange() { _adminLock.Lock(); - typename std::list::iterator index(_observers.begin()); - while (index != _observers.end()) { - if (_channel.IsOpen() == true) { + typename std::list::iterator index(_observers.begin()); + while (index != _observers.end()) + { + if (_channel.IsOpen() == true) + { (*index)->Opened(); } - else { + else + { (*index)->Closed(); } index++; } _adminLock.Unlock(); } + +#ifdef UNIT_TEST bool Open(const uint32_t waitTime) { bool result = true; @@ -412,18 +458,25 @@ namespace FireboltSDK { } return (result); } +#else + bool Open(const uint32_t waitTime) + { + return true; + } +#endif void Close() { _channel.Close(WPEFramework::Core::infinite); } private: - int32_t Inbound(const WPEFramework::Core::ProxyType& inbound) + int32_t Inbound(const WPEFramework::Core::ProxyType &inbound) { int32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; _adminLock.Lock(); - typename std::list::iterator index(_observers.begin()); - while ((result != WPEFramework::Core::ERROR_NONE) && (index != _observers.end())) { + typename std::list::iterator index(_observers.begin()); + while ((result != WPEFramework::Core::ERROR_NONE) && (index != _observers.end())) + { result = (*index)->Submit(inbound); index++; } @@ -436,42 +489,44 @@ namespace FireboltSDK { WPEFramework::Core::CriticalSection _adminLock; ChannelImpl _channel; mutable std::atomic _sequence; - std::list _observers; + std::list _observers; }; - class IEventHandler { + class IEventHandler + { public: - virtual Firebolt::Error ValidateResponse(const WPEFramework::Core::ProxyType& jsonResponse, bool& enabled) = 0; - virtual Firebolt::Error Dispatch(const string& eventName, const WPEFramework::Core::ProxyType& jsonResponse) = 0; + virtual Firebolt::Error ValidateResponse(const WPEFramework::Core::ProxyType &jsonResponse, bool &enabled) = 0; + virtual Firebolt::Error Dispatch(const string &eventName, const WPEFramework::Core::ProxyType &jsonResponse) = 0; virtual ~IEventHandler() = default; }; - template - class Transport { + template + class Transport + { private: using Channel = CommunicationChannel; using Entry = typename CommunicationChannel::Entry; using PendingMap = std::unordered_map; using EventMap = std::map; - typedef std::function& jsonResponse, bool& enabled)> EventResponseValidatioionFunction; + typedef std::function &jsonResponse, bool &enabled)> EventResponseValidatioionFunction; - class CommunicationJob : public WPEFramework::Core::IDispatch { + class CommunicationJob : public WPEFramework::Core::IDispatch + { protected: - CommunicationJob(const WPEFramework::Core::ProxyType& inbound, class Transport* parent) - : _inbound(inbound) - , _parent(parent) + CommunicationJob(const WPEFramework::Core::ProxyType &inbound, class Transport *parent) + : _inbound(inbound), _parent(parent) { } - public: + public: CommunicationJob() = delete; - CommunicationJob(const CommunicationJob&) = delete; - CommunicationJob& operator=(const CommunicationJob&) = delete; + CommunicationJob(const CommunicationJob &) = delete; + CommunicationJob &operator=(const CommunicationJob &) = delete; ~CommunicationJob() = default; public: - static WPEFramework::Core::ProxyType Create(const WPEFramework::Core::ProxyType& inbound, class Transport* parent); + static WPEFramework::Core::ProxyType Create(const WPEFramework::Core::ProxyType &inbound, class Transport *parent); void Dispatch() override { @@ -480,67 +535,60 @@ namespace FireboltSDK { private: const WPEFramework::Core::ProxyType _inbound; - class Transport* _parent; + class Transport *_parent; }; - class ConnectionJob : public WPEFramework::Core::IDispatch { + class ConnectionJob : public WPEFramework::Core::IDispatch + { protected: - ConnectionJob(class Transport* parent) + ConnectionJob(class Transport *parent) : _parent(parent) { } - public: + public: ConnectionJob() = delete; - ConnectionJob(const ConnectionJob&) = delete; - ConnectionJob& operator=(const ConnectionJob&) = delete; + ConnectionJob(const ConnectionJob &) = delete; + ConnectionJob &operator=(const ConnectionJob &) = delete; ~ConnectionJob() = default; public: - static WPEFramework::Core::ProxyType Create(class Transport* parent); + static WPEFramework::Core::ProxyType Create(class Transport *parent); void Dispatch() override { - if (Firebolt::Error::None != _parent->WaitForLinkReady()) { + if (Firebolt::Error::None != _parent->WaitForLinkReady()) + { _parent->NotifyStatus(Firebolt::Error::Timedout); } } private: const WPEFramework::Core::ProxyType _inbound; - class Transport* _parent; + class Transport *_parent; }; - protected: static constexpr uint32_t DefaultWaitTime = 10000; - inline void Announce() { + inline void Announce() + { _channel->Register(*this); } private: - static constexpr const TCHAR* PathPrefix = _T("/"); + static constexpr const TCHAR *PathPrefix = _T("/"); public: typedef std::function Listener; public: Transport() = delete; - Transport(const Transport&) = delete; - Transport& operator=(Transport&) = delete; - Transport(const WPEFramework::Core::URL& url, const uint32_t waitTime, const Listener listener) - : _adminLock() - , _connectId(WPEFramework::Core::NodeId(url.Host().Value().c_str(), url.Port().Value())) - , _channel(Channel::Instance(_connectId, ((url.Path().Value().rfind(PathPrefix, 0) == 0) ? url.Path().Value() : string(PathPrefix + url.Path().Value())), url.Query().Value(), true)) - , _eventHandler(nullptr) - , _pendingQueue() - , _scheduledTime(0) - , _waitTime(waitTime) - , _listener(listener) - , _connected(false) - , _status(Firebolt::Error::NotConnected) + Transport(const Transport &) = delete; + Transport &operator=(Transport &) = delete; + Transport(const WPEFramework::Core::URL &url, const uint32_t waitTime, const Listener listener) + : _adminLock(), _connectId(WPEFramework::Core::NodeId(url.Host().Value().c_str(), url.Port().Value())), _channel(Channel::Instance(_connectId, ((url.Path().Value().rfind(PathPrefix, 0) == 0) ? url.Path().Value() : string(PathPrefix + url.Path().Value())), url.Query().Value(), true)), _eventHandler(nullptr), _pendingQueue(), _scheduledTime(0), _waitTime(waitTime), _listener(listener), _connected(false), _status(Firebolt::Error::NotConnected) { _channel->Register(*this); WPEFramework::Core::ProxyType job = WPEFramework::Core::ProxyType(WPEFramework::Core::ProxyType::Create(this)); @@ -551,38 +599,63 @@ namespace FireboltSDK { { _channel->Unregister(*this); - for (auto& element : _pendingQueue) { + for (auto &element : _pendingQueue) + { element.second.Abort(element.first); } } public: + +#ifdef UNIT_TEST inline bool IsOpen() { return _channel->IsOpen(); } +#else + inline bool IsOpen() + { + return true; + } +#endif - void Revoke(const string& eventName) + void Revoke(const string &eventName) { _adminLock.Lock(); - // Remove from internal event map _internalEventMap.erase(eventName); // Remove from external event map _externalEventMap.erase(eventName); - _adminLock.Unlock(); } - void SetEventHandler(IEventHandler* eventHandler) + void SetEventHandler(IEventHandler *eventHandler) { _eventHandler = eventHandler; } +#ifdef UNIT_TEST + template + Firebolt::Error Invoke(const string &method, const PARAMETERS ¶meters, RESPONSE &response) + { + std::cout << "Inside Mock Transport Invoke function" << std::endl; + Entry slot; + uint32_t id = _channel->Sequence(); + Firebolt::Error result = Send(method, parameters, id); + + WPEFramework::Core::JSONRPC::Message message; + message.Designator = method; + std::unique_ptr jsonEngine = std::make_unique(); + result = jsonEngine->MockResponse(message, response); + FromMessage((INTERFACE *)&response, message); + return (result); + } +#else template Firebolt::Error Invoke(const string& method, const PARAMETERS& parameters, RESPONSE& response) { + std::cout << "Inside Transport Invoke function" << std::endl; Entry slot; uint32_t id = _channel->Sequence(); Firebolt::Error result = Send(method, parameters, id); @@ -592,9 +665,10 @@ namespace FireboltSDK { return (result); } +#endif template - Firebolt::Error InvokeAsync(const string& method, const PARAMETERS& parameters, uint32_t& id) + Firebolt::Error InvokeAsync(const string &method, const PARAMETERS ¶meters, uint32_t &id) { Entry slot; id = _channel->Sequence(); @@ -640,13 +714,12 @@ namespace FireboltSDK { { _adminLock.Lock(); typename PendingMap::iterator index = _pendingQueue.find(id); - Entry& slot(index->second); + Entry &slot(index->second); _adminLock.Unlock(); slot.Abort(id); } template - Firebolt::Error Subscribe(const string& eventName, const string& parameters, RESPONSE& response, bool updateInternal = false) { Entry slot; @@ -673,7 +746,7 @@ namespace FireboltSDK { return result; } - Firebolt::Error Unsubscribe(const string& eventName, const string& parameters) + Firebolt::Error Unsubscribe(const string &eventName, const string ¶meters) { Revoke(eventName); Entry slot; @@ -693,7 +766,8 @@ namespace FireboltSDK { static constexpr uint32_t SLEEPSLOT_TIME = 100; // Right, a wait till connection is closed is requested.. - while ((waiting > 0) && (IsOpen() == false) && (_status == Firebolt::Error::NotConnected)) { + while ((waiting > 0) && (IsOpen() == false) && (_status == Firebolt::Error::NotConnected)) + { uint32_t sleepSlot = (waiting > SLEEPSLOT_TIME ? SLEEPSLOT_TIME : waiting); @@ -707,7 +781,6 @@ namespace FireboltSDK { private: friend Channel; - inline bool IsEvent(const uint32_t id, string& eventName) { _adminLock.Lock(); @@ -734,7 +807,6 @@ namespace FireboltSDK { _adminLock.Unlock(); return eventExist; } - uint64_t Timed() { uint64_t result = ~0; @@ -745,12 +817,15 @@ namespace FireboltSDK { typename PendingMap::iterator index = _pendingQueue.begin(); - while (index != _pendingQueue.end()) { + while (index != _pendingQueue.end()) + { - if (index->second.Expired(index->first, currentTime, result) == true) { + if (index->second.Expired(index->first, currentTime, result) == true) + { index = _pendingQueue.erase(index); } - else { + else + { index++; } } @@ -764,7 +839,8 @@ namespace FireboltSDK { virtual void Opened() { _status = Firebolt::Error::None; - if (_connected != true) { + if (_connected != true) + { _connected = true; _listener(_connected, _status); } @@ -776,34 +852,38 @@ namespace FireboltSDK { _adminLock.Lock(); // See if we issued anything, if so abort it.. - while (_pendingQueue.size() != 0) { + while (_pendingQueue.size() != 0) + { _pendingQueue.begin()->second.Abort(_pendingQueue.begin()->first); _pendingQueue.erase(_pendingQueue.begin()); } _adminLock.Unlock(); - if (_connected != false) { + if (_connected != false) + { _connected = false; _listener(_connected, _status); } } - int32_t Submit(const WPEFramework::Core::ProxyType& inbound) + int32_t Submit(const WPEFramework::Core::ProxyType &inbound) { + std::cout << "Inside Transport Submit function 2" << std::endl; int32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; WPEFramework::Core::ProxyType job = WPEFramework::Core::ProxyType(WPEFramework::Core::ProxyType::Create(inbound, this)); WPEFramework::Core::IWorkerPool::Instance().Submit(job); - return result; + return 0; } - int32_t Inbound(const WPEFramework::Core::ProxyType& inbound) + int32_t Inbound(const WPEFramework::Core::ProxyType &inbound) { int32_t result = WPEFramework::Core::ERROR_INVALID_SIGNATURE; ASSERT(inbound.IsValid() == true); - if ((inbound->Id.IsSet() == true) && (inbound->Result.IsSet() || inbound->Error.IsSet())) { + if ((inbound->Id.IsSet() == true) && (inbound->Result.IsSet() || inbound->Error.IsSet())) + { // Looks like this is a response.. ASSERT(inbound->Parameters.IsSet() == false); ASSERT(inbound->Designator.IsSet() == false); @@ -813,36 +893,42 @@ namespace FireboltSDK { // See if we issued this.. typename PendingMap::iterator index = _pendingQueue.find(inbound->Id.Value()); - if (index != _pendingQueue.end()) { + if (index != _pendingQueue.end()) + { - if (index->second.Signal(inbound) == true) { + if (index->second.Signal(inbound) == true) + { _pendingQueue.erase(index); } - result = WPEFramework::Core::ERROR_NONE; + result = WPEFramework::Core::ERROR_NONE; _adminLock.Unlock(); - } else { + } + else + { _adminLock.Unlock(); string eventName; - if (IsEvent(inbound->Id.Value(), eventName)) { + if (IsEvent(inbound->Id.Value(), eventName)) + { _eventHandler->Dispatch(eventName, inbound); } - } } return (result); } - + template - Firebolt::Error Send(const string& method, const PARAMETERS& parameters, const uint32_t& id) + Firebolt::Error Send(const string &method, const PARAMETERS ¶meters, const uint32_t &id) { int32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; - if ((_channel.IsValid() == true) && (_channel->IsSuspended() == true)) { + if ((_channel.IsValid() == true) && (_channel->IsSuspended() == true)) + { result = WPEFramework::Core::ERROR_ASYNC_FAILED; } - else if (_channel.IsValid() == true) { + else if (_channel.IsValid() == true) + { result = WPEFramework::Core::ERROR_ASYNC_FAILED; @@ -853,13 +939,14 @@ namespace FireboltSDK { _adminLock.Lock(); - typename std::pair< typename PendingMap::iterator, bool> newElement = - _pendingQueue.emplace(std::piecewise_construct, - std::forward_as_tuple(id), - std::forward_as_tuple()); + typename std::pair newElement = + _pendingQueue.emplace(std::piecewise_construct, + std::forward_as_tuple(id), + std::forward_as_tuple()); ASSERT(newElement.second == true); - if (newElement.second == true) { + if (newElement.second == true) + { _adminLock.Unlock(); @@ -874,36 +961,45 @@ namespace FireboltSDK { static constexpr uint32_t WAITSLOT_TIME = 100; template - Firebolt::Error WaitForEventResponse(const uint32_t& id, const string& eventName, RESPONSE& response, const uint32_t waitTime, EventMap& _eventMap) + Firebolt::Error WaitForEventResponse(const uint32_t &id, const string &eventName, RESPONSE &response, const uint32_t waitTime, EventMap& _eventMap) { Firebolt::Error result = Firebolt::Error::Timedout; _adminLock.Lock(); typename PendingMap::iterator index = _pendingQueue.find(id); - Entry& slot(index->second); + Entry &slot(index->second); _adminLock.Unlock(); uint8_t waiting = waitTime; - do { + do + { uint32_t waitSlot = (waiting > WAITSLOT_TIME ? WAITSLOT_TIME : waiting); - if (slot.WaitForResponse(waitSlot) == true) { - WPEFramework::Core::ProxyType jsonResponse = slot.Response(); + if (slot.WaitForResponse(waitSlot) == true) + { + WPEFramework::Core::ProxyType jsonResponse = slot.Response(); // See if we have a jsonResponse, maybe it was just the connection // that closed? - if (jsonResponse.IsValid() == true) { - if (jsonResponse->Error.IsSet() == true) { + if (jsonResponse.IsValid() == true) + { + if (jsonResponse->Error.IsSet() == true) + { result = FireboltErrorValue(jsonResponse->Error.Code.Value()); - } else { - if ((jsonResponse->Result.IsSet() == true) - && (jsonResponse->Result.Value().empty() == false)) { + } + else + { + if ((jsonResponse->Result.IsSet() == true) && (jsonResponse->Result.Value().empty() == false)) + { bool enabled; result = _eventHandler->ValidateResponse(jsonResponse, enabled); - if (result == Firebolt::Error::None) { - FromMessage((INTERFACE*)&response, *jsonResponse); - if (enabled) { + if (result == Firebolt::Error::None) + { + FromMessage((INTERFACE *)&response, *jsonResponse); + if (enabled) + { _adminLock.Lock(); typename EventMap::iterator index = _eventMap.find(eventName); - if (index != _eventMap.end()) { + if (index != _eventMap.end()) + { index->second = id; } _adminLock.Unlock(); @@ -912,11 +1008,13 @@ namespace FireboltSDK { } } } - } else { + } + else + { result = Firebolt::Error::Timedout; } waiting -= (waiting == WPEFramework::Core::infinite ? 0 : waitSlot); - } while ((result != Firebolt::Error::None) && (waiting > 0 )); + } while ((result != Firebolt::Error::None) && (waiting > 0)); _adminLock.Lock(); _pendingQueue.erase(id); _adminLock.Unlock(); @@ -925,51 +1023,52 @@ namespace FireboltSDK { } public: - void FromMessage(WPEFramework::Core::JSON::IElement* response, const WPEFramework::Core::JSONRPC::Message& message) const + void FromMessage(WPEFramework::Core::JSON::IElement *response, const WPEFramework::Core::JSONRPC::Message &message) const { response->FromString(message.Result.Value()); } - void FromMessage(WPEFramework::Core::JSON::IMessagePack* response, const WPEFramework::Core::JSONRPC::Message& message) const + void FromMessage(WPEFramework::Core::JSON::IMessagePack *response, const WPEFramework::Core::JSONRPC::Message &message) const { string value = message.Result.Value(); std::vector result(value.begin(), value.end()); response->FromBuffer(result); } - private: - - void ToMessage(const string& parameters, WPEFramework::Core::ProxyType& message) const + void ToMessage(const string ¶meters, WPEFramework::Core::ProxyType &message) const { - if (parameters.empty() != true) { + if (parameters.empty() != true) + { message->Parameters = parameters; } } template - void ToMessage(PARAMETERS& parameters, WPEFramework::Core::ProxyType& message) const + void ToMessage(PARAMETERS ¶meters, WPEFramework::Core::ProxyType &message) const { - ToMessage((INTERFACE*)(¶meters), message); + ToMessage((INTERFACE *)(¶meters), message); return; } - void ToMessage(WPEFramework::Core::JSON::IMessagePack* parameters, WPEFramework::Core::ProxyType& message) const + void ToMessage(WPEFramework::Core::JSON::IMessagePack *parameters, WPEFramework::Core::ProxyType &message) const { std::vector values; parameters->ToBuffer(values); - if (values.empty() != true) { + if (values.empty() != true) + { string strValues(values.begin(), values.end()); message->Parameters = strValues; } return; } - void ToMessage(WPEFramework::Core::JSON::IElement* parameters, WPEFramework::Core::ProxyType& message) const + void ToMessage(WPEFramework::Core::JSON::IElement *parameters, WPEFramework::Core::ProxyType &message) const { string values; parameters->ToString(values); - if (values.empty() != true) { + if (values.empty() != true) + { message->Parameters = values; } return; @@ -977,9 +1076,9 @@ namespace FireboltSDK { Firebolt::Error FireboltErrorValue(const uint32_t error) { - Firebolt::Error fireboltError = static_cast(error); - switch (error) { + switch (error) + { case WPEFramework::Core::ERROR_NONE: fireboltError = Firebolt::Error::None; break; @@ -1001,14 +1100,15 @@ namespace FireboltSDK { WPEFramework::Core::CriticalSection _adminLock; WPEFramework::Core::NodeId _connectId; WPEFramework::Core::ProxyType _channel; - IEventHandler* _eventHandler; + IEventHandler *_eventHandler; PendingMap _pendingQueue; EventMap _internalEventMap; EventMap _externalEventMap; + EventMap _eventMap; uint64_t _scheduledTime; uint32_t _waitTime; Listener _listener; bool _connected; Firebolt::Error _status; }; -} +} \ No newline at end of file From 1055cdcef4fb78b8bc94176508c249f7a8f5d6a7 Mon Sep 17 00:00:00 2001 From: Keaton Sentak Date: Fri, 27 Sep 2024 15:09:39 -0400 Subject: [PATCH 07/11] feat: Update transport for test app to run --- .../cpp/src/shared/src/Transport/Transport.h | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/languages/cpp/src/shared/src/Transport/Transport.h b/languages/cpp/src/shared/src/Transport/Transport.h index a0c61718..d7fcfe37 100644 --- a/languages/cpp/src/shared/src/Transport/Transport.h +++ b/languages/cpp/src/shared/src/Transport/Transport.h @@ -420,12 +420,13 @@ namespace FireboltSDK #ifdef UNIT_TEST bool IsOpen() { - return (_channel.IsOpen() == true); + return true; } + #else bool IsOpen() { - return true; + return (_channel.IsOpen() == true); } #endif @@ -450,6 +451,11 @@ namespace FireboltSDK } #ifdef UNIT_TEST + bool Open(const uint32_t waitTime) + { + return true; + } +#else bool Open(const uint32_t waitTime) { bool result = true; @@ -458,11 +464,7 @@ namespace FireboltSDK } return (result); } -#else - bool Open(const uint32_t waitTime) - { - return true; - } + #endif void Close() { @@ -610,13 +612,13 @@ namespace FireboltSDK #ifdef UNIT_TEST inline bool IsOpen() { - return _channel->IsOpen(); + return true; } #else inline bool IsOpen() { - return true; - } + return _channel->IsOpen(); + } #endif void Revoke(const string &eventName) From ca094001f3ba2274099bfcb2ef3acc7607210d59 Mon Sep 17 00:00:00 2001 From: parag-pv Date: Mon, 30 Sep 2024 11:03:34 -0400 Subject: [PATCH 08/11] Review fix: Removing log prints, adding comments --- languages/cpp/src/shared/include/json_engine.h | 10 +--------- languages/cpp/src/shared/src/Transport/Transport.h | 10 ++++++---- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/languages/cpp/src/shared/include/json_engine.h b/languages/cpp/src/shared/include/json_engine.h index 33433cc5..0bb98734 100644 --- a/languages/cpp/src/shared/include/json_engine.h +++ b/languages/cpp/src/shared/include/json_engine.h @@ -126,7 +126,6 @@ class JsonEngine // template void MockRequest(const WPEFramework::Core::JSONRPC::Message* message) { - std::cout << "Inside JSON engine MockRequest function" << std::endl; std::string methodName = capitalizeFirstChar(message->Designator.Value().c_str()); /* TODO: Add a flag here that will be set to true if the method name is found in the rpc block, u @@ -140,29 +139,23 @@ class JsonEngine // ID Validation // TODO: Check if id gets incremented by 1 for each request - std::cout << "MockRequest actual message.Id.Value(): " << message->Id.Value() << std::endl; EXPECT_THAT(message->Id, AllOf(Ge(1),Le(std::numeric_limits::max()))); // Schema validation const json requestParams = json::parse(message->Parameters.Value()); - std::cout << "Schema validator requestParams JSON: " << requestParams.dump() << std::endl; if(method["params"].empty()) { - std::cout << "Params is empty" << std::endl; + std::cout << "Schema validation for empty parameters" << std::endl; EXPECT_EQ(requestParams, "{}"_json); } else { - std::cout << "Params is NOT empty" << std::endl; json_validator validator; const json openRPCParams = method["params"]; for (auto& item : openRPCParams.items()) { std::string key = item.key(); json currentSchema = item.value(); std::string paramName = currentSchema["name"]; - std::cout << "paramName: " << paramName << std::endl; if (requestParams.contains(paramName)) { - std::cout << "RequestParams contain paramName in rpc" << std::endl; json dereferenced_schema = process_schema(currentSchema, _data); - std::cout << "schema JSON after $ref: " << dereferenced_schema.dump() << std::endl; try{ validator.set_root_schema(dereferenced_schema["schema"]); validator.validate(requestParams[paramName]); @@ -181,7 +174,6 @@ class JsonEngine template Firebolt::Error MockResponse(WPEFramework::Core::JSONRPC::Message &message, RESPONSE &response) { - std::cout << "Inside JSON engine MockResponse function" << std::endl; std::string methodName = capitalizeFirstChar(message.Designator.Value().c_str()); // Loop through the methods to find the one with the given name diff --git a/languages/cpp/src/shared/src/Transport/Transport.h b/languages/cpp/src/shared/src/Transport/Transport.h index d7fcfe37..3fe0cbc9 100644 --- a/languages/cpp/src/shared/src/Transport/Transport.h +++ b/languages/cpp/src/shared/src/Transport/Transport.h @@ -390,6 +390,7 @@ namespace FireboltSDK _adminLock.Unlock(); } +// Send requests to JSON engine's mockRequest method for unit testing instead of channel's submit method #ifdef UNIT_TEST void Submit(const WPEFramework::Core::ProxyType &message) { @@ -417,6 +418,7 @@ namespace FireboltSDK Close(); } +// Always return true for unit testing #ifdef UNIT_TEST bool IsOpen() { @@ -449,7 +451,8 @@ namespace FireboltSDK } _adminLock.Unlock(); } - + +// Always return true for unit testing #ifdef UNIT_TEST bool Open(const uint32_t waitTime) { @@ -609,6 +612,7 @@ namespace FireboltSDK public: +// Always return true for unit testing #ifdef UNIT_TEST inline bool IsOpen() { @@ -637,11 +641,11 @@ namespace FireboltSDK _eventHandler = eventHandler; } +// Invoke method is overriden for unit testing to call MockResponse method from JSON engine #ifdef UNIT_TEST template Firebolt::Error Invoke(const string &method, const PARAMETERS ¶meters, RESPONSE &response) { - std::cout << "Inside Mock Transport Invoke function" << std::endl; Entry slot; uint32_t id = _channel->Sequence(); Firebolt::Error result = Send(method, parameters, id); @@ -657,7 +661,6 @@ namespace FireboltSDK template Firebolt::Error Invoke(const string& method, const PARAMETERS& parameters, RESPONSE& response) { - std::cout << "Inside Transport Invoke function" << std::endl; Entry slot; uint32_t id = _channel->Sequence(); Firebolt::Error result = Send(method, parameters, id); @@ -871,7 +874,6 @@ namespace FireboltSDK int32_t Submit(const WPEFramework::Core::ProxyType &inbound) { - std::cout << "Inside Transport Submit function 2" << std::endl; int32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; WPEFramework::Core::ProxyType job = WPEFramework::Core::ProxyType(WPEFramework::Core::ProxyType::Create(inbound, this)); WPEFramework::Core::IWorkerPool::Instance().Submit(job); From 3704c1d9fe42d6aba148109dd6380df8c2943b9d Mon Sep 17 00:00:00 2001 From: parag-pv Date: Wed, 2 Oct 2024 16:00:53 -0400 Subject: [PATCH 09/11] Native SDK events unit tests initial commit --- .../cpp/src/shared/src/Transport/Transport.h | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/languages/cpp/src/shared/src/Transport/Transport.h b/languages/cpp/src/shared/src/Transport/Transport.h index 3fe0cbc9..d70a6714 100644 --- a/languages/cpp/src/shared/src/Transport/Transport.h +++ b/languages/cpp/src/shared/src/Transport/Transport.h @@ -962,11 +962,25 @@ namespace FireboltSDK } return FireboltErrorValue(result); } - +#ifdef UNIT_TEST +template + Firebolt::Error WaitForEventResponse(const uint32_t &id, const string &eventName, RESPONSE &response, const uint32_t waitTime, EventMap& _eventMap) + { + std::cout << "Inside Mock Transport WaitForEventResponse function" << std::endl; + std::cout << "Mock Transport WaitForEventResponse eventName: " << eventName << std::endl; + /* We could optionally return a response inside WaitForEventResponse using JSON engine's mockResponse + pointing to #/x-schemas/Types/ListenResponse dereferenced to "{listening:true}". + Since there is no return value for event subscription, error would be the only validation for now. + Ideally, the event response should be returned here after the event is triggered. + */ + return Firebolt::Error::None; + } +#else static constexpr uint32_t WAITSLOT_TIME = 100; template Firebolt::Error WaitForEventResponse(const uint32_t &id, const string &eventName, RESPONSE &response, const uint32_t waitTime, EventMap& _eventMap) { + std::cout << "Inside Transport WaitForEventResponse function" << std::endl; Firebolt::Error result = Firebolt::Error::Timedout; _adminLock.Lock(); typename PendingMap::iterator index = _pendingQueue.find(id); @@ -1025,7 +1039,7 @@ namespace FireboltSDK return result; } - +#endif public: void FromMessage(WPEFramework::Core::JSON::IElement *response, const WPEFramework::Core::JSONRPC::Message &message) const { From f02d05aa038b7108b115f3af35a51b62fefb69e2 Mon Sep 17 00:00:00 2001 From: parag-pv Date: Thu, 3 Oct 2024 14:22:52 -0400 Subject: [PATCH 10/11] Comment changes --- languages/cpp/src/shared/src/Transport/Transport.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/languages/cpp/src/shared/src/Transport/Transport.h b/languages/cpp/src/shared/src/Transport/Transport.h index d70a6714..f7a77b23 100644 --- a/languages/cpp/src/shared/src/Transport/Transport.h +++ b/languages/cpp/src/shared/src/Transport/Transport.h @@ -968,10 +968,10 @@ template { std::cout << "Inside Mock Transport WaitForEventResponse function" << std::endl; std::cout << "Mock Transport WaitForEventResponse eventName: " << eventName << std::endl; - /* We could optionally return a response inside WaitForEventResponse using JSON engine's mockResponse - pointing to #/x-schemas/Types/ListenResponse dereferenced to "{listening:true}". - Since there is no return value for event subscription, error would be the only validation for now. - Ideally, the event response should be returned here after the event is triggered. + /* Since there is no return value for event subscription, error would be the only validation for now. + Returning a mock event response from open rpc would mean that the logic in WaitForEventResponse to check a queue is not used. + At which point, the function would no longer be validating the SDK functionality. + If the queue find functionality is to be tested, the _pendingQueue could be mocked in upcoming iterations. */ return Firebolt::Error::None; } From 172de82c06164628abb416587b3c805f191d5e76 Mon Sep 17 00:00:00 2001 From: Keaton Sentak Date: Fri, 4 Oct 2024 13:29:23 -0400 Subject: [PATCH 11/11] feat: Add standard str validator for format types --- languages/cpp/src/shared/include/json_engine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/languages/cpp/src/shared/include/json_engine.h b/languages/cpp/src/shared/include/json_engine.h index 0bb98734..d2e2b308 100644 --- a/languages/cpp/src/shared/include/json_engine.h +++ b/languages/cpp/src/shared/include/json_engine.h @@ -148,7 +148,7 @@ class JsonEngine EXPECT_EQ(requestParams, "{}"_json); } else { - json_validator validator; + json_validator validator(nullptr, nlohmann::json_schema::default_string_format_check); const json openRPCParams = method["params"]; for (auto& item : openRPCParams.items()) { std::string key = item.key();