diff --git a/languages/cpp/src/shared/CMakeLists.txt b/languages/cpp/src/shared/CMakeLists.txt index fe7a0085..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) @@ -29,10 +34,59 @@ else () set(FIREBOLT_LIBRARY_TYPE SHARED) endif () -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) +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_PREFIX_PATH ${SYSROOT_PATH}/usr/lib/cmake CACHE INTERNAL "" FORCE) +# endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" @@ -42,15 +96,25 @@ 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) +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" - @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..d2e2b308 --- /dev/null +++ b/languages/cpp/src/shared/include/json_engine.h @@ -0,0 +1,191 @@ +#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::ifstream _file; + nlohmann::json _data; + + public: + + JsonEngine() + { + _data = read_json_from_file("../../firebolt-core-open-rpc.json"); + } + + ~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 ""; + } + + 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; + } + + + #ifdef UNIT_TEST + + // template + void MockRequest(const WPEFramework::Core::JSONRPC::Message* message) + { + 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 + EXPECT_THAT(message->Id, AllOf(Ge(1),Le(std::numeric_limits::max()))); + + // Schema validation + const json requestParams = json::parse(message->Parameters.Value()); + if(method["params"].empty()) { + std::cout << "Schema validation for empty parameters" << std::endl; + EXPECT_EQ(requestParams, "{}"_json); + } + else { + 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(); + json currentSchema = item.value(); + std::string paramName = currentSchema["name"]; + if (requestParams.contains(paramName)) { + json dereferenced_schema = process_schema(currentSchema, _data); + 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; + } + } + } + } + } + } + } + + template + Firebolt::Error MockResponse(WPEFramework::Core::JSONRPC::Message &message, RESPONSE &response) + { + 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..1f116522 100644 --- a/languages/cpp/src/shared/src/CMakeLists.txt +++ b/languages/cpp/src/shared/src/CMakeLists.txt @@ -31,17 +31,23 @@ 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) find_package(${NAMESPACE}WebSocket CONFIG REQUIRED) find_package(${NAMESPACE}Core CONFIG REQUIRED) +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} @@ -76,4 +82,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/Transport.h b/languages/cpp/src/shared/src/Transport/Transport.h index 16205745..f7a77b23 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,44 @@ 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) +// 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) + { + 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 +417,48 @@ namespace FireboltSDK { { Close(); } + +// Always return true for unit testing +#ifdef UNIT_TEST + bool IsOpen() + { + return true; + } + +#else bool IsOpen() { return (_channel.IsOpen() == 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(); } + +// Always return true for unit testing +#ifdef UNIT_TEST + bool Open(const uint32_t waitTime) + { + return true; + } +#else bool Open(const uint32_t waitTime) { bool result = true; @@ -412,18 +467,21 @@ namespace FireboltSDK { } return (result); } + +#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 +494,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 +540,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,35 +604,60 @@ namespace FireboltSDK { { _channel->Unregister(*this); - for (auto& element : _pendingQueue) { + for (auto &element : _pendingQueue) + { element.second.Abort(element.first); } } public: + +// Always return true for unit testing +#ifdef UNIT_TEST inline bool IsOpen() { - return _channel->IsOpen(); + return true; } +#else + inline bool IsOpen() + { + return _channel->IsOpen(); + } +#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; } +// 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) + { + 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) { @@ -592,9 +670,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 +719,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 +751,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 +771,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 +786,6 @@ namespace FireboltSDK { private: friend Channel; - inline bool IsEvent(const uint32_t id, string& eventName) { _adminLock.Lock(); @@ -734,7 +812,6 @@ namespace FireboltSDK { _adminLock.Unlock(); return eventExist; } - uint64_t Timed() { uint64_t result = ~0; @@ -745,12 +822,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 +844,8 @@ namespace FireboltSDK { virtual void Opened() { _status = Firebolt::Error::None; - if (_connected != true) { + if (_connected != true) + { _connected = true; _listener(_connected, _status); } @@ -776,34 +857,37 @@ 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) { 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 +897,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 +943,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(); @@ -871,39 +962,62 @@ 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; + /* 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; + } +#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) + 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); - 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,64 +1026,67 @@ 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(); return result; } - +#endif 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 +1094,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 +1118,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 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