From d91261d9839fb5df36987365d8ff45dc2e54229f Mon Sep 17 00:00:00 2001 From: msieben <4319079+msieben@users.noreply.github.com> Date: Tue, 6 Aug 2024 16:27:09 +0200 Subject: [PATCH] Development/iptestadministrator (#1704) * [Tests/unit] : Do not depend on 'IPTestAdministrator' if not used * [Tests/unit/tests] : Cherry pick 'test_iptestmanager' from development/namespaces * [Tests/unit / Tests/unit/tests] : Improve synchronization usage of 'IPTestAdministrator' * [core/IPCChannel] : cherry-pick from 'development/ipcchannel_dangling_handlers' * Tests/unit/core] : align tests with '957977381dfa8137f1475131622390ebed2bdcf7' * [Tests/unit] : Improve synchronization with premature signalling * [Tests/unit] : Add test cases to 'Tests/unit/tests/test_iptestmanager.cpp' * [Tests/unit/core] : align tests with '346fb6ec2a0da5058f7a581190e63c7cb61d929c' Including various improvements for some test. * [Tests/unit/core] : amend '7c9a286d1398de9f2b470b02bb58596120f0d8c9' * [Tests/core/unit] : cherry pick from 'development/messageunit' * [Tests/unit/core] : Use 'IPTestAdministrator' for 'PopMessageShouldReturnLastPushedMessageInOtherProcess' in test_message_unit' * [Tests/unit/IPTestAdministrator] : Try to prevent to leak shared memory descriptors * Revert "[core/IPCChannel] : cherry-pick from 'development/ipcchannel_dangling_handlers'" This reverts commit 8bc4c8a8711729fa9e9a7f12bb4bf7d0eeb78610. * [Tests/unit/core] : disable 'test_ipc' --------- Co-authored-by: MFransen69 <39826971+MFransen69@users.noreply.github.com> --- Tests/unit/IPTestAdministrator.cpp | 370 +++---- Tests/unit/IPTestAdministrator.h | 70 +- Tests/unit/core/CMakeLists.txt | 75 +- Tests/unit/core/test_cyclicbuffer.cpp | 8 +- Tests/unit/core/test_doorbell.cpp | 103 +- Tests/unit/core/test_ipc.cpp | 669 ++++++------ Tests/unit/core/test_ipcclient.cpp | 65 +- Tests/unit/core/test_message_dispatcher.cpp | 3 +- Tests/unit/core/test_message_unit.cpp | 1089 ++++++++++++++----- Tests/unit/core/test_rpc.cpp | 77 +- Tests/unit/core/test_socketstreamjson.cpp | 65 +- Tests/unit/core/test_socketstreamtext.cpp | 59 +- Tests/unit/core/test_statetrigger.cpp | 3 - Tests/unit/core/test_synchronous.cpp | 63 +- Tests/unit/core/test_tracing.cpp | 359 ------ Tests/unit/core/test_valuerecorder.cpp | 3 - Tests/unit/core/test_weblinkjson.cpp | 84 +- Tests/unit/core/test_weblinktext.cpp | 74 +- Tests/unit/core/test_websocketjson.cpp | 71 +- Tests/unit/core/test_websockettext.cpp | 65 +- Tests/unit/tests/CMakeLists.txt | 3 + Tests/unit/tests/test_iptestmanager.cpp | 144 ++- 22 files changed, 2014 insertions(+), 1508 deletions(-) delete mode 100644 Tests/unit/core/test_tracing.cpp diff --git a/Tests/unit/IPTestAdministrator.cpp b/Tests/unit/IPTestAdministrator.cpp index a231d62d0..31435c865 100644 --- a/Tests/unit/IPTestAdministrator.cpp +++ b/Tests/unit/IPTestAdministrator.cpp @@ -17,234 +17,198 @@ * limitations under the License. */ -#include "IPTestAdministrator.h" - -#include -#include -#include -#include -#include -#include -#include +#include -MODULE_NAME_DECLARATION(BUILD_REFERENCE); - -#ifdef WITH_CODE_COVERAGE -extern "C" void __gcov_flush(); +#ifdef __cplusplus +extern "C" { #endif - -IPTestAdministrator::IPTestAdministrator(OtherSideMain otherSideMain, void* data, const uint32_t waitTime) - : m_sharedData(nullptr) - , m_childPid(0) - , m_data(data) - , m_maxWaitTime(waitTime) -{ - ForkChildProcess(otherSideMain); -} -IPTestAdministrator::IPTestAdministrator(OtherSideMain otherSideMain, const uint32_t waitTime) - : m_sharedData(nullptr) - , m_childPid(0) - , m_data(nullptr) - , m_maxWaitTime(waitTime) -{ - ForkChildProcess(otherSideMain); +#include // FUTEX_* constants +#include // SYS_* constants +#include +#ifdef __cplusplus } +#endif -void IPTestAdministrator::ForkChildProcess(OtherSideMain otherSideMain) -{ - m_sharedData = static_cast(mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0)); - - pthread_mutexattr_t mutexAttr; - pthread_mutexattr_init(&mutexAttr); - pthread_mutexattr_setpshared(&mutexAttr, PTHREAD_PROCESS_SHARED); - pthread_mutex_init(&m_sharedData->m_stateMutex, &mutexAttr); - pthread_mutex_init(&m_sharedData->m_waitingForSecondCondMutex, &mutexAttr); - pthread_mutex_init(&m_sharedData->m_waitingForNormalCondMutex, &mutexAttr); - pthread_mutexattr_destroy(&mutexAttr); - - pthread_condattr_t condAttr; - pthread_condattr_init(&condAttr); - pthread_condattr_setpshared(&condAttr, PTHREAD_PROCESS_SHARED); - pthread_cond_init(&m_sharedData->m_waitingForSecondCond, &condAttr); - pthread_cond_init(&m_sharedData->m_waitingForNormalCond, &condAttr); - pthread_condattr_destroy(&condAttr); - - pid_t childProcess = fork(); - - if (childProcess == 0) { - // In child process - otherSideMain(*this); - - // TODO: should we clean up stuff here or not? - //Thunder::Core::Singleton::Dispose(); - - // Make sure no gtest cleanup code is called (summary etc). - #ifdef WITH_CODE_COVERAGE - __gcov_flush(); - #endif - - abort(); - } else { - // In parent process, store child pid, so we can kill it later. - m_childPid = childProcess; - } -} +#include +#include -IPTestAdministrator::~IPTestAdministrator() -{ - waitpid(m_childPid, 0, 0); -} +#include "IPTestAdministrator.h" -void IPTestAdministrator::WaitForChildCompletion() -{ - waitpid(m_childPid, 0, 0); -} +MODULE_NAME_DECLARATION(BUILD_REFERENCE); -bool IPTestAdministrator::Sync(const std::string & str) +IPTestAdministrator::IPTestAdministrator(Callback parent, Callback child, const uint32_t initHandShakeValue, const uint32_t waitTime) + : _sharedData{nullptr} + , _pid{-1} + , _waitTime(waitTime) { - bool result = false; + ASSERT(waitTime > 0 && waitTime < ::Thunder::Core::infinite); - // Get hold of mutex guarding state. - TimedLock(&m_sharedData->m_stateMutex, str); + _shm_id = shmget(IPC_PRIVATE, sizeof(struct SharedData) /* size */, IPC_CREAT | 0666 /* read and write for user, group and others */); - if (!m_sharedData->m_waitingForOther) { - // We are the first. - if (str.length() >= m_messageBufferSize) { - fprintf(stderr, "Warning: sync string is too long: \"%s\"\n", str.c_str()); - } + ASSERT(_shm_id >= 0); - strncpy(m_sharedData->m_message, str.c_str(), m_messageBufferSize); + _sharedData = reinterpret_cast(shmat(_shm_id, nullptr, 0 /* attach for reading and writing */)); - // Get hold of mutex of "waiting for second" cond var. - TimedLock(&m_sharedData->m_waitingForSecondCondMutex, str); + ASSERT(_sharedData != nullptr); - m_sharedData->m_waitingForOther = true; + _sharedData->handshakeValue.exchange(initHandShakeValue); - // Release state mutex, because we set "waiting for other". - pthread_mutex_unlock(&m_sharedData->m_stateMutex); + _pid = fork(); - // Wait for other side to arrive and set "m_messageTheSame" - while (m_sharedData->m_waitingForOther) { - TimedWait(&m_sharedData->m_waitingForSecondCond, &m_sharedData->m_waitingForSecondCondMutex, str); - } + ASSERT(_pid != -1); - // Now other side if waiting for us to set everything to normal situation. + if (_pid == 0) { + // Child process - // Cond var was triggered, release mutex we now hold. - pthread_mutex_unlock(&m_sharedData->m_waitingForSecondCondMutex); + ASSERT(child != nullptr); + child(*this); - // Other side arrived and set m_messageTheSame - result = m_sharedData->m_messageTheSame; - - // Unset "waiting for other" bool - m_sharedData->m_waitingForOther = false; - - // Get hold of mutex guarding "back to normal" cond var - TimedLock(&m_sharedData->m_waitingForNormalCondMutex, str); - - m_sharedData->m_backToNormal = true; - - // Signal other side everything is back to normal. - pthread_cond_signal(&m_sharedData->m_waitingForNormalCond); - - // Unlock mutex belonging to this cond var. - pthread_mutex_unlock(&m_sharedData->m_waitingForNormalCondMutex); - } else { - if (str.length() >= m_messageBufferSize) { - fprintf(stderr, "Warning: sync string is too long: \"%s\"\n", str.c_str()); - } - - // Other side came first and set "m_message", compare. - result = (strcmp(m_sharedData->m_message, str.c_str()) == 0); - - // Straight away we can unlock state mutex - pthread_mutex_unlock(&m_sharedData->m_stateMutex); - - // Store result, so other side will also return it. - m_sharedData->m_messageTheSame = result; - - // We will have to wait for other side to set everything back to normal. - // For this we need to lock the mutex with the cond var, and wait for it. - TimedLock(&m_sharedData->m_waitingForNormalCondMutex, str); - - // We have to mutex, so now safe to unset this variable. - m_sharedData->m_backToNormal = false; - - // We ("other side") are done, tell other process about it. - TimedLock(&m_sharedData->m_waitingForSecondCondMutex, str); - m_sharedData->m_waitingForOther = false; - pthread_cond_signal(&m_sharedData->m_waitingForSecondCond); - pthread_mutex_unlock(&m_sharedData->m_waitingForSecondCondMutex); - - // Wait until other process tells us all is back to normal. - while (!m_sharedData->m_backToNormal) { - TimedWait(&m_sharedData->m_waitingForNormalCond, &m_sharedData->m_waitingForNormalCondMutex, str); - } - - // We hold that mutex now, unlock it. - pthread_mutex_unlock(&m_sharedData->m_waitingForNormalCondMutex); - } - - return result; + std::exit(0); // Avoid multiple / repeated test runs + } else { + ASSERT(parent != nullptr); + parent(*this); + } } -const char * IPTestAdministrator::GetProcessName() const +IPTestAdministrator::~IPTestAdministrator() { - if (m_childPid != 0) { - return "parent"; - } else { - return "child"; - } + if (_pid > 0) { + // Is the child still alive? + + int pid_fd = syscall(SYS_pidfd_open, _pid, 0); + + if (pid_fd != -1) { + struct pollfd fds = { pid_fd, POLLIN, 0 }; + + switch (poll(&fds, sizeof(fds) / sizeof(struct pollfd), _waitTime >= std::numeric_limits::max() ? std::numeric_limits::max() : _waitTime /* timeout in milliseconds */)) { + case -1 : // error + switch(errno) { + case EFAULT : // fds is not within address space + do {} while(false); + case EINTR : // Signal occured before any requested event + do {} while(false); + case EINVAL : // Number of descriptors too large or invalid timeout value + do {} while(false); + case ENOMEM : // Unable to allocated supported memory + do {} while(false); + default :; + } + do {} while(false); + case 0 : // Timeout expired before events are available + { + // Potential leak of shared memory descriptor + int result = syscall(SYS_kill, _pid, SIGKILL); + + ASSERT(result == 0); + } + do {} while(false); + default : // Number of events set because the descriptor is readable, eg, the process has terminated + ; + } + + /* int */ close(pid_fd); + } + } + + if(shmdt(_sharedData) == -1) { + switch(errno) { + case EINVAL : // The shared data is not the start address of the shared segment + do {} while(false); + default : // Uninpsected or unknown error + ; + } + } + + if (shmctl(_shm_id, IPC_RMID, nullptr) == -1) { + switch(errno) { + case EPERM : // User ID is not that of the creator, owner or insufficient privileges + do {} while(false); + default : // Uninpsected or unknown error + ; + } + + } } -void IPTestAdministrator::TimedLock(pthread_mutex_t * mutex, const std::string & str) -{ - timespec timeSpec; - FillTimeOut(timeSpec); -#ifdef __APPLE__ - int result; - do { - result = pthread_mutex_trylock(mutex); - if (result == EBUSY) { - int wait = -1; - timespec ts; - ts.tv_sec = 1000; - if (timeSpec.tv_sec > 1000) { - timeSpec.tv_sec -= ts.tv_sec; - } else if (timeSpec.tv_sec != -1) { - ts.tv_sec = timeSpec.tv_sec; - timeSpec.tv_sec = 0; - } - - while (wait == -1) { - wait = nanosleep(&ts, &ts); - } - } else { - break; - } - } while (result != 0); - -#else - int result = pthread_mutex_timedlock(mutex, &timeSpec); - if (result == ETIMEDOUT) { - fprintf(stderr, "While locking mutex, time out expired for \"%s\" in %s.\n", str.c_str(), GetProcessName()); - abort(); - } -#endif -} -void IPTestAdministrator::TimedWait(pthread_cond_t * cond, pthread_mutex_t * mutex, const std::string & str) +uint32_t IPTestAdministrator::Wait(uint32_t expectedHandshakeValue) const { - timespec timeSpec; - FillTimeOut(timeSpec); - int result = pthread_cond_timedwait(cond, mutex, &timeSpec); - if (result == ETIMEDOUT) { - fprintf(stderr, "While waiting on cond var, time out expired for \"%s\" in %s (%u).\n", str.c_str(), GetProcessName(), getpid()); - abort(); - } + uint32_t result = ::Thunder::Core::ERROR_GENERAL; + + // Never wait infinite amount of time + const struct timespec timeout { _waitTime /* seconds */, 0 /* nanoseconds */}; + + constexpr bool stop = { false }; + + do { + long futex_result = syscall(SYS_futex, reinterpret_cast(&(_sharedData->handshakeValue)), FUTEX_WAIT, expectedHandshakeValue, &timeout, nullptr, 0); + + switch(futex_result) { + case 0 : if (_sharedData->handshakeValue == expectedHandshakeValue) { + // True wake-up + result = ::Thunder::Core::ERROR_NONE; + break; + } + + // Spurious wake-up +// TODO: continue with remaining time + continue; + case -1 : // + switch(errno) { + case EAGAIN : // Value mismatch + result = ::Thunder::Core::ERROR_INVALID_RANGE; + break; + case ETIMEDOUT : // Value has not changed within the specified timeout + result = ::Thunder::Core::ERROR_TIMEDOUT; + break; + default : // Uninspected conditions like EINTR and EINVAL + ; + } + break; + default : // Serious error + ; + } + + break; + + } while(!stop); + + return result; } -void IPTestAdministrator::FillTimeOut(timespec & timeSpec) +uint32_t IPTestAdministrator::Signal(uint32_t expectedNextHandshakeValue, uint8_t maxRetries) { - clock_gettime(CLOCK_REALTIME, &timeSpec); - timeSpec.tv_sec += m_maxWaitTime; + constexpr std::chrono::seconds s {0}; + + uint32_t result = ::Thunder::Core::ERROR_GENERAL; + + long futex_result = syscall(SYS_futex, &(_sharedData->handshakeValue), FUTEX_WAKE, INT_MAX /* number of waiters to wake-up */, nullptr, nullptr, 0); + + switch(futex_result) { + case -1 : // Error + switch(errno) { + case EINVAL : // Inconsistent state + do {} while (false); + default : // Uninspected // unknown conditions + ; + } + break; + case 0 : { + // No waiters + constexpr auto seconds2milliseconds = std::chrono::duration_cast(std::chrono::seconds(1)).count(); + + if (maxRetries > 0) { + std::this_thread::yield(); // Hopefully the scheduler plays nice + std::this_thread::sleep_for(std::chrono::milliseconds(_waitTime * seconds2milliseconds / _waitTimeDivisor)); // Sleep a fraction of the maximum specified waiting time + result = Signal(expectedNextHandshakeValue, maxRetries - 1); + } else { + result = ::Thunder::Core::ERROR_BAD_REQUEST; + } + } + break; + default : result = ::Thunder::Core::ERROR_NONE; + // Atomically replaces the current value by the expected value + bool oldHandshakeValue = _sharedData->handshakeValue.exchange(expectedNextHandshakeValue); + } + + return result; } diff --git a/Tests/unit/IPTestAdministrator.h b/Tests/unit/IPTestAdministrator.h index ef749723f..01bab4724 100644 --- a/Tests/unit/IPTestAdministrator.h +++ b/Tests/unit/IPTestAdministrator.h @@ -21,51 +21,51 @@ #include #include +#include +#include +#ifndef MODULE_NAME #include "Module.h" +#endif + +#include + +#ifndef __LINUX__ +static_assert(false, "Only LINUX is supported"); +#endif class IPTestAdministrator { -private: - static constexpr uint32_t MaxWaitTime = 2; // In seconds +public : + + using Callback = std::function; + + IPTestAdministrator(Callback /* executed by parent */, Callback /* executed by child */, const uint32_t initHandshakeValue, const uint32_t waitTime /* seconds */); + ~IPTestAdministrator(); -public: - typedef void (*OtherSideMain)(IPTestAdministrator & testAdmin); + IPTestAdministrator(const IPTestAdministrator&) = delete; + const IPTestAdministrator& operator=(const IPTestAdministrator&) = delete; - IPTestAdministrator(OtherSideMain otherSideMain, const uint32_t waitTime = MaxWaitTime); - IPTestAdministrator(OtherSideMain otherSideMain, void* data, const uint32_t waitTime = MaxWaitTime); - ~IPTestAdministrator(); + uint32_t Wait(uint32_t expectedHandshakeValue) const; + uint32_t Signal(uint32_t expectedNextHandshakeValue, uint8_t maxRetries = 0); - void ForkChildProcess(OtherSideMain otherSideMain); - // Method to sync the two test processes. - bool Sync(const std::string & str); - void WaitForChildCompletion(); + uint32_t WaitTimeDivisor() const { + return _waitTimeDivisor; + } - void* Data() { return m_data; } -private: - static const uint32_t m_messageBufferSize = 1024; +private : - struct SharedData - { - pthread_mutex_t m_stateMutex; // Guards state (are we first or second?) - pthread_cond_t m_waitingForSecondCond; // Used to wait for second - pthread_mutex_t m_waitingForSecondCondMutex; - bool m_waitingForOther; // whether we are waiting for the other side or not - bool m_messageTheSame; // whether message comparison was a success - char m_message[m_messageBufferSize]; // Expected message string - bool m_backToNormal; - pthread_cond_t m_waitingForNormalCond; // To wait for first to restore state to normal - pthread_mutex_t m_waitingForNormalCondMutex; - }; + struct SharedData + { + // std::atomic integral> has standard layout! + // A pointer may be converted to a pointer of the first non-static data element with reinterpret_cast + std::atomic handshakeValue; + }; - SharedData * m_sharedData; - pid_t m_childPid; // Set if we are parent processs. - void* m_data; - uint32_t m_maxWaitTime; // In seconds. + constexpr static uint32_t _waitTimeDivisor = 10; - const char * GetProcessName() const; - void TimedLock(pthread_mutex_t * mutex, const std::string & str); - void TimedWait(pthread_cond_t * cond, pthread_mutex_t * mutex, const std::string & str); - - void FillTimeOut(timespec & timeSpec); + SharedData* _sharedData; + int _shm_id; + pid_t _pid; + uint32_t _waitTime; // Seconds }; diff --git a/Tests/unit/core/CMakeLists.txt b/Tests/unit/core/CMakeLists.txt index b18982d77..2e665133f 100644 --- a/Tests/unit/core/CMakeLists.txt +++ b/Tests/unit/core/CMakeLists.txt @@ -17,8 +17,8 @@ set(TEST_RUNNER_NAME "Thunder_test_core") - - +if(LINUX) + # IPTestAdministrator only supported on LINUX platform add_executable(${TEST_RUNNER_NAME} ../IPTestAdministrator.cpp #test_cyclicbuffer.cpp @@ -28,12 +28,12 @@ add_executable(${TEST_RUNNER_NAME} test_doorbell.cpp test_enumerate.cpp test_event.cpp - test_hex2strserialization.cpp test_filesystem.cpp test_frametype.cpp test_hash.cpp + test_hex2strserialization.cpp + #test_ipc.cpp test_ipcclient.cpp - test_ipc.cpp test_iso639.cpp test_iterator.cpp test_jsonparser.cpp @@ -79,20 +79,63 @@ add_executable(${TEST_RUNNER_NAME} test_websockettext.cpp test_workerpool.cpp test_xgetopt.cpp - test_message_dispatcher.cpp ) - -#[[ if(MESSAGING) - target_sources(${TEST_RUNNER_NAME} PRIVATE test_message_unit.cpp) - target_link_libraries(${TEST_RUNNER_NAME} - ThunderMessaging - ) +#[[ +target_sources(${TEST_RUNNER_NAME} PRIVATE test_message_unit.cpp) +target_link_libraries(${TEST_RUNNER_NAME} + ThunderMessaging +) +]] else() - target_sources(${TEST_RUNNER_NAME} PRIVATE test_tracing.cpp) - target_link_libraries(${TEST_RUNNER_NAME} - ThunderTracing - ) -endif() ]] +add_executable(${TEST_RUNNER_NAME} + test_databuffer.cpp + test_dataelement.cpp + test_dataelementfile.cpp + test_enumerate.cpp + test_event.cpp + test_filesystem.cpp + test_frametype.cpp + test_hash.cpp + test_hex2strserialization.cpp + test_iso639.cpp + test_iterator.cpp + test_jsonparser.cpp + test_keyvalue.cpp + test_library.cpp + test_lockablecontainer.cpp + test_measurementtype.cpp + test_memberavailability.cpp +# test_message_dispatcher.cpp + test_messageException.cpp + test_networkinfo.cpp + test_nodeid.cpp + test_numbertype.cpp + test_optional.cpp + test_parser.cpp + test_portability.cpp + test_processinfo.cpp + test_queue.cpp + test_rangetype.cpp + test_readwritelock.cpp + test_rectangle.cpp + test_semaphore.cpp + test_singleton.cpp + test_statetrigger.cpp + test_stopwatch.cpp + test_synchronize.cpp + test_systeminfo.cpp + test_textfragment.cpp + test_textreader.cpp + test_thread.cpp + test_threadpool.cpp + test_time.cpp + test_timer.cpp + test_tristate.cpp + #test_valuerecorder.cpp + test_workerpool.cpp + test_xgetopt.cpp +) +endif() set_source_files_properties(test_systeminfo.cpp PROPERTIES COMPILE_OPTIONS "-fexceptions") diff --git a/Tests/unit/core/test_cyclicbuffer.cpp b/Tests/unit/core/test_cyclicbuffer.cpp index 50271970e..667982cde 100644 --- a/Tests/unit/core/test_cyclicbuffer.cpp +++ b/Tests/unit/core/test_cyclicbuffer.cpp @@ -17,6 +17,10 @@ * limitations under the License. */ +#ifdef __APPLE__ +#include +#endif + #include #ifndef MODULE_NAME @@ -27,10 +31,6 @@ #include "../IPTestAdministrator.h" -#ifdef __APPLE__ -#include -#endif - namespace Thunder { namespace Tests { namespace Core { diff --git a/Tests/unit/core/test_doorbell.cpp b/Tests/unit/core/test_doorbell.cpp index 4b8aed5de..2bcccf3cc 100644 --- a/Tests/unit/core/test_doorbell.cpp +++ b/Tests/unit/core/test_doorbell.cpp @@ -33,74 +33,87 @@ namespace Core { TEST(Core_DoorBell, simpleSet) { - std::string fileName {"/tmp/doorbell01"}; - auto lambdaFunc = [fileName] (IPTestAdministrator & testAdmin) { + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; + + const std::string fileName {"/tmp/doorbell01"}; + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { ::Thunder::Core::DoorBell doorBell(fileName.c_str()); - EXPECT_EQ(doorBell.Wait(::Thunder::Core::infinite), ::Thunder::Core::ERROR_NONE); - if (doorBell.Wait(::Thunder::Core::infinite) == ::Thunder::Core::ERROR_NONE) { - doorBell.Acknowledge(); - testAdmin.Sync("First ring"); - } - - EXPECT_EQ(doorBell.Wait(::Thunder::Core::infinite), ::Thunder::Core::ERROR_NONE); - if (doorBell.Wait(::Thunder::Core::infinite) == ::Thunder::Core::ERROR_NONE) { - doorBell.Acknowledge(); - testAdmin.Sync("Second ring"); - } + ASSERT_EQ(doorBell.Wait(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + doorBell.Acknowledge(); + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(doorBell.Wait(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + doorBell.Acknowledge(); + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + doorBell.Relinquish(); }; - static std::function lambdaVar = lambdaFunc; - - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + // a small delay so the child can be set up + SleepMs(maxInitTime); - IPTestAdministrator testAdmin(otherSide); - { ::Thunder::Core::DoorBell doorBell(fileName.c_str()); - ::SleepMs(10); + doorBell.Ring(); - testAdmin.Sync("First ring"); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + doorBell.Ring(); - testAdmin.Sync("Second ring"); - } + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + ::Thunder::Core::Singleton::Dispose(); } TEST(Core_DoorBell, simpleSetReversed) { - + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; - std::string fileName {"/tmp/doorbell02"}; - auto lambdaFunc = [fileName] (IPTestAdministrator & testAdmin) { + const std::string fileName {"/tmp/doorbell02"}; + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { ::Thunder::Core::DoorBell doorBell(fileName.c_str()); - ::SleepMs(10); + + // A small delay so the child can be set up + SleepMs(maxInitTime); + doorBell.Ring(); - testAdmin.Sync("First ring"); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + doorBell.Ring(); - testAdmin.Sync("Second ring"); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); }; - static std::function lambdaVar = lambdaFunc; + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + ::Thunder::Core::DoorBell doorBell(fileName.c_str()); - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; + ASSERT_EQ(doorBell.Wait(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + doorBell.Acknowledge(); + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(doorBell.Wait(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + doorBell.Acknowledge(); + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - IPTestAdministrator testAdmin(otherSide); - { - ::Thunder::Core::DoorBell doorBell(fileName.c_str()); - EXPECT_EQ(doorBell.Wait(::Thunder::Core::infinite), ::Thunder::Core::ERROR_NONE); - if (doorBell.Wait(::Thunder::Core::infinite) == ::Thunder::Core::ERROR_NONE) { - doorBell.Acknowledge(); - testAdmin.Sync("First ring"); - } - - EXPECT_EQ(doorBell.Wait(::Thunder::Core::infinite), ::Thunder::Core::ERROR_NONE); - if (doorBell.Wait(::Thunder::Core::infinite) == ::Thunder::Core::ERROR_NONE) { - doorBell.Acknowledge(); - testAdmin.Sync("Second ring"); - } doorBell.Relinquish(); - } + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + ::Thunder::Core::Singleton::Dispose(); } diff --git a/Tests/unit/core/test_ipc.cpp b/Tests/unit/core/test_ipc.cpp index 52299d591..51d9e9baf 100644 --- a/Tests/unit/core/test_ipc.cpp +++ b/Tests/unit/core/test_ipc.cpp @@ -205,12 +205,15 @@ namespace Core { } }; - TEST(DISABLED_Core_IPC, ContinuousChannel) + TEST(Core_IPC, ContinuousChannel) { - std::string connector = _T("/tmp/testserver0"); - auto lambdaFunc = [connector](IPTestAdministrator & testAdmin) { + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; + + const std::string connector = _T("/tmp/testserver0"); + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { ::Thunder::Core::NodeId continousNode(connector.c_str()); - uint32_t error; ::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> > factory(::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> >::Create()); @@ -218,6 +221,7 @@ namespace Core { factory->CreateFactory(2); factory->CreateFactory(2); + // Listening with no internal factory ::Thunder::Core::IPCChannelClientType<::Thunder::Core::Void, true, false> continousChannel(continousNode, 32, factory); ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler1(::Thunder::Core::ProxyType::Create()); @@ -228,32 +232,35 @@ namespace Core { continousChannel.Register(VoidTriplet::Id(), handler2); continousChannel.Register(TextText::Id(), handler3); - error = continousChannel.Source().Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(continousChannel.Source().Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); - testAdmin.Sync("setup server"); - testAdmin.Sync("setup client"); - testAdmin.Sync("done testing"); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); - error = continousChannel.Source().Close(1000); // Wait for 1 second - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); continousChannel.Unregister(TripletResponse::Id()); continousChannel.Unregister(VoidTriplet::Id()); continousChannel.Unregister(TextText::Id()); - factory->DestroyFactories(); - }; + handler1.Release(); + handler2.Release(); + handler3.Release(); - static std::function lambdaVar = lambdaFunc; + factory->DestroyFactory(); + factory->DestroyFactory(); + factory->DestroyFactory(); + + // Only for internal factories +// factory->DestroyFactories(); + + EXPECT_EQ(continousChannel.Source().Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + }; - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + // A small delay so the child can be set up + SleepMs(maxInitTime); - IPTestAdministrator testAdmin(otherSide); - { ::Thunder::Core::NodeId continousNode(connector.c_str()); - uint32_t error; - - testAdmin.Sync("setup server"); ::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> > factory(::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> >::Create()); @@ -261,59 +268,65 @@ namespace Core { factory->CreateFactory(2); factory->CreateFactory(2); + // NOT listening with no internal factory ::Thunder::Core::IPCChannelClientType<::Thunder::Core::Void, false, false> continousChannel(continousNode, 32, factory); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler1(::Thunder::Core::ProxyType::Create()); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler2(::Thunder::Core::ProxyType::Create()); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler3(::Thunder::Core::ProxyType::Create()); - - error = continousChannel.Source().Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - - testAdmin.Sync("setup client"); + ASSERT_EQ(continousChannel.Source().Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); ::Thunder::Core::ProxyType tripletResponseData(::Thunder::Core::ProxyType::Create(Triplet(1, 2, 3))); ::Thunder::Core::ProxyType voidTripletData(::Thunder::Core::ProxyType::Create()); - string text = "test text"; + + const string text = "test text"; ::Thunder::Core::ProxyType textTextData(::Thunder::Core::ProxyType::Create(::Thunder::Core::IPC::Text<2048>(text))); - uint16_t display = 1;; - uint32_t surface = 2; - uint64_t context = 3; - uint32_t result = 6; + constexpr uint16_t display = 1;; + constexpr uint32_t surface = 2; + constexpr uint64_t context = 3; + constexpr uint32_t result = 6; - error = continousChannel.Invoke(tripletResponseData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(continousChannel.Invoke(tripletResponseData, maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); EXPECT_EQ(tripletResponseData->Response().Result(), result); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue), ::Thunder::Core::ERROR_NONE); - error = continousChannel.Invoke(voidTripletData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(continousChannel.Invoke(voidTripletData, maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); EXPECT_EQ(voidTripletData->Response().Display(), display); EXPECT_EQ(voidTripletData->Response().Surface(), surface); EXPECT_EQ(voidTripletData->Response().Context(), context); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - error = continousChannel.Invoke(textTextData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(continousChannel.Invoke(textTextData, maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); EXPECT_STREQ(textTextData->Response().Value(), text.c_str()); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - error = continousChannel.Source().Close(1000); // Wait for 1 second - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + factory->DestroyFactory(); + factory->DestroyFactory(); + factory->DestroyFactory(); + + // Only for internal factories +// factory->DestroyFactories(); + + ASSERT_EQ(continousChannel.Source().Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + }; - factory->DestroyFactories(); + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); - ::Thunder::Core::Singleton::Dispose(); - } - testAdmin.Sync("done testing"); + // Code after this line is executed by both parent and child + + ::Thunder::Core::Singleton::Dispose(); } TEST(Core_IPC, ContinuousChannelReversed) { - std::string connector = _T("/tmp/testserver1"); - auto lambdaFunc = [connector](IPTestAdministrator & testAdmin) { - ::Thunder::Core::NodeId continousNode(connector.c_str()); - uint32_t error; + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; + + const std::string connector = _T("/tmp/testserver1"); - testAdmin.Sync("setup client"); + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { + // A small delay so the parent can be set up + SleepMs(maxInitTime); + + ::Thunder::Core::NodeId continousNode(connector.c_str()); ::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> > factory(::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> >::Create()); @@ -321,60 +334,48 @@ namespace Core { factory->CreateFactory(2); factory->CreateFactory(2); + // NOT listening with no factory ::Thunder::Core::IPCChannelClientType<::Thunder::Core::Void, false, false> continousChannel(continousNode, 32, factory); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler1(::Thunder::Core::ProxyType::Create()); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler2(::Thunder::Core::ProxyType::Create()); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler3(::Thunder::Core::ProxyType::Create()); - - error = continousChannel.Source().Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - - - testAdmin.Sync("setup server"); + ASSERT_EQ(continousChannel.Source().Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); ::Thunder::Core::ProxyType tripletResponseData(::Thunder::Core::ProxyType::Create(Triplet(1, 2, 3))); ::Thunder::Core::ProxyType voidTripletData(::Thunder::Core::ProxyType::Create()); - string text = "test text"; + + const string text = "test text"; ::Thunder::Core::ProxyType textTextData(::Thunder::Core::ProxyType::Create(::Thunder::Core::IPC::Text<2048>(text))); - uint16_t display = 1;; - uint32_t surface = 2; - uint64_t context = 3; - uint32_t result = 6; + constexpr uint16_t display = 1;; + constexpr uint32_t surface = 2; + constexpr uint64_t context = 3; + constexpr uint32_t result = 6; - error = continousChannel.Invoke(tripletResponseData, 5000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(continousChannel.Invoke(tripletResponseData, maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); EXPECT_EQ(tripletResponseData->Response().Result(), result); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - error = continousChannel.Invoke(voidTripletData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(continousChannel.Invoke(voidTripletData, maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); EXPECT_EQ(voidTripletData->Response().Display(), display); EXPECT_EQ(voidTripletData->Response().Surface(), surface); EXPECT_EQ(voidTripletData->Response().Context(), context); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - error = continousChannel.Invoke(textTextData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(continousChannel.Invoke(textTextData, maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); EXPECT_STREQ(textTextData->Response().Value(), text.c_str()); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - error = continousChannel.Source().Close(1000); // Wait for 1 second - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - - factory->DestroyFactories(); + factory->DestroyFactory(); + factory->DestroyFactory(); + factory->DestroyFactory(); - ::Thunder::Core::Singleton::Dispose(); + // Only for internal factories +// factory->DestroyFactories(); - testAdmin.Sync("done testing"); + EXPECT_EQ(continousChannel.Source().Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); }; - static std::function lambdaVar = lambdaFunc; - - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; - - IPTestAdministrator testAdmin(otherSide); - { + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { ::Thunder::Core::NodeId continousNode(connector.c_str()); - uint32_t error; ::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> > factory(::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> >::Create()); @@ -382,6 +383,7 @@ namespace Core { factory->CreateFactory(2); factory->CreateFactory(2); + // Listening with no internal factory ::Thunder::Core::IPCChannelClientType<::Thunder::Core::Void, true, false> continousChannel(continousNode, 32, factory); ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler1(::Thunder::Core::ProxyType::Create()); @@ -392,32 +394,46 @@ namespace Core { continousChannel.Register(VoidTriplet::Id(), handler2); continousChannel.Register(TextText::Id(), handler3); - error = continousChannel.Source().Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - - testAdmin.Sync("setup client"); - testAdmin.Sync("setup server"); - testAdmin.Sync("done testing"); + EXPECT_EQ(continousChannel.Source().Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); - error = continousChannel.Source().Close(1000); // Wait for 1 second - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); continousChannel.Unregister(TripletResponse::Id()); continousChannel.Unregister(VoidTriplet::Id()); continousChannel.Unregister(TextText::Id()); - factory->DestroyFactories(); + handler1.Release(); + handler2.Release(); + handler3.Release(); - ::Thunder::Core::Singleton::Dispose(); - } + factory->DestroyFactory(); + factory->DestroyFactory(); + factory->DestroyFactory(); + + // Only for internal factories +// factory->DestroyFactories(); + + EXPECT_EQ(continousChannel.Source().Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + + ::Thunder::Core::Singleton::Dispose(); } - TEST(DISABLED_Core_IPC, FlashChannel) + TEST(Core_IPC, FlashChannel) { - std::string connector = _T("/tmp/testserver2"); - auto lambdaFunc = [connector](IPTestAdministrator & testAdmin) { + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; + + const std::string connector = _T("/tmp/testserver2"); + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { ::Thunder::Core::NodeId flashNode(connector.c_str()); - uint32_t error; ::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> > factory(::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> >::Create()); @@ -425,6 +441,7 @@ namespace Core { factory->CreateFactory(2); factory->CreateFactory(2); + // Listening with no internal factory ::Thunder::Core::IPCChannelClientType<::Thunder::Core::Void, true, false> flashChannel(flashNode, 512, factory); ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler1(::Thunder::Core::ProxyType::Create()); @@ -435,32 +452,35 @@ namespace Core { flashChannel.Register(VoidTriplet::Id(), handler2); flashChannel.Register(TextText::Id(), handler3); - error = flashChannel.Source().Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(flashChannel.Source().Open(maxWaitTime), ::Thunder::Core::ERROR_NONE); - testAdmin.Sync("setup server"); - testAdmin.Sync("setup client"); - testAdmin.Sync("done testing"); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); - error = flashChannel.Source().Close(1000); // Wait for 1 Second - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); flashChannel.Unregister(TripletResponse::Id()); flashChannel.Unregister(VoidTriplet::Id()); flashChannel.Unregister(TextText::Id()); - factory->DestroyFactories(); - }; + handler1.Release(); + handler2.Release(); + handler3.Release(); + + factory->DestroyFactory(); + factory->DestroyFactory(); + factory->DestroyFactory(); - static std::function lambdaVar = lambdaFunc; + // Only for internal factories +// factory->DestroyFactories(); - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; + EXPECT_EQ(flashChannel.Source().Close(maxWaitTime), ::Thunder::Core::ERROR_NONE); + }; - IPTestAdministrator testAdmin(otherSide); - { - ::Thunder::Core::NodeId flashNode(connector.c_str()); - uint32_t error; + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + // A small delay so the child can be set up + SleepMs(maxInitTime); - testAdmin.Sync("setup server"); + ::Thunder::Core::NodeId flashNode(connector.c_str()); ::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> > factory(::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> >::Create()); @@ -468,63 +488,65 @@ namespace Core { factory->CreateFactory(2); factory->CreateFactory(2); + // NOT listening with no internal factory ::Thunder::Core::IPCChannelClientType<::Thunder::Core::Void, false, false> flashChannel(flashNode, 512, factory); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler1(::Thunder::Core::ProxyType::Create()); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler2(::Thunder::Core::ProxyType::Create()); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler3(::Thunder::Core::ProxyType::Create()); - - testAdmin.Sync("setup client"); + ASSERT_EQ(flashChannel.Source().Open(maxWaitTime), ::Thunder::Core::ERROR_NONE); ::Thunder::Core::ProxyType tripletResponseData(::Thunder::Core::ProxyType::Create(Triplet(1, 2, 3))); ::Thunder::Core::ProxyType voidTripletData(::Thunder::Core::ProxyType::Create()); - string text = "test text"; + + const string text = "test text"; ::Thunder::Core::ProxyType textTextData(::Thunder::Core::ProxyType::Create(::Thunder::Core::IPC::Text<2048>(text))); - uint16_t display = 1;; - uint32_t surface = 2; - uint64_t context = 3; - uint32_t result = 6; + constexpr uint16_t display = 1;; + constexpr uint32_t surface = 2; + constexpr uint64_t context = 3; + constexpr uint32_t result = 6; - error = flashChannel.Source().Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - error = flashChannel.Invoke(tripletResponseData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(flashChannel.Invoke(tripletResponseData, maxWaitTime), ::Thunder::Core::ERROR_NONE); EXPECT_EQ(tripletResponseData->Response().Result(), result); - error = flashChannel.Source().Close(1000); // Wait for 1 Second - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - error = flashChannel.Source().Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - error = flashChannel.Invoke(voidTripletData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(flashChannel.Invoke(voidTripletData, maxWaitTime), ::Thunder::Core::ERROR_NONE); EXPECT_EQ(voidTripletData->Response().Display(), display); EXPECT_EQ(voidTripletData->Response().Surface(), surface); EXPECT_EQ(voidTripletData->Response().Context(), context); - error = flashChannel.Source().Close(1000); // Wait for 1 Second - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - error = flashChannel.Source().Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - error = flashChannel.Invoke(textTextData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(flashChannel.Invoke(textTextData, maxWaitTime), ::Thunder::Core::ERROR_NONE); EXPECT_STREQ(textTextData->Response().Value(), text.c_str()); - error = flashChannel.Source().Close(1000); // Wait for 1 Second - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - factory->DestroyFactories(); - ::Thunder::Core::Singleton::Dispose(); - } + factory->DestroyFactory(); + factory->DestroyFactory(); + factory->DestroyFactory(); + + // Only for internal factories +// factory->DestroyFactories(); + + ASSERT_EQ(flashChannel.Source().Close(maxWaitTime), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); - testAdmin.Sync("done testing"); + // Code after this line is executed by both parent and child + + ::Thunder::Core::Singleton::Dispose(); } - TEST(DISABLED_Core_IPC, FlashChannelReversed) + TEST(Core_IPC, FlashChannelReversed) { - std::string connector = _T("/tmp/testserver3"); - auto lambdaFunc = [connector](IPTestAdministrator & testAdmin) { + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; + + const std::string connector = _T("/tmp/testserver3"); + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { + // A small delay so the parent can be set up + SleepMs(maxInitTime); + ::Thunder::Core::NodeId flashNode(connector.c_str()); - uint32_t error; ::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> > factory(::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> >::Create()); @@ -532,42 +554,48 @@ namespace Core { factory->CreateFactory(2); factory->CreateFactory(2); - ::Thunder::Core::IPCChannelClientType<::Thunder::Core::Void, true, false> flashChannel(flashNode, 512, factory); + // NOT listening with no internal factory + ::Thunder::Core::IPCChannelClientType<::Thunder::Core::Void, false, false> flashChannel(flashNode, 512, factory); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler1(::Thunder::Core::ProxyType::Create()); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler2(::Thunder::Core::ProxyType::Create()); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler3(::Thunder::Core::ProxyType::Create()); + ASSERT_EQ(flashChannel.Source().Open(maxWaitTime), ::Thunder::Core::ERROR_NONE); - flashChannel.Register(TripletResponse::Id(), handler1); - flashChannel.Register(VoidTriplet::Id(), handler2); - flashChannel.Register(TextText::Id(), handler3); + ::Thunder::Core::ProxyType tripletResponseData(::Thunder::Core::ProxyType::Create(Triplet(1, 2, 3))); + ::Thunder::Core::ProxyType voidTripletData(::Thunder::Core::ProxyType::Create()); - error = flashChannel.Source().Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + const string text = "test text"; + ::Thunder::Core::ProxyType textTextData(::Thunder::Core::ProxyType::Create(::Thunder::Core::IPC::Text<2048>(text))); - testAdmin.Sync("setup server"); - testAdmin.Sync("setup client"); - testAdmin.Sync("done testing"); + constexpr uint16_t display = 1;; + constexpr uint32_t surface = 2; + constexpr uint64_t context = 3; + constexpr uint32_t result = 6; - error = flashChannel.Source().Close(1000); // Wait for 1 Second - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - flashChannel.Unregister(TripletResponse::Id()); - flashChannel.Unregister(VoidTriplet::Id()); - flashChannel.Unregister(TextText::Id()); + EXPECT_EQ(flashChannel.Invoke(tripletResponseData, maxWaitTime), ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(tripletResponseData->Response().Result(), result); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - factory->DestroyFactories(); - }; + EXPECT_EQ(flashChannel.Invoke(voidTripletData, maxWaitTime), ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(voidTripletData->Response().Display(), display); + EXPECT_EQ(voidTripletData->Response().Surface(), surface); + EXPECT_EQ(voidTripletData->Response().Context(), context); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - static std::function lambdaVar = lambdaFunc; + EXPECT_EQ(flashChannel.Invoke(textTextData, maxWaitTime), ::Thunder::Core::ERROR_NONE); + EXPECT_STREQ(textTextData->Response().Value(), text.c_str()); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; + factory->DestroyFactory(); + factory->DestroyFactory(); + factory->DestroyFactory(); - IPTestAdministrator testAdmin(otherSide); - { - ::Thunder::Core::NodeId flashNode(connector.c_str()); - uint32_t error; + // Only for internal factories +// factory->DestroyFactories(); - testAdmin.Sync("setup server"); + ASSERT_EQ(flashChannel.Source().Close(maxWaitTime), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + ::Thunder::Core::NodeId flashNode(connector.c_str()); ::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> > factory(::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> >::Create()); @@ -575,62 +603,57 @@ namespace Core { factory->CreateFactory(2); factory->CreateFactory(2); - ::Thunder::Core::IPCChannelClientType<::Thunder::Core::Void, false, false> flashChannel(flashNode, 512, factory); + // Listening with no internal factory + ::Thunder::Core::IPCChannelClientType<::Thunder::Core::Void, true, false> flashChannel(flashNode, 512, factory); ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler1(::Thunder::Core::ProxyType::Create()); ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler2(::Thunder::Core::ProxyType::Create()); ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler3(::Thunder::Core::ProxyType::Create()); - testAdmin.Sync("setup client"); + flashChannel.Register(TripletResponse::Id(), handler1); + flashChannel.Register(VoidTriplet::Id(), handler2); + flashChannel.Register(TextText::Id(), handler3); - ::Thunder::Core::ProxyType tripletResponseData(::Thunder::Core::ProxyType::Create(Triplet(1, 2, 3))); - ::Thunder::Core::ProxyType voidTripletData(::Thunder::Core::ProxyType::Create()); - string text = "test text"; - ::Thunder::Core::ProxyType textTextData(::Thunder::Core::ProxyType::Create(::Thunder::Core::IPC::Text<2048>(text))); + ASSERT_EQ(flashChannel.Source().Open(maxWaitTime), ::Thunder::Core::ERROR_NONE); - uint16_t display = 1;; - uint32_t surface = 2; - uint64_t context = 3; - uint32_t result = 6; + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); - error = flashChannel.Source().Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - error = flashChannel.Invoke(tripletResponseData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - EXPECT_EQ(tripletResponseData->Response().Result(), result); - error = flashChannel.Source().Close(1000); // Wait for 1 Second - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + flashChannel.Unregister(TripletResponse::Id()); + flashChannel.Unregister(VoidTriplet::Id()); + flashChannel.Unregister(TextText::Id()); - error = flashChannel.Source().Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - error = flashChannel.Invoke(voidTripletData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - EXPECT_EQ(voidTripletData->Response().Display(), display); - EXPECT_EQ(voidTripletData->Response().Surface(), surface); - EXPECT_EQ(voidTripletData->Response().Context(), context); - error = flashChannel.Source().Close(1000); // Wait for 1 Second - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + handler1.Release(); + handler2.Release(); + handler3.Release(); - error = flashChannel.Source().Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - error = flashChannel.Invoke(textTextData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - EXPECT_STREQ(textTextData->Response().Value(), text.c_str()); - error = flashChannel.Source().Close(1000); // Wait for 1 Second - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + factory->DestroyFactory(); + factory->DestroyFactory(); + factory->DestroyFactory(); - factory->DestroyFactories(); - ::Thunder::Core::Singleton::Dispose(); - } - testAdmin.Sync("done testing"); + // Only for internal factories +// factory->DestroyFactories(); + + EXPECT_EQ(flashChannel.Source().Close(maxWaitTime), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + + ::Thunder::Core::Singleton::Dispose(); } TEST(Core_IPC, MultiChannel) { - std::string connector = _T("/tmp/testserver4"); - auto lambdaFunc = [connector](IPTestAdministrator & testAdmin) { + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; + + const std::string connector = _T("/tmp/testserver4"); + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { ::Thunder::Core::NodeId multiNode(connector.c_str()); - uint32_t error; ::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> > factory(::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> >::Create()); @@ -638,6 +661,7 @@ namespace Core { factory->CreateFactory(2); factory->CreateFactory(2); + // Server (always listening) with no internal factory ::Thunder::Core::IPCChannelServerType<::Thunder::Core::Void, false> multiChannel(multiNode, 512, factory); ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler1(::Thunder::Core::ProxyType::Create()); @@ -648,31 +672,41 @@ namespace Core { multiChannel.Register(VoidTriplet::Id(), handler2); multiChannel.Register(TextText::Id(), handler3); - error = multiChannel.Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(multiChannel.Open(maxWaitTime), ::Thunder::Core::ERROR_NONE); - testAdmin.Sync("setup server"); - testAdmin.Sync("setup client"); - testAdmin.Sync("done testing"); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); - error = multiChannel.Close(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + multiChannel.Unregister(TripletResponse::Id()); + multiChannel.Unregister(VoidTriplet::Id()); + multiChannel.Unregister(TextText::Id()); - factory->DestroyFactories(); + handler1.Release(); + handler2.Release(); + handler3.Release(); - ::Thunder::Core::Singleton::Dispose(); - }; + factory->DestroyFactory(); + factory->DestroyFactory(); + factory->DestroyFactory(); - static std::function lambdaVar = lambdaFunc; + // Only for internal factories +// factory->DestroyFactories(); + + // A server cannot 'end' its life if clients are connected + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; + // Do not unregister any / the last handler prior the call of cleanup + multiChannel.Cleanup(); - IPTestAdministrator testAdmin(otherSide); - { - ::Thunder::Core::NodeId multiNode(connector.c_str()); - uint32_t error; + ASSERT_EQ(multiChannel.Close(maxWaitTime), ::Thunder::Core::ERROR_NONE); + }; - testAdmin.Sync("setup server"); + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + // A small delay so the child can be set up + SleepMs(maxInitTime); + + ::Thunder::Core::NodeId multiNode(connector.c_str()); ::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> > factory(::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> >::Create()); @@ -680,171 +714,172 @@ namespace Core { factory->CreateFactory(2); factory->CreateFactory(2); + // (Server client) NOT listening with no internal factory ::Thunder::Core::IPCChannelClientType<::Thunder::Core::Void, false, false> multiChannel(multiNode, 512, factory); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler1(::Thunder::Core::ProxyType::Create()); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler2(::Thunder::Core::ProxyType::Create()); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler3(::Thunder::Core::ProxyType::Create()); - - error = multiChannel.Source().Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - - testAdmin.Sync("setup client"); + ASSERT_EQ(multiChannel.Source().Open(maxWaitTime), ::Thunder::Core::ERROR_NONE); ::Thunder::Core::ProxyType tripletResponseData(::Thunder::Core::ProxyType::Create(Triplet(1, 2, 3))); ::Thunder::Core::ProxyType voidTripletData(::Thunder::Core::ProxyType::Create()); - string text = "test text"; + + const string text = "test text"; ::Thunder::Core::ProxyType textTextData(::Thunder::Core::ProxyType::Create(::Thunder::Core::IPC::Text<2048>(text))); - uint16_t display = 1;; - uint32_t surface = 2; - uint64_t context = 3; - uint32_t result = 6; + constexpr uint16_t display = 1;; + constexpr uint32_t surface = 2; + constexpr uint64_t context = 3; + constexpr uint32_t result = 6; - error = multiChannel.Invoke(tripletResponseData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(multiChannel.Invoke(tripletResponseData, maxWaitTime), ::Thunder::Core::ERROR_NONE); EXPECT_EQ(tripletResponseData->Response().Result(), result); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - error = multiChannel.Invoke(voidTripletData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(multiChannel.Invoke(voidTripletData, maxWaitTime), ::Thunder::Core::ERROR_NONE); EXPECT_EQ(voidTripletData->Response().Display(), display); EXPECT_EQ(voidTripletData->Response().Surface(), surface); EXPECT_EQ(voidTripletData->Response().Context(), context); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - error = multiChannel.Invoke(textTextData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(multiChannel.Invoke(textTextData, maxWaitTime), ::Thunder::Core::ERROR_NONE); EXPECT_STREQ(textTextData->Response().Value(), text.c_str()); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + + factory->DestroyFactory(); + factory->DestroyFactory(); + factory->DestroyFactory(); - error = multiChannel.Source().Close(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + // Only for internal factories +// factory->DestroyFactories(); - factory->DestroyFactories(); + ASSERT_EQ(multiChannel.Source().Close(maxWaitTime), ::Thunder::Core::ERROR_NONE); - ::Thunder::Core::Singleton::Dispose(); - } -// testAdmin.Sync("done testing"); + // Signal the server it can 'end' its life + ASSERT_EQ(testAdmin.Signal(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + + ::Thunder::Core::Singleton::Dispose(); } TEST(Core_IPC, MultiChannelReversed) { - std::string connector = _T("/tmp/testserver5"); - auto lambdaFunc = [connector](IPTestAdministrator & testAdmin) { - ::Thunder::Core::NodeId multiNode(connector.c_str()); - uint32_t error; + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; - testAdmin.Sync("setup client"); + const std::string connector = _T("/tmp/testserver5"); + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { + // A small delay so the parent can be set up + SleepMs(maxInitTime); + + ::Thunder::Core::NodeId multiNode(connector.c_str()); ::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> > factory(::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> >::Create()); + factory->CreateFactory(2); factory->CreateFactory(2); factory->CreateFactory(2); + // (Server client) NOT listening with no internal factory ::Thunder::Core::IPCChannelClientType<::Thunder::Core::Void, false, false> multiChannel(multiNode, 512, factory); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler1(::Thunder::Core::ProxyType::Create()); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler2(::Thunder::Core::ProxyType::Create()); - ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler3(::Thunder::Core::ProxyType::Create()); - - error = multiChannel.Source().Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - - testAdmin.Sync("setup server"); + ASSERT_EQ(multiChannel.Source().Open(maxWaitTime), ::Thunder::Core::ERROR_NONE); ::Thunder::Core::ProxyType tripletResponseData(::Thunder::Core::ProxyType::Create(Triplet(1, 2, 3))); ::Thunder::Core::ProxyType voidTripletData(::Thunder::Core::ProxyType::Create()); - string text = "test text"; + + const string text = "test text"; ::Thunder::Core::ProxyType textTextData(::Thunder::Core::ProxyType::Create(::Thunder::Core::IPC::Text<2048>(text))); - uint16_t display = 1;; - uint32_t surface = 2; - uint64_t context = 3; - uint32_t result = 6; - error = multiChannel.Invoke(tripletResponseData, 5000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + constexpr uint16_t display = 1;; + constexpr uint32_t surface = 2; + constexpr uint64_t context = 3; + constexpr uint32_t result = 6; + + EXPECT_EQ(multiChannel.Invoke(tripletResponseData, maxWaitTime), ::Thunder::Core::ERROR_NONE); EXPECT_EQ(tripletResponseData->Response().Result(), result); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - error = multiChannel.Invoke(voidTripletData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(multiChannel.Invoke(voidTripletData, maxWaitTime), ::Thunder::Core::ERROR_NONE); EXPECT_EQ(voidTripletData->Response().Display(), display); EXPECT_EQ(voidTripletData->Response().Surface(), surface); EXPECT_EQ(voidTripletData->Response().Context(), context); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - error = multiChannel.Invoke(textTextData, 2000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(multiChannel.Invoke(textTextData, maxWaitTime), ::Thunder::Core::ERROR_NONE); EXPECT_STREQ(textTextData->Response().Value(), text.c_str()); - - tripletResponseData.Release(); - voidTripletData.Release(); - textTextData.Release(); - - error = multiChannel.Source().Close(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - - handler1.Release(); - handler2.Release(); - handler3.Release(); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); factory->DestroyFactory(); factory->DestroyFactory(); factory->DestroyFactory(); - factory->DestroyFactories(); + // Only for internal factories +// factory->DestroyFactories(); - ::Thunder::Core::Singleton::Dispose(); + ASSERT_EQ(multiChannel.Source().Close(maxWaitTime), ::Thunder::Core::ERROR_NONE); - testAdmin.Sync("done testing"); + // Signal the server it can 'end' its life + ASSERT_EQ(testAdmin.Signal(initHandshakeValue), ::Thunder::Core::ERROR_NONE); }; - static std::function lambdaVar = lambdaFunc; - - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; - - IPTestAdministrator testAdmin(otherSide); - { + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { ::Thunder::Core::NodeId multiNode(connector.c_str()); - uint32_t error; ::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> > factory(::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> >::Create()); + factory->CreateFactory(2); factory->CreateFactory(2); factory->CreateFactory(2); + // Server (always listening) with no internal factory ::Thunder::Core::IPCChannelServerType<::Thunder::Core::Void, false> multiChannel(multiNode, 512, factory); ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler1(::Thunder::Core::ProxyType::Create()); ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler2(::Thunder::Core::ProxyType::Create()); ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer> handler3(::Thunder::Core::ProxyType::Create()); + ASSERT_EQ(multiChannel.Open(maxWaitTime), ::Thunder::Core::ERROR_NONE); + multiChannel.Register(TripletResponse::Id(), handler1); multiChannel.Register(VoidTriplet::Id(), handler2); multiChannel.Register(TextText::Id(), handler3); - error = multiChannel.Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - - testAdmin.Sync("setup client"); - testAdmin.Sync("setup server"); - testAdmin.Sync("done testing"); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); - error = multiChannel.Close(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + multiChannel.Unregister(TripletResponse::Id()); + multiChannel.Unregister(VoidTriplet::Id()); + multiChannel.Unregister(TextText::Id()); handler1.Release(); handler2.Release(); handler3.Release(); - multiChannel.Cleanup(); - factory->DestroyFactory(); factory->DestroyFactory(); factory->DestroyFactory(); - factory->DestroyFactories(); + // Only for internal factories +// factory->DestroyFactories(); - ::Thunder::Core::Singleton::Dispose(); + // A server cannot 'end' its life if clients are connected + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); -// testAdmin.Sync("done testing"); - } + multiChannel.Cleanup(); + + ASSERT_EQ(multiChannel.Close(maxWaitTime), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + + ::Thunder::Core::Singleton::Dispose(); } } // Core diff --git a/Tests/unit/core/test_ipcclient.cpp b/Tests/unit/core/test_ipcclient.cpp index 66298e695..04bf53a83 100644 --- a/Tests/unit/core/test_ipcclient.cpp +++ b/Tests/unit/core/test_ipcclient.cpp @@ -33,53 +33,58 @@ namespace Core { TEST(Core_IPC, IPCClientConnection) { - std::string connector = _T("/tmp/testserver"); + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; - auto lambdaFunc = [connector](IPTestAdministrator & testAdmin) { - ::Thunder::Core::NodeId serverNode(connector.c_str()); + const std::string connector = _T("/tmp/testserver"); - uint32_t error; + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { + ::Thunder::Core::NodeId serverNode(connector.c_str()); ::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> > factory(::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> >::Create()); + ::Thunder::Core::IPCChannelServerType<::Thunder::Core::Void, false> serverChannel(serverNode, 512, factory); - error = serverChannel.Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - testAdmin.Sync("setup server"); - testAdmin.Sync("setup client"); - testAdmin.Sync("done testing"); + ASSERT_EQ(serverChannel.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + // Only for internal factories +// factory->DestroyFactories(); - error = serverChannel.Close(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); + // A server cannot 'end' its life if clients are connected + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + // Do not unregister any / the last handler prior the call of cleanup serverChannel.Cleanup(); - factory->DestroyFactories(); + ASSERT_EQ(serverChannel.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); }; - static std::function lambdaVar = lambdaFunc; + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + // A small delay so the child can be set up + SleepMs(maxInitTime); - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; - - IPTestAdministrator testAdmin(otherSide); - { ::Thunder::Core::NodeId clientNode(connector.c_str()); - uint32_t error; - - testAdmin.Sync("setup server"); ::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> > factory(::Thunder::Core::ProxyType<::Thunder::Core::FactoryType<::Thunder::Core::IIPC, uint32_t> >::Create()); + ::Thunder::Core::IPCChannelClientType<::Thunder::Core::Void, false, false> clientChannel(clientNode, 512, factory); - error = clientChannel.Source().Open(1000); // Wait for 1 Second. - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - testAdmin.Sync("setup client"); - - error = clientChannel.Close(1000); - EXPECT_EQ(error, ::Thunder::Core::ERROR_NONE); - factory->DestroyFactories(); - ::Thunder::Core::Singleton::Dispose(); - } - testAdmin.Sync("done testing"); + + ASSERT_EQ(clientChannel.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + // Only for internal factories +// factory->DestroyFactories(); + + ASSERT_EQ(clientChannel.Source().Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + // Signal the server it can 'end' its life + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + + ::Thunder::Core::Singleton::Dispose(); } } // Core diff --git a/Tests/unit/core/test_message_dispatcher.cpp b/Tests/unit/core/test_message_dispatcher.cpp index 53f6ef996..cf0a3a2ce 100644 --- a/Tests/unit/core/test_message_dispatcher.cpp +++ b/Tests/unit/core/test_message_dispatcher.cpp @@ -26,11 +26,10 @@ #endif #include +#include #include "../IPTestAdministrator.h" -#include - namespace Thunder { namespace Tests { diff --git a/Tests/unit/core/test_message_unit.cpp b/Tests/unit/core/test_message_unit.cpp index a4966c0a3..446f05f09 100644 --- a/Tests/unit/core/test_message_unit.cpp +++ b/Tests/unit/core/test_message_unit.cpp @@ -17,6 +17,8 @@ * limitations under the License. */ +#include + #include #ifndef MODULE_NAME @@ -25,15 +27,19 @@ #include +#include + #include "../IPTestAdministrator.h" +#define PRINT_MODULES(NAME, MODULES) {std::cout << NAME << " ---> "; std::for_each(MODULES.begin(), MODULES.end(), [](std::string MODULE){std::cout << " " << MODULE;}); std::cout << std::endl;}; + namespace Thunder { namespace Tests { namespace Core { class Control : public ::Thunder::Core::Messaging::IControl { public: - Control(const ::Thunder::Core::Messaging::MetaData& metaData) + Control(const ::Thunder::Core::Messaging::Metadata& metaData) : _metaData(metaData) { } @@ -53,72 +59,94 @@ namespace Core { { _isEnabled = false; } - const ::Thunder::Core::Messaging::MetaData& MessageMetaData() const override + const ::Thunder::Core::Messaging::Metadata& Metadata() const override { return _metaData; } private: bool _isEnabled; - ::Thunder::Core::Messaging::MetaData _metaData; + ::Thunder::Core::Messaging::Metadata _metaData; }; class Core_Messaging_MessageUnit : public testing::Test { protected: Core_Messaging_MessageUnit() { - _controls.emplace_back(new Control({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("Test_Category_1"), EXPAND_AND_QUOTE(MODULE_NAME) })); - _controls.emplace_back(new Control({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("Test_Category_2"), EXPAND_AND_QUOTE(MODULE_NAME) })); - _controls.emplace_back(new Control({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("Test_Category_3"), EXPAND_AND_QUOTE(MODULE_NAME) })); - _controls.emplace_back(new Control({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("Test_Category_4"), EXPAND_AND_QUOTE(MODULE_NAME) })); - _controls.emplace_back(new Control({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("Test_Category_1"), _T("Test_Module2") })); - _controls.emplace_back(new Control({ ::Thunder::Core::Messaging::MetaData::MessageType::LOGGING, _T("Test_Category_5"), _T("SysLog") })); + _controls.emplace_back(new Control({ ::Thunder::Core::Messaging::Metadata::type::TRACING, _T("Test_Category_1"), EXPAND_AND_QUOTE(MODULE_NAME) })); + _controls.emplace_back(new Control({ ::Thunder::Core::Messaging::Metadata::type::TRACING, _T("Test_Category_2"), EXPAND_AND_QUOTE(MODULE_NAME) })); + _controls.emplace_back(new Control({ ::Thunder::Core::Messaging::Metadata::type::TRACING, _T("Test_Category_3"), EXPAND_AND_QUOTE(MODULE_NAME) })); + _controls.emplace_back(new Control({ ::Thunder::Core::Messaging::Metadata::type::TRACING, _T("Test_Category_4"), EXPAND_AND_QUOTE(MODULE_NAME) })); + _controls.emplace_back(new Control({ ::Thunder::Core::Messaging::Metadata::type::TRACING, _T("Test_Category_1"), _T("Test_Module2") })); + _controls.emplace_back(new Control({ ::Thunder::Core::Messaging::Metadata::type::LOGGING, _T("Test_Category_5"), _T("SysLog") })); + } ~Core_Messaging_MessageUnit() = default; static void SetUpTestSuite() { - ::Thunder::Core::Messaging::MessageUnit::Instance().IsBackground(_background); - ::Thunder::Core::Messaging::MessageUnit::Instance().Open(_basePath); } static void TearDownTestSuite() { - ::Thunder::Core::Messaging::MessageUnit::Instance().Close(); ::Thunder::Core::Singleton::Dispose(); } + void SetUp() override { - for (const auto& control : _controls) { - ::Thunder::Core::Messaging::MessageUnit::Instance().Announce(control.get()); - } } void TearDown() override { - ::Thunder::Core::Messaging::MessageUnit::Instance().Defaults(_T("")); - for (const auto& control : _controls) { - ::Thunder::Core::Messaging::MessageUnit::Instance().Revoke(control.get()); - } } string DispatcherIdentifier() { - string result; - ::Thunder::Core::SystemInfo::GetEnvironment(::Thunder::Core::Messaging::MessageUnit::MESSAGE_DISPACTHER_IDENTIFIER_ENV, result); - return result; + return ::Thunder::Messaging::MessageUnit::Instance().Identifier(); } string DispatcherBasePath() { string result; - ::Thunder::Core::SystemInfo::GetEnvironment(::Thunder::Core::Messaging::MessageUnit::MESSAGE_DISPATCHER_PATH_ENV, result); - return result; + return ::Thunder::Messaging::MessageUnit::Instance().BasePath(); + } + + void AnnounceAllControls() + { + for (const auto& control : _controls) { + // Only for 'controls' enabled in configuration + ::Thunder::Core::Messaging::IControl::Announce(control.get()); + } + } + + void RevokeAllControls() + { + for (const auto& control : _controls) { + ::Thunder::Core::Messaging::IControl::Revoke(control.get()); + } + } + + void ToggleDefaultConfig(bool activate) + { + ASSERT(_activeConfig != activate); + + if (!_activeConfig && activate) { + ::Thunder::Messaging::MessageUnit::Settings::Config configuration; + ::Thunder::Messaging::MessageUnit::Instance().Open(Core_Messaging_MessageUnit::_basePath, configuration, Core_Messaging_MessageUnit::_background, ::Thunder::Messaging::MessageUnit::OFF); + } + + if (_activeConfig && !activate) { + ::Thunder::Messaging::MessageUnit::Instance().Close(); + } + + _activeConfig = !_activeConfig; } static bool _background; static string _basePath; std::list> _controls; + + bool _activeConfig; }; bool Core_Messaging_MessageUnit::_background = false; @@ -126,457 +154,932 @@ namespace Core { TEST_F(Core_Messaging_MessageUnit, TraceMessageIsEnabledByDefaultWhenConfigFullySpecified) { - const string config = R"({"tracing":{"messages":[{"category":"Information","module":"Plugin_DeviceInfo","enabled":true}]}})"; + ::Thunder::Messaging::MessageUnit::Settings::Config configuration; + configuration.FromString(R"({"tracing":{"settings":[{"category":"Information","module":"Plugin_DeviceInfo","enabled":true}]}})"); - ::Thunder::Core::Messaging::MessageUnit::Instance().Defaults(config); - ASSERT_TRUE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("Information"), _T("Plugin_DeviceInfo") })); - ASSERT_FALSE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("Information"), _T("Some_Module") })); - ASSERT_FALSE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("SomeCategory"), _T("Plugin_DeviceInfo") })); + ::Thunder::Messaging::MessageUnit::Settings settings; + settings.Configure(Core_Messaging_MessageUnit::_basePath, "SomeIdentifier", configuration, Core_Messaging_MessageUnit::_background, ::Thunder::Messaging::MessageUnit::OFF); + + ::Thunder::Core::Messaging::Metadata metaData(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("Information"), _T("Plugin_DeviceInfo")); + EXPECT_TRUE(settings.IsEnabled(metaData)); + + metaData = ::Thunder::Core::Messaging::Metadata(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("Information"), _T("Some_Module")); + EXPECT_FALSE(settings.IsEnabled(metaData)); + + metaData = ::Thunder::Core::Messaging::Metadata(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("SomeCategory"), _T("Plugin_DeviceInfo")); + EXPECT_FALSE(settings.IsEnabled(metaData)); } TEST_F(Core_Messaging_MessageUnit, TraceMessageIsDisabledByDefaultWhenConfigFullySpecified) { - const string config = R"({"tracing":{"messages":[{"category":"Information","module":"Plugin_DeviceInfo","enabled":false}]}})"; + ::Thunder::Messaging::MessageUnit::Settings::Config configuration; + configuration.FromString(R"({"tracing":{"settings":[{"category":"Information","module":"Plugin_DeviceInfo","enabled":false}]}})"); + + ::Thunder::Messaging::MessageUnit::Settings settings; + settings.Configure(Core_Messaging_MessageUnit::_basePath, "SomeIdentifier", configuration, Core_Messaging_MessageUnit::_background, ::Thunder::Messaging::MessageUnit::OFF); + + ::Thunder::Core::Messaging::Metadata metaData(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("Information"), _T("Plugin_DeviceInfo")); + EXPECT_FALSE(settings.IsEnabled(metaData)); - ::Thunder::Core::Messaging::MessageUnit::Instance().Defaults(config); - ASSERT_FALSE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("Information"), _T("Plugin_DeviceInfo") })); - ASSERT_FALSE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("Information"), _T("Some_Module") })); - ASSERT_FALSE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("SomeCategory"), _T("Plugin_DeviceInfo") })); + metaData = ::Thunder::Core::Messaging::Metadata(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("Information"), _T("Some_Module")); + EXPECT_FALSE(settings.IsEnabled(metaData)); + + metaData = ::Thunder::Core::Messaging::Metadata(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("SomeCategory"), _T("Plugin_DeviceInfo")); + EXPECT_FALSE(settings.IsEnabled(metaData)); } TEST_F(Core_Messaging_MessageUnit, TraceMessagesAreEnabledWhenModuleNotSpecified) { - const string config = R"({"tracing":{"messages":[{"category":"Information","enabled":true}]}})"; + ::Thunder::Messaging::MessageUnit::Settings::Config configuration; + configuration.FromString(R"({"tracing":{"settings":[{"category":"Information","enabled":true}]}})"); + + ::Thunder::Messaging::MessageUnit::Settings settings; + settings.Configure(Core_Messaging_MessageUnit::_basePath, "SomeIdentifier", configuration, Core_Messaging_MessageUnit::_background, ::Thunder::Messaging::MessageUnit::OFF); + + ::Thunder::Core::Messaging::Metadata metaData(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("Information"), _T("Plugin_DeviceInfo")); + EXPECT_TRUE(settings.IsEnabled(metaData)); + + metaData = ::Thunder::Core::Messaging::Metadata(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("Information"), _T("Some_Module")); + EXPECT_TRUE(settings.IsEnabled(metaData)); - ::Thunder::Core::Messaging::MessageUnit::Instance().Defaults(config); - ASSERT_TRUE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("Information"), _T("Plugin_DeviceInfo") })); - ASSERT_TRUE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("Information"), _T("Some_Module") })); - ASSERT_FALSE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("SomeCategory"), _T("Plugin_DeviceInfo") })); + metaData = ::Thunder::Core::Messaging::Metadata(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("SomeCategory"), _T("Plugin_DeviceInfo")); + EXPECT_FALSE(settings.IsEnabled(metaData)); } TEST_F(Core_Messaging_MessageUnit, TraceMessagesAreDisabledWhenModuleNotSpecified) { - const string config = R"({"tracing":{"messages":[{"category":"Information","enabled":false}]}})"; + ::Thunder::Messaging::MessageUnit::Settings::Config configuration; + configuration.FromString(R"({"tracing":{"messages":[{"category":"Information","enabled":false}]}})"); - ::Thunder::Core::Messaging::MessageUnit::Instance().Defaults(config); - ASSERT_FALSE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("Information"), _T("Plugin_DeviceInfo") })); - ASSERT_FALSE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("Information"), _T("Some_Module") })); - ASSERT_FALSE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("SomeCategory"), _T("Plugin_DeviceInfo") })); + ::Thunder::Messaging::MessageUnit::Settings settings; + settings.Configure(Core_Messaging_MessageUnit::_basePath, "SomeIdentifier", configuration, Core_Messaging_MessageUnit::_background, ::Thunder::Messaging::MessageUnit::OFF); + + ::Thunder::Core::Messaging::Metadata metaData(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("Information"), _T("Plugin_DeviceInfo")); + EXPECT_FALSE(settings.IsEnabled(metaData)); + + metaData = ::Thunder::Core::Messaging::Metadata(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("Information"), _T("Some_Module")); + EXPECT_FALSE(settings.IsEnabled(metaData)); + + metaData = ::Thunder::Core::Messaging::Metadata(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("SomeCategory"), _T("Plugin_DeviceInfo")); + EXPECT_FALSE(settings.IsEnabled(metaData)); } TEST_F(Core_Messaging_MessageUnit, LoggingMessageIsEnabledIfNotConfigured) { - //logging messages are enabled by default (if not specified otherwise in the config) - const string config = R"({"logging":{"messages":[{"category":"Startup","module":"SysLog","enabled":false}]}})"; - ::Thunder::Core::Messaging::MessageUnit::Instance().Defaults(config); - ASSERT_FALSE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault({ ::Thunder::Core::Messaging::MetaData::MessageType::LOGGING, _T("Startup"), _T("SysLog") })); - ASSERT_TRUE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault({ ::Thunder::Core::Messaging::MetaData::MessageType::LOGGING, _T("Notification"), _T("SysLog") })); + ::Thunder::Messaging::MessageUnit::Settings::Config configuration; + configuration.FromString(R"({"logging":{"settings":[{"category":"Startup","module":"SysLog","enabled":false}]}})"); + + ::Thunder::Messaging::MessageUnit::Settings settings; + settings.Configure(Core_Messaging_MessageUnit::_basePath, "SomeIdentifier", configuration, Core_Messaging_MessageUnit::_background, ::Thunder::Messaging::MessageUnit::OFF); + + ::Thunder::Core::Messaging::Metadata metaData(::Thunder::Core::Messaging::Metadata::type::LOGGING, _T("Startup"), _T("SysLog")); + // Internal Metadata::Default() is true for LOGGING but here overwritten because of element of config + EXPECT_FALSE(settings.IsEnabled(metaData)); + + metaData = ::Thunder::Core::Messaging::Metadata(::Thunder::Core::Messaging::Metadata::type::LOGGING, _T("Notification"), _T("SysLog")); + // Internal Metadata::Default() is true for LOGGING and not overwritten because of no element of config + EXPECT_TRUE(settings.IsEnabled(metaData)); } TEST_F(Core_Messaging_MessageUnit, MessageClientWillReturnListOfControls) { + ToggleDefaultConfig(true); + //this test is using metadata (IPC) passing, so no other proces tests for now - Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath()); - client.AddInstance(0); //we are in framework - auto it = client.Enabled(); + ::Thunder::Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath()); + + client.AddInstance(0 /*id*/); //we are in framework + + std::vector modules; + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); int matches = 0; int count = 0; - while (it.Next()) { - auto info = it.Current(); - if (info.first.Module() == EXPAND_AND_QUOTE(MODULE_NAME)) { + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + if (*it == EXPAND_AND_QUOTE(MODULE_NAME)) { ++matches; } ++count; } - ASSERT_GE(count, 4); - ASSERT_EQ(matches, 4); + client.RemoveInstance(0); + + EXPECT_GE(count, 2); + EXPECT_EQ(matches, 0); + + ToggleDefaultConfig(false); } TEST_F(Core_Messaging_MessageUnit, EnablingMessagesShouldUpdateExistingDefaultConfig) { - const string config = R"({"tracing":{"messages":[{"category":"ExampleCategory","module":"ExampleModule","enabled":false}]}})"; - ::Thunder::Core::Messaging::MessageUnit::Instance().Defaults(config); - const ::Thunder::Core::Messaging::MetaData toBeUpdated(::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("ExampleCategory"), _T("ExampleModule")); - ASSERT_FALSE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault(toBeUpdated)); + ::Thunder::Messaging::MessageUnit::Settings::Config configuration; + + // If 'enabled' equals false the entry is not added to 'Settings' + configuration.FromString(R"({"tracing":{"settings":[{"category":"ExampleCategory","module":"ExampleModule","enabled":false}]}})"); + + // Populate settings with specified configuration + ::Thunder::Messaging::MessageUnit::Instance().Open(Core_Messaging_MessageUnit::_basePath, configuration, Core_Messaging_MessageUnit::_background, ::Thunder::Messaging::MessageUnit::OFF); + + const ::Thunder::Core::Messaging::Metadata toBeUpdated(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("ExampleCategory"), _T("ExampleModule")); + + Control control(toBeUpdated); + // Add to the internal list if it is not already + ::Thunder::Core::Messaging::IControl::Announce(&control); + + ::Thunder::Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath() /*, socketPort not specified, domain socket used instead */); - Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath()); + // Creates a MessageUnit::Client internally with the id passed in client.AddInstance(0); //we are in framework + + // Get the system 'status' + + bool enabled = false; + + std::vector modules; + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + int matches = 0; + int count = 0; + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + enabled = enabled + || + ( toBeUpdated.Type() == item.Type() + && toBeUpdated.Category() == item.Category() + && toBeUpdated.Module() == item.Module() + && item.Enabled() + ) + ; + } + } + + EXPECT_FALSE(enabled); + + // Enable message via metadata, eg, set enable for the previously added Control, eg, enable category client.Enable(toBeUpdated, true); - ASSERT_TRUE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault(toBeUpdated)); + modules.clear(); + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + /* bool */ enabled = false; + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + enabled = enabled + || + ( toBeUpdated.Type() == item.Type() + && toBeUpdated.Category() == item.Category() + && toBeUpdated.Module() == item.Module() + && item.Enabled() + ) + ; + } + } + + EXPECT_TRUE(enabled); + + ::Thunder::Core::Messaging::IControl::Revoke(&control); + + client.RemoveInstance(0); + + // Direct call required + ::Thunder::Messaging::MessageUnit::Instance().Close(); } TEST_F(Core_Messaging_MessageUnit, EnablingMessagesShouldAddToDefaultConfigListIfNotPresent) { - const ::Thunder::Core::Messaging::MetaData toBeAdded(::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("ExampleCategory"), _T("ExampleModule")); + ToggleDefaultConfig(true); - ASSERT_FALSE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault(toBeAdded)); + const ::Thunder::Core::Messaging::Metadata toBeAdded(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("ExampleCategory"), _T("ExampleModule")); + + ::Thunder::Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath() /*, socketPort not specified, domain socket used instead */); - Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath()); client.AddInstance(0); //we are in framework - client.Enable(toBeAdded, true); - ASSERT_TRUE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault(toBeAdded)); - auto defaultsString = ::Thunder::Core::Messaging::MessageUnit::Instance().Defaults(); - ::Thunder::Core::Messaging::Settings settings; - settings.FromString(defaultsString); + std::vector modules; + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + bool enabled = false; + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + enabled = enabled + || + ( toBeAdded.Type() == item.Type() + && toBeAdded.Category() == item.Category() + && toBeAdded.Module() == item.Module() + && item.Enabled() + ) + ; + } + } + + EXPECT_FALSE(enabled); + + Control control(toBeAdded); + + ::Thunder::Core::Messaging::IControl::Announce(&control); + + modules.clear(); + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + enabled = false; + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); - ASSERT_EQ(settings.Tracing.Entries.Length(), 1); - auto entriesIt = settings.Tracing.Entries.Elements(); - while (entriesIt.Next()) { - ASSERT_STREQ(entriesIt.Current().Category.Value().c_str(), toBeAdded.Category().c_str()); - ASSERT_STREQ(entriesIt.Current().Module.Value().c_str(), toBeAdded.Module().c_str()); + while (item.Next()) { + enabled = enabled + || + ( toBeAdded.Type() == item.Type() + && toBeAdded.Category() == item.Category() + && toBeAdded.Module() == item.Module() + ) + ; + } } + + EXPECT_TRUE(enabled); + + client.RemoveInstance(0); + + ::Thunder::Core::Messaging::IControl::Revoke(&control); + + ToggleDefaultConfig(false); } TEST_F(Core_Messaging_MessageUnit, EnablingMessagesByTypeShouldEnableEverything) { - Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath()); + ToggleDefaultConfig(true); + + ::Thunder::Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath() /*, socketPort not specified, domain socket used instead */); + client.AddInstance(0); //we are in framework - auto itBeforeUpdate = client.Enabled(); - int matches = 0; - while (itBeforeUpdate.Next()) { - auto info = itBeforeUpdate.Current(); - if (info.first.Type() == ::Thunder::Core::Messaging::MetaData::MessageType::TRACING && info.second == true) { - ++matches; + std::vector modules; + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + bool enabled = true; + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + enabled = enabled + && item.Enabled() + ; } } - ASSERT_EQ(matches, 0); - matches = 0; - client.Enable({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T(""), _T("") }, true); - auto itAfterUpdate = client.Enabled(); - while (itAfterUpdate.Next()) { - auto info = itAfterUpdate.Current(); - if (info.first.Type() == ::Thunder::Core::Messaging::MetaData::MessageType::TRACING && info.second == true) { - ++matches; + // Controls from the default are disabled by default, except a few + EXPECT_FALSE(enabled); + + // Enable message via metadata, eg, set enable for the previously added Control, eg, enable category + client.Enable({::Thunder::Core::Messaging::Metadata::type::TRACING, _T(""), _T("")}, true); + + modules.clear(); + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + enabled = true; + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + enabled = enabled + && item.Enabled() + ; } } - ASSERT_GE(matches, 5); + + EXPECT_TRUE(enabled); + + client.RemoveInstance(0); + + ToggleDefaultConfig(false); } TEST_F(Core_Messaging_MessageUnit, LogMessagesCanToggledWhenLogModuleSpecified) { - Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath()); + ToggleDefaultConfig(true); + + ::Thunder::Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath() /*, socketPort not specified, domain socket used instead */); + client.AddInstance(0); //we are in framework - auto itBeforeUpdate = client.Enabled(); - ::Thunder::Core::Messaging::MetaData messageToToggle(::Thunder::Core::Messaging::MetaData::MessageType::LOGGING, _T("Test_Category_5"), _T("SysLog")); + + ::Thunder::Core::Messaging::Metadata messageToToggle(::Thunder::Core::Messaging::Metadata::type::LOGGING, _T("Test_Category_5"), _T("SysLog")); + + std::vector modules; + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); int matches = 0; - while (itBeforeUpdate.Next()) { - auto info = itBeforeUpdate.Current(); - if (info.first == messageToToggle && info.second == true) { - ++matches; + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + if ( item.Type() == messageToToggle.Type() + && item.Category() == messageToToggle.Category() + && item.Module() == messageToToggle.Module() + && item.Enabled() + ) { + ++matches; + } } } - ASSERT_EQ(matches, 1); - matches = 0; + EXPECT_EQ(matches, 1); + client.Enable(messageToToggle, false); - auto itAfterUpdate = client.Enabled(); - while (itAfterUpdate.Next()) { - auto info = itAfterUpdate.Current(); - if (info.first == messageToToggle && info.second == false) { - ++matches; + + modules.clear(); + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + matches = 0; + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + if ( item.Type() == messageToToggle.Type() + && item.Category() == messageToToggle.Category() + && item.Module() == messageToToggle.Module() + && !item.Enabled() + ) { + ++matches; + } } } - ASSERT_EQ(matches, 1); + + EXPECT_EQ(matches, 1); + + client.RemoveInstance(0); + + ToggleDefaultConfig(false); } TEST_F(Core_Messaging_MessageUnit, LogEnablingMessagesShouldAddToDefaultConfigListIfNotPresent) { - const ::Thunder::Core::Messaging::MetaData tobeAdded(::Thunder::Core::Messaging::MetaData::MessageType::LOGGING, _T("Test_Category_5"), _T("SysLog")); - ASSERT_TRUE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault(tobeAdded)); + ToggleDefaultConfig(true); + + const ::Thunder::Core::Messaging::Metadata toBeAdded(::Thunder::Core::Messaging::Metadata::type::LOGGING, _T("Test_Category_5"), _T("SysLog")); + + ::Thunder::Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath() /*, socketPort not specified, domain socket used instead */); - Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath()); client.AddInstance(0); //we are in framework - client.Enable(tobeAdded, false); - ASSERT_FALSE(::Thunder::Core::Messaging::MessageUnit::Instance().IsEnabledByDefault(tobeAdded)); - auto defaultsString = ::Thunder::Core::Messaging::MessageUnit::Instance().Defaults(); - ::Thunder::Core::Messaging::Settings settings; - settings.FromString(defaultsString); + // LOGGING is enabled and available by default + + std::vector modules; + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + bool enabled = false; + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; - ASSERT_EQ(settings.Logging.Entries.Length(), 1); - auto entriesIt = settings.Logging.Entries.Elements(); - while (entriesIt.Next()) { - ASSERT_STREQ(entriesIt.Current().Category.Value().c_str(), tobeAdded.Category().c_str()); - ASSERT_STREQ(entriesIt.Current().Module.Value().c_str(), tobeAdded.Module().c_str()); + client.Controls(item, *it); + + while (item.Next()) { + enabled = enabled + || + ( toBeAdded.Type() == item.Type() + && toBeAdded.Category() == item.Category() + && toBeAdded.Module() == item.Module() + && item.Enabled() + ) + ; + } } + + EXPECT_TRUE(enabled); + + client.RemoveInstance(0); + + ToggleDefaultConfig(false); } TEST_F(Core_Messaging_MessageUnit, EnablingFullySpecifiedMessageUpdateOnlyThisOne) { - Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath()); + ToggleDefaultConfig(true); + + ::Thunder::Core::Messaging::Metadata message(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("Test_Category_1"), EXPAND_AND_QUOTE(MODULE_NAME)); + + ::Thunder::Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath() /*, socketPort not specified, domain socket used instead */); + client.AddInstance(0); //we are in framework - auto itBeforeUpdate = client.Enabled(); - ::Thunder::Core::Messaging::MetaData message(::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("Test_Category_1"), EXPAND_AND_QUOTE(MODULE_NAME)); - int matches = 0; - while (itBeforeUpdate.Next()) { - auto info = itBeforeUpdate.Current(); - if (info.first == message && info.second == false) { - ++matches; + // TRACING is not enabled but available by default + + std::vector modules; + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + bool enabled = false; + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + enabled = enabled + || + ( message.Type() == item.Type() + && message.Category() == item.Category() + && message.Module() == item.Module() + && item.Enabled() + ) + ; } } - ASSERT_EQ(matches, 1); - matches = 0; + EXPECT_FALSE(enabled); + client.Enable(message, true); - auto itAfterUpdate = client.Enabled(); - while (itAfterUpdate.Next()) { - auto info = itAfterUpdate.Current(); - if (info.first == message && info.second == true) { - ++matches; + + client.Enable(message, true); + + modules.clear(); + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + enabled = false; + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + enabled = enabled + || + ( message.Type() == item.Type() + && message.Category() == item.Category() + && message.Module() == item.Module() + && item.Enabled() + ) + ; } } - ASSERT_EQ(matches, 1); + + EXPECT_TRUE(enabled); + + client.RemoveInstance(0); + + ToggleDefaultConfig(false); } TEST_F(Core_Messaging_MessageUnit, EnablingMessageSpecifiedByModuleShouldEnableAllCategoriesInsideIt) { - Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath()); + ToggleDefaultConfig(false); + + const ::Thunder::Core::Messaging::Metadata message(::Thunder::Core::Messaging::Metadata::type::TRACING, _T(""), EXPAND_AND_QUOTE(MODULE_NAME)); + + ::Thunder::Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath() /*, socketPort not specified, domain socket used instead */); + client.AddInstance(0); //we are in framework - auto itBeforeUpdate = client.Enabled(); - - int enabled = 0; - while (itBeforeUpdate.Next()) { - auto info = itBeforeUpdate.Current(); - if (info.first.Type() == ::Thunder::Core::Messaging::MetaData::MessageType::TRACING && info.first.Module() == EXPAND_AND_QUOTE(MODULE_NAME)) { - if (info.second == true) { - ++enabled; + + std::vector modules; + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + bool enabled = true; + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + if ( message.Type() == item.Type() + && message.Module() == item.Module() + ) { + enabled = enabled + && item.Enabled() + ; } } } - ASSERT_EQ(enabled, 0); - enabled = 0; - client.Enable({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T(""), EXPAND_AND_QUOTE(MODULE_NAME) }, true); - auto itAfterUpdate = client.Enabled(); - while (itAfterUpdate.Next()) { - auto info = itAfterUpdate.Current(); - if (info.first.Type() == ::Thunder::Core::Messaging::MetaData::MessageType::TRACING && info.first.Module() == EXPAND_AND_QUOTE(MODULE_NAME)) { - if (info.second == true) { - ++enabled; + EXPECT_FALSE(enabled); + + client.Enable(message, true); + + modules.clear(); + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + enabled = true; + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + if ( message.Type() == item.Type() + && message.Module() == item.Module() + ) { + enabled = enabled + && item.Enabled() + ; } } } - ASSERT_EQ(enabled, 4); + EXPECT_TRUE(enabled); + + client.RemoveInstance(0); + + ToggleDefaultConfig(false); } TEST_F(Core_Messaging_MessageUnit, EnablingMessageSpecifiedByCategoryShouldEnableItInAllModules) { - Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath()); + ToggleDefaultConfig(true); + + const ::Thunder::Core::Messaging::Metadata message(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("Test_Category_1"), _T("")); + + ::Thunder::Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath() /*, socketPort not specified, domain socket used instead */); + client.AddInstance(0); //we are in framework - auto itBeforeUpdate = client.Enabled(); - - int enabled = 0; - while (itBeforeUpdate.Next()) { - auto info = itBeforeUpdate.Current(); - if (info.first.Type() == ::Thunder::Core::Messaging::MetaData::MessageType::TRACING && info.first.Category() == _T("Test_Category_1")) { - if (info.second == true) { - ++enabled; + + std::vector modules; + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + bool enabled = true; + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + if ( message.Type() == item.Type() + && message.Category() == item.Category() + ) { + enabled = enabled + && item.Enabled() + ; } } } - ASSERT_EQ(enabled, 0); - enabled = 0; - client.Enable({ ::Thunder::Core::Messaging::MetaData::MessageType::TRACING, _T("Test_Category_1"), _T("") }, true); - auto itAfterUpdate = client.Enabled(); - while (itAfterUpdate.Next()) { - auto info = itAfterUpdate.Current(); - if (info.first.Type() == ::Thunder::Core::Messaging::MetaData::MessageType::TRACING && info.first.Category() == _T("Test_Category_1")) { - if (info.second == true) { - ++enabled; + EXPECT_FALSE(enabled); + + client.Enable(message, true); + + modules.clear(); + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + enabled = true; + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + if ( message.Type() == item.Type() + && message.Category() == item.Category() + ) { + enabled = enabled + && item.Enabled() + ; } } } - ASSERT_EQ(enabled, 2); + EXPECT_TRUE(enabled); + + client.RemoveInstance(0); + + ToggleDefaultConfig(false); } TEST_F(Core_Messaging_MessageUnit, TextMessageEventIsProperlySerializedIfBufferBigEnough) { - uint8_t buffer[1 * 1024]; + constexpr string::size_type bufferSize = 1024; + + uint8_t buffer[bufferSize]; const string testTextMessage = _T("TEST MESSAGE"); - Messaging::TextMessage tm(testTextMessage); + EXPECT_GT(bufferSize, sizeof(testTextMessage.size())); + + ::Thunder::Messaging::TextMessage tm(testTextMessage); auto serialized = tm.Serialize(buffer, sizeof(buffer)); - ASSERT_GT(serialized, 0); + EXPECT_GT(serialized, 0); auto deserialized = tm.Deserialize(buffer, sizeof(buffer)); - ASSERT_EQ(serialized, deserialized); + EXPECT_EQ(serialized, deserialized); - string result; - tm.ToString(result); - ASSERT_STREQ(result.c_str(), testTextMessage.c_str()); + string result = tm.Data(); + EXPECT_STREQ(result.c_str(), testTextMessage.c_str()); } TEST_F(Core_Messaging_MessageUnit, TextMessageEventIsProperlySerializedAndCutIfBufferNotBigEnough) { - uint8_t buffer[5]; + constexpr string::size_type bufferSize = 5; + + uint8_t buffer[bufferSize]; const string testTextMessage = _T("abcdefghi"); - Messaging::TextMessage tm(testTextMessage); + EXPECT_LT(bufferSize, sizeof(testTextMessage.size())); + + ::Thunder::Messaging::TextMessage tm(testTextMessage); auto serialized = tm.Serialize(buffer, sizeof(buffer)); - ASSERT_GT(serialized, 0); + EXPECT_GT(serialized, 0); auto deserialized = tm.Deserialize(buffer, serialized); - ASSERT_EQ(serialized, deserialized); + EXPECT_EQ(serialized, deserialized); - string result; - tm.ToString(result); - //last byte reserved for null termination - ASSERT_STREQ(result.c_str(), _T("abcd")); + string result = tm.Data(); + + EXPECT_STREQ(result.c_str(), testTextMessage.substr(0, bufferSize - 1).c_str()); } TEST_F(Core_Messaging_MessageUnit, ControlListIsProperlySerializedIfBufferBigEnough) { - uint8_t buffer[1 * 1024]; + ToggleDefaultConfig(true); - ::Thunder::Core::Messaging::ControlList cl; - for (const auto& control : _controls) { - cl.Announce(control.get()); - } + constexpr string::size_type bufferSize = 1024; - auto serialized = cl.Serialize(buffer, sizeof(buffer)); - ASSERT_GT(serialized, 0); - ASSERT_EQ(buffer[0], _controls.size()); + uint8_t buffer[bufferSize]; - auto deserialized = cl.Deserialize(buffer, serialized); - ASSERT_EQ(serialized, deserialized); + ::Thunder::Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath() /*, socketPort not specified, domain socket used instead */); + + client.AddInstance(0); //we are in framework - auto informationIt = cl.Information(); - auto controlsIt = _controls.cbegin(); - while (informationIt.Next()) { - ASSERT_EQ(informationIt.Current().first, controlsIt->get()->MessageMetaData()); - ++controlsIt; + std::vector modules; + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + ::Thunder::Messaging::MessageUnit::Control control({item.Type(), item.Category(), item.Module()}, item.Enabled()); + auto serialized = control.Serialize(buffer, sizeof(buffer)); + + EXPECT_GT(serialized, 0); + + auto deserialized = control.Deserialize(buffer, serialized); + + EXPECT_EQ(serialized, deserialized); + } } + + client.RemoveInstance(0); + + ToggleDefaultConfig(false); } TEST_F(Core_Messaging_MessageUnit, ControlListIsProperlySerializedIfBufferNotBigEnough) { - const int controlsThatShouldFit = 2; - uint16_t maxBufferSize = 0; - auto it = _controls.cbegin(); - for (int i = 0; i < controlsThatShouldFit; ++i, ++it) { - maxBufferSize += sizeof(it->get()->MessageMetaData().Type()); - maxBufferSize += it->get()->MessageMetaData().Category().size() + 1; - maxBufferSize += it->get()->MessageMetaData().Module().size() + 1; - maxBufferSize += sizeof(bool); - } + ToggleDefaultConfig(true); + + ::Thunder::Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath() /*, socketPort not specified, domain socket used instead */); + + client.AddInstance(0); //we are in framework + + std::vector modules; + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); std::vector buffer; - buffer.resize(maxBufferSize + 1); - ::Thunder::Core::Messaging::ControlList cl; - for (const auto& control : _controls) { - cl.Announce(control.get()); + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + buffer.resize(buffer.size() + sizeof(item.Type()), ::Thunder::Core::Messaging::Metadata::type::INVALID); + buffer.resize(buffer.size() + item.Category().size() + 1, ::Thunder::Core::Messaging::Metadata::type::INVALID); + buffer.resize(buffer.size() + item.Module().size() + 1, ::Thunder::Core::Messaging::Metadata::type::INVALID); + buffer.resize(buffer.size() + sizeof(bool), ::Thunder::Core::Messaging::Metadata::type::INVALID); + } } - auto serialized = cl.Serialize(buffer.data(), buffer.size()); - ASSERT_GT(serialized, 0); - ASSERT_EQ(buffer[0], controlsThatShouldFit); + buffer.resize(buffer.size() + 1); - auto deserialized = cl.Deserialize(buffer.data(), serialized); - ASSERT_EQ(serialized, deserialized); + uint16_t index = 0; - auto informationIt = cl.Information(); - auto controlsIt = _controls.cbegin(); - while (informationIt.Next()) { - ASSERT_EQ(informationIt.Current().first, controlsIt->get()->MessageMetaData()); - ++controlsIt; + modules.clear(); + client.Modules(modules); + +PRINT_MODULES(__PRETTY_FUNCTION__, modules); + + for (auto it = modules.begin(), end = modules.end(); it != end; it++) { + ::Thunder::Messaging::MessageUnit::Iterator item; + + client.Controls(item, *it); + + while (item.Next()) { + ::Thunder::Messaging::MessageUnit::Control control({item.Type(), item.Category(), item.Module()}, item.Enabled()); + auto serialized = control.Serialize(&(buffer.data()[index]), buffer.size()); + + EXPECT_GT(serialized, 0); + + EXPECT_GT(buffer.size(), serialized); + + auto deserialized = control.Deserialize(&buffer.data()[index], serialized); + + index += serialized; + + EXPECT_EQ(serialized, deserialized); + } } + + EXPECT_LT(index, buffer.size()); + + client.RemoveInstance(0); + + ToggleDefaultConfig(false); } TEST_F(Core_Messaging_MessageUnit, PopMessageShouldReturnLastPushedMessage) { - const string traceMessage = _T("some trace"); - Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath()); + ToggleDefaultConfig(true); + + ::Thunder::Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath() /*, socketPort not specified, domain socket used instead */); + client.AddInstance(0); //we are in framework //factory should be added before attempting to pop data - Messaging::TraceFactory factory; - client.AddFactory(::Thunder::Core::Messaging::MetaData::MessageType::TRACING, &factory); - - Messaging::TextMessage tm(traceMessage); - ::Thunder::Core::Messaging::Information info(::Thunder::Core::Messaging::MetaData::MessageType::TRACING, - _T("some_category"), - EXPAND_AND_QUOTE(MODULE_NAME), - _T("some_file.cpp"), - 1337, - ::Thunder::Core::Time::Now().Ticks()); - - ::Thunder::Core::Messaging::MessageUnit::Instance().Push(info, &tm); - - auto messages = client.PopMessagesAsList(); - ASSERT_EQ(messages.size(), 1); - auto message = messages.front(); - - ASSERT_NE(message.first.MessageMetaData().Type(), ::Thunder::Core::Messaging::MetaData::MessageType::INVALID); - ASSERT_EQ(message.first.MessageMetaData(), info.MessageMetaData()); - - string result; - message.second->ToString(result); - ASSERT_STREQ(message.first.FileName().c_str(), info.FileName().c_str()); - ASSERT_EQ(message.first.LineNumber(), info.LineNumber()); - ASSERT_EQ(message.first.TimeStamp(), info.TimeStamp()); - ASSERT_STREQ(traceMessage.c_str(), result.c_str()); + ::Thunder::Messaging::TraceFactoryType<::Thunder::Core::Messaging::IStore::Tracing, ::Thunder::Messaging::TextMessage> factory; + client.AddFactory(::Thunder::Core::Messaging::Metadata::type::TRACING, &factory); + + ::Thunder::Core::Messaging::Metadata metadata(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("some_category"), EXPAND_AND_QUOTE(MODULE_NAME)); + + client.Enable(metadata, true); + + const string traceMessage = _T("some trace"); + ::Thunder::Messaging::TextMessage tm(traceMessage); + + ::Thunder::Core::Messaging::IStore::Tracing info(::Thunder::Core::Messaging::MessageInfo(metadata, ::Thunder::Core::Time::Now().Ticks()), _T("some_file"), 1337, EXPAND_AND_QUOTE(MODULE_NAME)); + + ::Thunder::Messaging::MessageUnit::Instance().Push(info, &tm); + + // Instead 'flush' and continue + client.SkipWaiting(); + + bool present = false; + + client.PopMessagesAndCall( + [&](const ::Thunder::Core::ProxyType<::Thunder::Core::Messaging::MessageInfo>& metadata, const ::Thunder::Core::ProxyType<::Thunder::Core::Messaging::IEvent>& message) { + //(*metadata).TimeStamp(); + //(*metadata).Module(); + //(*metadata).Category(); + + if ((*metadata).Type() == ::Thunder::Core::Messaging::Metadata::type::TRACING) { + TRACE_L1( + _T("PopMessagesAndCall : Tracing message -> Filename : %s, Linenumber : %d, Classname : %s") + , static_cast<::Thunder::Core::Messaging::IStore::Tracing&>(*metadata).FileName().c_str() + , static_cast<::Thunder::Core::Messaging::IStore::Tracing&>(*metadata).LineNumber() + , static_cast<::Thunder::Core::Messaging::IStore::Tracing&>(*metadata).ClassName().c_str() + ); + + present = present || (*message).Data() == traceMessage; + } else { + TRACE_L1(_T("PopMessagesAndCall : Unknown message")); + } + + // By defining a callback data could be further processed + } + ); + + EXPECT_TRUE(present); + + client.RemoveInstance(0); + + ToggleDefaultConfig(false); } TEST_F(Core_Messaging_MessageUnit, PopMessageShouldReturnLastPushedMessageInOtherProcess) { + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 15; + const string traceMessage = _T("some trace"); - Messaging::TextMessage tm(traceMessage); - ::Thunder::Core::Messaging::Information info(::Thunder::Core::Messaging::MetaData::MessageType::TRACING, - _T("some_category"), - EXPAND_AND_QUOTE(MODULE_NAME), - _T("some_file.cpp"), - 1337, - ::Thunder::Core::Time::Now().Ticks()); - - auto lambdaFunc = [&](IPTestAdministrator& testAdmin) { - Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath()); + + ::Thunder::Core::Messaging::Metadata metadata(::Thunder::Core::Messaging::Metadata::type::TRACING, _T("some_category"), EXPAND_AND_QUOTE(MODULE_NAME)); + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + + ::Thunder::Messaging::MessageClient client(DispatcherIdentifier(), DispatcherBasePath() /*, socketPort not specified, domain socket used instead */); + client.AddInstance(0); - Messaging::TraceFactory factory; - client.AddFactory(::Thunder::Core::Messaging::MetaData::MessageType::TRACING, &factory); - testAdmin.Sync("setup"); - testAdmin.Sync("writer wrote"); - auto messages = client.PopMessagesAsList(); - ASSERT_EQ(messages.size(), 1); - auto message = messages.front(); + ::Thunder::Messaging::TraceFactoryType<::Thunder::Core::Messaging::IStore::Tracing, ::Thunder::Messaging::TextMessage> factory; + client.AddFactory(::Thunder::Core::Messaging::Metadata::type::TRACING, &factory); - ASSERT_NE(message.first.MessageMetaData().Type(), ::Thunder::Core::Messaging::MetaData::MessageType::INVALID); - ASSERT_EQ(message.first.MessageMetaData(), info.MessageMetaData()); + client.Enable(metadata, true); - string result; - message.second->ToString(result); - ASSERT_STREQ(message.first.FileName().c_str(), info.FileName().c_str()); - ASSERT_EQ(message.first.LineNumber(), info.LineNumber()); - ASSERT_EQ(message.first.TimeStamp(), info.TimeStamp()); - ASSERT_STREQ(traceMessage.c_str(), result.c_str()); - - testAdmin.Sync("reader read"); - testAdmin.Sync("done"); + client.WaitForUpdates(maxWaitTimeMs); + + client.PopMessagesAndCall( + [&](const ::Thunder::Core::ProxyType<::Thunder::Core::Messaging::MessageInfo>& metadata, const ::Thunder::Core::ProxyType<::Thunder::Core::Messaging::IEvent>& message) { + if ((*metadata).Type() == ::Thunder::Core::Messaging::Metadata::type::TRACING) { + TRACE_L1( + _T("PopMessagesAndCall : Tracing message -> Filename : %s, Linenumber : %d, Classname : %s") + , static_cast<::Thunder::Core::Messaging::IStore::Tracing&>(*metadata).FileName().c_str() + , static_cast<::Thunder::Core::Messaging::IStore::Tracing&>(*metadata).LineNumber() + , static_cast<::Thunder::Core::Messaging::IStore::Tracing&>(*metadata).ClassName().c_str() + ); + + EXPECT_TRUE((*message).Data() == traceMessage); + } + } + ); + + client.RemoveFactory(::Thunder::Core::Messaging::Metadata::TRACING); + + client.RemoveInstance(0); + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); }; - static std::function lambdaVar = lambdaFunc; - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin) { lambdaVar(testAdmin); }; - IPTestAdministrator testAdmin(otherSide); + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + ToggleDefaultConfig(true); - { - testAdmin.Sync("setup"); - testAdmin.Sync("writer wrote"); - ::Thunder::Core::Messaging::MessageUnit::Instance().Push(info, &tm); - testAdmin.Sync("reader read"); - } - testAdmin.Sync("done"); + // a small delay so the child can be set up + SleepMs(maxInitTime); + + ::Thunder::Messaging::TextMessage tm(traceMessage); + + ::Thunder::Core::Messaging::IStore::Tracing info(::Thunder::Core::Messaging::MessageInfo(metadata, ::Thunder::Core::Time::Now().Ticks()), _T("some_file"), 1337, EXPAND_AND_QUOTE(MODULE_NAME)); + + ::Thunder::Messaging::MessageUnit::Instance().Push(info, &tm); + + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + + ToggleDefaultConfig(false); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + + ::Thunder::Core::Singleton::Dispose(); } } // Core diff --git a/Tests/unit/core/test_rpc.cpp b/Tests/unit/core/test_rpc.cpp index c2fdf2d9e..83cd7c632 100644 --- a/Tests/unit/core/test_rpc.cpp +++ b/Tests/unit/core/test_rpc.cpp @@ -221,7 +221,7 @@ namespace Exchange { public: ExternalAccess() = delete; ExternalAccess(const ExternalAccess &) = delete; - ExternalAccess & operator=(const ExternalAccess &) = delete; + ExternalAccess& operator=(const ExternalAccess &) = delete; ExternalAccess(const ::Thunder::Core::NodeId & source) : ::Thunder::RPC::Communicator(source, _T("")) @@ -251,59 +251,62 @@ namespace Exchange { TEST(Core_RPC, adder) { -#ifndef __APPLE__ - std::string connector{"/tmp/wperpc01"}; - auto lambdaFunc = [connector](IPTestAdministrator & testAdmin) { - ::Thunder::Core::NodeId remoteNode(connector.c_str()); + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; - ExternalAccess communicator(remoteNode); + const std::string connector{"/tmp/wperpc01"}; - testAdmin.Sync("setup server"); + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { + ::Thunder::Core::NodeId remoteNode(connector.c_str()); - testAdmin.Sync("done testing"); + ExternalAccess communicator(remoteNode); - communicator.Close(::Thunder::Core::infinite); - }; + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); - static std::function lambdaVar = lambdaFunc; + EXPECT_EQ(communicator.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + }; - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + // A small delay so the child can be set up + SleepMs(maxInitTime); - IPTestAdministrator testAdmin(otherSide); + ::Thunder::Core::NodeId remoteNode(connector.c_str()); - testAdmin.Sync("setup server"); + ::Thunder::Core::ProxyType<::Thunder::RPC::InvokeServerType<4, 0, 1>> engine = ::Thunder::Core::ProxyType<::Thunder::RPC::InvokeServerType<4, 0, 1>>::Create(); + ASSERT_TRUE(engine.IsValid()); - { - ::Thunder::Core::NodeId remoteNode(connector.c_str()); + ::Thunder::Core::ProxyType<::Thunder::RPC::CommunicatorClient> client = ::Thunder::Core::ProxyType<::Thunder::RPC::CommunicatorClient>::Create(remoteNode, ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer>(engine)); + ASSERT_TRUE(client.IsValid()); - ::Thunder::Core::ProxyType<::Thunder::RPC::InvokeServerType<4, 0, 1>> engine = ::Thunder::Core::ProxyType<::Thunder::RPC::InvokeServerType<4, 0, 1>>::Create(); - EXPECT_TRUE(engine.IsValid()); - ::Thunder::Core::ProxyType<::Thunder::RPC::CommunicatorClient> client = ::Thunder::Core::ProxyType<::Thunder::RPC::CommunicatorClient>::Create(remoteNode, ::Thunder::Core::ProxyType<::Thunder::Core::IIPCServer>(engine)); - EXPECT_TRUE(client.IsValid()); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - // Create remote instance of "Thunder::Tests::Core::Exchange::IAdder". - Thunder::Tests::Core::Exchange::IAdder * adder = client->Open(_T("Adder")); + // Create remote instance of "Thunder::Tests::Core::Exchange::IAdder". + Thunder::Tests::Core::Exchange::IAdder* adder = client->Open(_T("Adder")); + ASSERT_TRUE(adder != nullptr); - ASSERT_TRUE(adder != nullptr); + // Perform some arithmatic. + EXPECT_EQ(adder->GetValue(), static_cast(0)); + adder->Add(20); + EXPECT_EQ(adder->GetValue(), static_cast(20)); + adder->Add(22); + EXPECT_EQ(adder->GetValue(), static_cast(42)); - // Perform some arithmatic. - EXPECT_EQ(adder->GetValue(), static_cast(0)); - adder->Add(20); - EXPECT_EQ(adder->GetValue(), static_cast(20)); - adder->Add(22); - EXPECT_EQ(adder->GetValue(), static_cast(42)); + // Make sure other side is indeed running in other process. + EXPECT_NE(adder->GetPid(), static_cast(getpid())); - // Make sure other side is indeed running in other process. - EXPECT_NE(adder->GetPid(), (uint32_t)getpid()); + EXPECT_EQ(adder->Release(), ::Thunder::Core::ERROR_DESTRUCTION_SUCCEEDED); - adder->Release(); + ASSERT_EQ(client->Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); - client->Close(::Thunder::Core::infinite); - } + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + }; - testAdmin.Sync("done testing"); - ::Thunder::Core::Singleton::Dispose(); -#endif + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + + ::Thunder::Core::Singleton::Dispose(); } } // Core diff --git a/Tests/unit/core/test_socketstreamjson.cpp b/Tests/unit/core/test_socketstreamjson.cpp index 6441a3054..57762452d 100644 --- a/Tests/unit/core/test_socketstreamjson.cpp +++ b/Tests/unit/core/test_socketstreamjson.cpp @@ -187,7 +187,7 @@ namespace Core { return (true); } - int Wait() const + uint32_t Wait() const { return _dataPending.Lock(); } @@ -224,46 +224,71 @@ namespace Core { TEST(Core_Socket, StreamJSON) { - std::string connector = "/tmp/wpestreamjson0"; - auto lambdaFunc = [connector](IPTestAdministrator & testAdmin) { + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; + + const std::string connector = "/tmp/wpestreamjson0"; + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { ::Thunder::Core::SocketServerType> jsonSocketServer(::Thunder::Core::NodeId(connector.c_str())); - jsonSocketServer.Open(::Thunder::Core::infinite); - testAdmin.Sync("setup server"); + + ASSERT_EQ(jsonSocketServer.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + std::unique_lock lk(JSONConnector<::Thunder::Core::JSON::IElement>::_mutex); + while (!JSONConnector<::Thunder::Core::JSON::IElement>::GetState()) { JSONConnector<::Thunder::Core::JSON::IElement>::_cv.wait(lk); } - testAdmin.Sync("client open"); - testAdmin.Sync("client done"); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(jsonSocketServer.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); }; - static std::function lambdaVar = lambdaFunc; + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + // a small delay so the child can be set up + SleepMs(maxInitTime); - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; + ASSERT_EQ(testAdmin.Signal(initHandshakeValue), ::Thunder::Core::ERROR_NONE); - IPTestAdministrator testAdmin(otherSide); - testAdmin.Sync("setup server"); - { ::Thunder::Core::ProxyType sendObject = ::Thunder::Core::ProxyType::Create(); + ASSERT_TRUE(sendObject.IsValid()); + sendObject->Identifier = 1; sendObject->Name = _T("TestCase"); sendObject->Params.Duration = 100; + std::string sendString; - sendObject->ToString(sendString); + EXPECT_TRUE(sendObject->ToString(sendString)); JSONConnector<::Thunder::Core::JSON::IElement> jsonSocketClient(::Thunder::Core::NodeId(connector.c_str())); - jsonSocketClient.Open(::Thunder::Core::infinite); - testAdmin.Sync("client open"); + + ASSERT_EQ(jsonSocketClient.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + jsonSocketClient.Submit(::Thunder::Core::ProxyType<::Thunder::Core::JSON::IElement>(sendObject)); - jsonSocketClient.Wait(); + + EXPECT_EQ(jsonSocketClient.Wait(), ::Thunder::Core::ERROR_NONE); + string received; jsonSocketClient.Retrieve(received); + EXPECT_STREQ(sendString.c_str(), received.c_str()); - jsonSocketClient.Close(::Thunder::Core::infinite); - testAdmin.Sync("client done"); - } - ::Thunder::Core::Singleton::Dispose(); + + EXPECT_EQ(jsonSocketClient.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + + ::Thunder::Core::Singleton::Dispose(); } } // Core diff --git a/Tests/unit/core/test_socketstreamtext.cpp b/Tests/unit/core/test_socketstreamtext.cpp index 59fe3ed64..a91165873 100644 --- a/Tests/unit/core/test_socketstreamtext.cpp +++ b/Tests/unit/core/test_socketstreamtext.cpp @@ -72,7 +72,7 @@ namespace Core { } } - int Wait() const + uint32_t Wait() const { return _dataPending.Lock(); } @@ -120,39 +120,58 @@ namespace Core { TEST(Core_Socket, StreamText) { - std::string connector {"/tmp/wpestreamtext0"}; + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; - auto lambdaFunc = [connector](IPTestAdministrator & testAdmin) { + const std::string connector {"/tmp/wpestreamtext0"}; + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { ::Thunder::Core::SocketServerType textSocketServer(::Thunder::Core::NodeId(connector.c_str())); - textSocketServer.Open(::Thunder::Core::infinite); - testAdmin.Sync("setup server"); + + ASSERT_EQ(textSocketServer.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + std::unique_lock lk(TextConnector::_mutex); + while (!TextConnector::GetState()) { TextConnector::_cv.wait(lk); } - testAdmin.Sync("server open"); - testAdmin.Sync("client done"); - }; - static std::function lambdaVar = lambdaFunc; + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; + ASSERT_EQ(textSocketServer.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + // a small delay so the child can be set up + SleepMs(maxInitTime); - IPTestAdministrator testAdmin(otherSide); - testAdmin.Sync("setup server"); - { TextConnector textSocketClient(::Thunder::Core::NodeId(connector.c_str())); - textSocketClient.Open(::Thunder::Core::infinite); - testAdmin.Sync("server open"); - string message = "hello"; + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(textSocketClient.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + const string message = "hello"; textSocketClient.Submit(message); - textSocketClient.Wait(); + EXPECT_EQ(textSocketClient.Wait(), ::Thunder::Core::ERROR_NONE); + string received; textSocketClient.Retrieve(received); + EXPECT_STREQ(message.c_str(), received.c_str()); - textSocketClient.Close(::Thunder::Core::infinite); - testAdmin.Sync("client done"); - } + + ASSERT_EQ(textSocketClient.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + ::Thunder::Core::Singleton::Dispose(); } diff --git a/Tests/unit/core/test_statetrigger.cpp b/Tests/unit/core/test_statetrigger.cpp index 4af677dec..5fd152885 100644 --- a/Tests/unit/core/test_statetrigger.cpp +++ b/Tests/unit/core/test_statetrigger.cpp @@ -29,9 +29,6 @@ namespace Thunder { namespace Tests { namespace Core { - - - enum class TestState { TEST_INIT = 0x00, TEST_MESSAGE = 0x01, diff --git a/Tests/unit/core/test_synchronous.cpp b/Tests/unit/core/test_synchronous.cpp index abd8623e5..0126ebf62 100644 --- a/Tests/unit/core/test_synchronous.cpp +++ b/Tests/unit/core/test_synchronous.cpp @@ -130,6 +130,8 @@ namespace Core { class SynchronousSocket : public ::Thunder::Core::SynchronousChannelType<::Thunder::Core::SocketPort> { public: + static constexpr uint32_t maxWaitTimeMs = 4000; + SynchronousSocket(const SynchronousSocket&) = delete; SynchronousSocket& operator=(const SynchronousSocket&) = delete; SynchronousSocket() = delete; @@ -146,12 +148,12 @@ namespace Core { , bufferSize, bufferSize ) { - EXPECT_FALSE(::Thunder::Core::SynchronousChannelType<::Thunder::Core::SocketPort>::Open(::Thunder::Core::infinite) != ::Thunder::Core::ERROR_NONE); + EXPECT_EQ(::Thunder::Core::SynchronousChannelType<::Thunder::Core::SocketPort>::Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); } virtual ~SynchronousSocket() { - ::Thunder::Core::SynchronousChannelType<::Thunder::Core::SocketPort>::Close(::Thunder::Core::infinite); + EXPECT_EQ(::Thunder::Core::SynchronousChannelType<::Thunder::Core::SocketPort>::Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); } virtual uint16_t Deserialize(const uint8_t* dataFrame, const uint16_t availableData) @@ -162,34 +164,49 @@ namespace Core { TEST(test_synchronous, simple_synchronous) { - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin) { + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 15; // Approximately 150% maxWaitTime + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { SynchronousSocket synchronousServerSocket(true); - testAdmin.Sync("setup server"); - testAdmin.Sync("connect client"); - testAdmin.Sync("client msg"); - testAdmin.Sync("client newmsg"); - testAdmin.Sync("client revokemsg"); + // a small delay so the parent can be set up + SleepMs(maxInitTime); + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); }; - IPTestAdministrator testAdmin(otherSide); - { - testAdmin.Sync("setup server"); + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + SynchronousSocket synchronousClientSocket(false); - testAdmin.Sync("connect client"); - uint8_t buffer[] = "Hello"; - Message message(5,buffer); - EXPECT_EQ(synchronousClientSocket.Exchange(500, message),0u); - testAdmin.Sync("client msg"); - - InMessage response; - Message newmessage(5,buffer); - synchronousClientSocket.Exchange(500, newmessage, response);//TODO Output verification is pending. - testAdmin.Sync("client newmsg"); + uint8_t buffer1[] = "Hello"; + Message message(sizeof(buffer1), buffer1); + // Outbound + EXPECT_EQ(synchronousClientSocket.Exchange(maxWaitTimeMs, message), ::Thunder::Core::ERROR_NONE); + synchronousClientSocket.Revoke(message); - testAdmin.Sync("client revokemsg"); - } + + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + + InMessage response; + uint8_t buffer2[] = "olleH"; + Message newmessage(sizeof(buffer2), buffer2); + // Inbound +// EXPECT_EQ(synchronousClientSocket.Exchange(maxWaitTimeMs, newmessage, response), ::Thunder::Core::ERROR_NONE); + + synchronousClientSocket.Revoke(newmessage); + + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + ::Thunder::Core::Singleton::Dispose(); } diff --git a/Tests/unit/core/test_tracing.cpp b/Tests/unit/core/test_tracing.cpp deleted file mode 100644 index c0b67e3c9..000000000 --- a/Tests/unit/core/test_tracing.cpp +++ /dev/null @@ -1,359 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2020 Metrological - * - * 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. - */ - -#include - -#ifndef MODULE_NAME -#include "../Module.h" -#endif - -#include - -#include "../IPTestAdministrator.h" - -namespace Thunder { -namespace Tests { -namespace Core { - -#pragma pack(push) -#pragma pack(1) - struct TraceHeader - { - uint16_t _Length; - uint64_t _ClockTicks; - uint32_t _LineNumber; - }; -#pragma pack(pop) - - struct TraceData - { - TraceHeader _Header; - string _File; - string _Module; - string _Category; - string _Class; - string _Text; - - string ToString() - { - std::stringstream output; - output << _File << "(" << _Header._LineNumber << "): " << _Text; - return output.str(); - } - }; - - class ServerCyclicBuffer01 : public CyclicBuffer - { - public: - ServerCyclicBuffer01(const string& fileName, uint32_t size) - : CyclicBuffer(fileName, File::USER_WRITE|File::USER_READ|File::SHAREABLE, size, true) - { - } - - virtual uint32_t GetReadSize(Cursor& cursor) override - { - uint16_t entrySize = 0; - cursor.Peek(entrySize); - return entrySize; - } - }; - - bool ReadTraceString(const uint8_t buffer[], uint32_t length, uint32_t& offset, string& output) - { - output = ""; - - const char * charBuffer = reinterpret_cast(buffer); - - while (true) { - char c = charBuffer[offset]; - - if (c == '\0') { - // Found the end - offset++; - return true; - } - - output += string(1, c); - offset++; - - if (offset >= length) { - // Buffer overrun - return false; - } - } - - return true; - } - - bool ParseTraceData(const uint8_t buffer[], uint32_t length, uint32_t& offset, TraceData& traceData, uint32_t bufferSize) - { - uint32_t startOffset = offset; - - const TraceHeader * header = reinterpret_cast(buffer + offset); - offset += sizeof(TraceHeader); - - if (offset > length) { - std::cerr << "Offset " << offset << " is larger than length " << length << std::endl; - return false; - } - - traceData._Header = *header; - uint16_t entrySize = traceData._Header._Length; - EXPECT_TRUE(entrySize <= bufferSize); - - if (!ReadTraceString(buffer, length, offset, traceData._File)) { - std::cerr << "Failed to read file name" << std::endl; - return false; - } - - if (!ReadTraceString(buffer, length, offset, traceData._Module)) { - std::cerr << "Failed to read module name" << std::endl; - return false; - } - - if (!ReadTraceString(buffer, length, offset, traceData._Category)) { - std::cerr << "Failed to read category" << std::endl; - return false; - } - if (!ReadTraceString(buffer, length, offset, traceData._Class)) { - std::cerr << "Failed to read class name" << std::endl; - return false; - } - - uint16_t totalHeaderLength = offset - startOffset; - uint16_t textLength = entrySize - totalHeaderLength; - uint16_t textBufferLength = textLength + 1; - char textBuffer[textBufferLength]; - - memcpy(textBuffer, buffer + offset, textLength); - textBuffer[textLength] = '\0'; - traceData._Text = string(textBuffer); - - offset += textLength; - - EXPECT_TRUE(offset == (startOffset + entrySize)); - - return true; - } - - void DebugCheckIfConsistent(const uint8_t * buffer, int length, CyclicBuffer& cycBuffer, uint32_t bufferSize) - { - uint entryCount = 0; - - int index = 0; - while (index < length) { - uint16_t entrySize = 0; - entrySize += static_cast(buffer[index]); - index++; - entrySize += static_cast(buffer[index]) << 8; - - EXPECT_TRUE(entrySize < bufferSize); - index += entrySize - 1; - - entryCount++; - } - - EXPECT_TRUE(index == length); - } - - void CreateTraceBuffer(string tracePath) - { - char systemCmd[1024]; - string command = "mkdir -p "; - snprintf(systemCmd, command.size()+tracePath.size()+1, "%s%s", command.c_str(),tracePath.c_str()); - system(systemCmd); - } - - TEST(Core_tracing, simpleTracing) - { - // Call dispose to ensure there is no any resource handler registered to - // avoid hang on the poll - ::Thunder::Core::Singleton::Dispose(); - - std::string tracePath = "/tmp/tracebuffer01"; - auto lambdaFunc = [tracePath](IPTestAdministrator & testAdmin) { - std::string db = (tracePath + "/tracebuffer.doorbell"); - string cycBufferName = (tracePath + "/tracebuffer"); - - testAdmin.Sync("client start"); - DoorBell doorBell(db.c_str()); - constexpr uint32_t bufferSize = ((8 * 1024) - (sizeof(struct CyclicBuffer::control))); /* 8Kb */ - ServerCyclicBuffer01 cycBuffer(cycBufferName, bufferSize); - - // Note: this test case is forking a child process with parent process space. - // In such case, signalfd:poll of parent will not get signal from pthread_kill(SIGUSR2) from child - // process. Hence please ensure parent process cleared all resource registration - // using Singleton::Dispose() call from parent context. - // https://lore.kernel.org/linux-man/20190923222413.5c79b179@kappa.digital-domain.net/T/ - - // TODO: maximum running time? - if (doorBell.Wait(infinite) == ERROR_NONE) { - doorBell.Acknowledge(); - uint32_t bufferLength = bufferSize; - uint8_t buffer[bufferLength]; - uint32_t actuallyRead = cycBuffer.Read(buffer, sizeof(buffer)); - testAdmin.Sync("server done"); - - EXPECT_TRUE(actuallyRead < cycBuffer.Size()); - - DebugCheckIfConsistent(buffer, actuallyRead, cycBuffer, bufferSize); - - uint32_t offset = 0; - int traceCount = 0; - while (offset < actuallyRead) { - TraceData traceData; - EXPECT_TRUE(ParseTraceData(buffer, actuallyRead, offset, traceData, bufferSize)); - string time(Time::Now().ToRFC1123(true)); - - EXPECT_STREQ(traceData._File.c_str(), "test_tracing.cpp"); - EXPECT_STREQ(traceData._Class.c_str(), "TestBody"); - EXPECT_STREQ(traceData._Category.c_str(), "Information"); - EXPECT_STREQ(traceData._Text.c_str(), "Trace Log"); - - traceCount++; - } - } - doorBell.Relinquish(); - }; - - static std::function lambdaVar = lambdaFunc; - - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; - - // This side (tested) acts as client. - IPTestAdministrator testAdmin(otherSide); - { - CreateTraceBuffer(tracePath); - Trace::TraceUnit::Instance().Open(tracePath); - testAdmin.Sync("client start"); - sleep(2); - Trace::TraceType::Enable(true); - - TRACE_GLOBAL(Trace::Information, (_T("Trace Log"))); - testAdmin.Sync("server done"); - - Trace::TraceUnit::Instance().SetCategories(true,"Tracing", reinterpret_cast("Information")); - Trace::TraceUnit::Iterator index = Trace::TraceUnit::Instance().GetCategories(); - - while (index.Next() == true) - if ((*index)->Enabled() == true) { - if ((strcmp((*index)->Module(), "Tracing")) == 0 ) { - EXPECT_STREQ((*index)->Category(), "Information"); - } - } - - bool enabled = false; - Trace::TraceUnit::Instance().IsDefaultCategory("Tracing", reinterpret_cast("Information"), enabled); - TRACE(Trace::Information,(Trace::Format(_T("Checking the Format() with 1 parameter")))); - std::string text = "Hello"; - TRACE(Trace::Information,(Trace::Format(text.c_str(), _T("Checking the Format() with 2 parameter")))); - Trace::TraceUnit::Instance().Close(); - Trace::TraceUnit::Instance().Open(1); - } - - testAdmin. WaitForChildCompletion(); - Singleton::Dispose(); - } - - TEST(Core_tracing, simpleTracingReversed) - { - std::string tracePath = "/tmp/tracebuffer02"; - auto lambdaFunc = [&](IPTestAdministrator & testAdmin) { - CreateTraceBuffer(tracePath); - Trace::TraceUnit::Instance().Open(tracePath); - testAdmin.Sync("client start"); - sleep(2); - Trace::TraceType::Enable(true); - - TRACE_GLOBAL(Trace::Information, (_T("Trace Log"))); - testAdmin.Sync("server done"); - - Trace::TraceUnit::Instance().SetCategories(true,"Tracing", reinterpret_cast("Information")); - Trace::TraceUnit::Iterator index = Trace::TraceUnit::Instance().GetCategories(); - - while (index.Next() == true) - if ((*index)->Enabled() == true) { - if ((strcmp((*index)->Module(), "Tracing")) == 0 ) { - EXPECT_STREQ((*index)->Category(), "Information"); - } - } - - bool enabled = false; - Trace::TraceUnit::Instance().IsDefaultCategory("Tracing", reinterpret_cast("Information"), enabled); - TRACE(Trace::Information,(Trace::Format(_T("Checking the Format() with 1 parameter")))); - std::string text = "Hello"; - TRACE(Trace::Information,(Trace::Format(text.c_str(), _T("Checking the Format() with 2 parameter")))); - Trace::TraceUnit::Instance().Close(); - Trace::TraceUnit::Instance().Open(1); - - Singleton::Dispose(); - }; - - static std::function lambdaVar = lambdaFunc; - - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; - - // This side (tested) acts as client. - IPTestAdministrator testAdmin(otherSide); - { - std::string db = (tracePath + "/tracebuffer.doorbell"); - string cycBufferName = (tracePath + "/tracebuffer"); - - testAdmin.Sync("server start"); - DoorBell doorBell(db.c_str()); - constexpr uint32_t bufferSize = ((8 * 1024) - (sizeof(struct CyclicBuffer::control))); /* 8Kb */ - ServerCyclicBuffer01 cycBuffer(cycBufferName, bufferSize); - - // TODO: maximum running time? - if (doorBell.Wait(infinite) == ERROR_NONE) { - doorBell.Acknowledge(); - uint32_t bufferLength = bufferSize; - uint8_t buffer[bufferLength]; - uint32_t actuallyRead = cycBuffer.Read(buffer, sizeof(buffer)); - testAdmin.Sync("client done"); - - EXPECT_TRUE(actuallyRead < cycBuffer.Size()); - - DebugCheckIfConsistent(buffer, actuallyRead, cycBuffer, bufferSize); - - uint32_t offset = 0; - int traceCount = 0; - while (offset < actuallyRead) { - TraceData traceData; - EXPECT_TRUE(ParseTraceData(buffer, actuallyRead, offset, traceData, bufferSize)); - string time(Time::Now().ToRFC1123(true)); - - EXPECT_STREQ(traceData._File.c_str(), "test_tracing.cpp"); - EXPECT_STREQ(traceData._Class.c_str(), "operator()"); - EXPECT_STREQ(traceData._Category.c_str(), "Information"); - EXPECT_STREQ(traceData._Text.c_str(), "Trace Log"); - - traceCount++; - } - } - doorBell.Relinquish(); - } - - testAdmin.WaitForChildCompletion(); - Singleton::Dispose(); - } - -} // Core -} // Tests -} // Thunder diff --git a/Tests/unit/core/test_valuerecorder.cpp b/Tests/unit/core/test_valuerecorder.cpp index 86f0dad72..db51c7226 100644 --- a/Tests/unit/core/test_valuerecorder.cpp +++ b/Tests/unit/core/test_valuerecorder.cpp @@ -29,9 +29,6 @@ namespace Thunder { namespace Tests { namespace Core { - - - const unsigned int BLOCKSIZE = 20; class WriterClass : public RecorderType::Writer diff --git a/Tests/unit/core/test_weblinkjson.cpp b/Tests/unit/core/test_weblinkjson.cpp index 8a52afec3..2e4c1bf86 100644 --- a/Tests/unit/core/test_weblinkjson.cpp +++ b/Tests/unit/core/test_weblinkjson.cpp @@ -106,6 +106,8 @@ namespace Core { private: typedef Web::WebLinkType<::Thunder::Core::SocketStream, Web::Request, Web::Response, ::Thunder::Core::ProxyPoolType&> BaseClass; + constexpr static uint32_t maxWaitTimeMs = 4000; + public: JSONWebServer() = delete; JSONWebServer(const JSONWebServer& copy) = delete; @@ -120,7 +122,7 @@ namespace Core { virtual ~JSONWebServer() { - Close(::Thunder::Core::infinite); + EXPECT_EQ(Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); } public: @@ -142,7 +144,8 @@ namespace Core { ::Thunder::Core::ProxyType response(::Thunder::Core::ProxyType::Create()); response->ErrorCode = 200; response->Body(request->Body()); - Submit(response); + + EXPECT_TRUE(Submit(response)); } virtual void Send(const ::Thunder::Core::ProxyType& response) @@ -164,6 +167,8 @@ namespace Core { private: typedef Web::WebLinkType<::Thunder::Core::SocketStream, Web::Response, Web::Request, ::Thunder::Core::ProxyPoolType&> BaseClass; + static constexpr uint32_t maxWaitTimeMs = 4000; + public: JSONWebClient() = delete; JSONWebClient(const JSONWebClient& copy) = delete; @@ -179,7 +184,7 @@ namespace Core { virtual ~JSONWebClient() { - Close(::Thunder::Core::infinite); + EXPECT_EQ(Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); } public: @@ -199,8 +204,9 @@ namespace Core { EXPECT_TRUE(response->HasBody()); EXPECT_EQ(response->ContentLength.Value(), 60u); - response->Body()->ToString(_dataReceived); - _dataPending.Unlock(); + EXPECT_TRUE(response->Body()->ToString(_dataReceived)); + + EXPECT_EQ(_dataPending.Unlock(), ::Thunder::Core::ERROR_NONE); } virtual void Send(const ::Thunder::Core::ProxyType& request) @@ -213,7 +219,7 @@ namespace Core { { } - int Wait() const + uint32_t Wait() const { return _dataPending.Lock(); } @@ -233,42 +239,72 @@ namespace Core { TEST(WebLink, Json) { - std::string connector {"0.0.0.0"}; - auto lambdaFunc = [connector](IPTestAdministrator & testAdmin) { + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; + + const std::string connector {"0.0.0.0"}; + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { ::Thunder::Core::SocketServerType webServer(::Thunder::Core::NodeId(connector.c_str(), 12341)); - webServer.Open(::Thunder::Core::infinite); - testAdmin.Sync("setup server"); - testAdmin.Sync("client done"); + + ASSERT_EQ(webServer.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(webServer.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); }; - static std::function lambdaVar = lambdaFunc; + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + SleepMs(maxInitTime); - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - IPTestAdministrator testAdmin(otherSide); - testAdmin.Sync("setup server"); - { JSONWebClient jsonWebConnector(::Thunder::Core::NodeId(connector.c_str(), 12341)); + ::Thunder::Core::ProxyType jsonRequest(::Thunder::Core::ProxyType::Create()); ::Thunder::Core::ProxyType jsonRequestBody(::Thunder::Core::ProxyType::Create()); + + // ProxyType + ASSERT_TRUE(jsonRequest.IsValid()); + // ProxyType + ASSERT_TRUE(jsonRequestBody.IsValid()); + jsonRequest->Body(jsonRequestBody); - jsonWebConnector.Open(::Thunder::Core::infinite); - while (!jsonWebConnector.IsOpen()); + + ASSERT_EQ(jsonWebConnector.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + ASSERT_TRUE(jsonWebConnector.IsOpen()); + jsonRequest->Verb = Web::Request::HTTP_GET; jsonRequestBody->Identifier = 123; jsonRequestBody->Name = _T("TestCase"); jsonRequestBody->Params.Speed = 4321; + + // True object + ASSERT_TRUE(jsonRequest->IsValid()); + string sent; - jsonRequestBody->ToString(sent); - jsonWebConnector.Submit(jsonRequest); + EXPECT_TRUE(jsonRequestBody->ToString(sent)); + + EXPECT_TRUE(jsonWebConnector.Submit(jsonRequest)); + + EXPECT_EQ(jsonWebConnector.Wait(), ::Thunder::Core::ERROR_NONE); - jsonWebConnector.Wait(); string received; + jsonWebConnector.Retrieve(received); + EXPECT_STREQ(received.c_str(), sent.c_str()); - testAdmin.Sync("client done"); - } - ::Thunder::Core::Singleton::Dispose(); + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + + ::Thunder::Core::Singleton::Dispose(); } } // Core diff --git a/Tests/unit/core/test_weblinktext.cpp b/Tests/unit/core/test_weblinktext.cpp index 0d8f2d3f3..090d45f9f 100644 --- a/Tests/unit/core/test_weblinktext.cpp +++ b/Tests/unit/core/test_weblinktext.cpp @@ -23,7 +23,6 @@ #include "../Module.h" #endif -#include #include #include "../IPTestAdministrator.h" @@ -36,6 +35,8 @@ namespace Core { private: typedef Web::WebLinkType<::Thunder::Core::SocketStream, Web::Request, Web::Response, ::Thunder::Core::ProxyPoolType > BaseClass; + constexpr static uint32_t maxWaitTimeMs = 4000; + public: WebServer() = delete; WebServer(const WebServer& copy) = delete; @@ -48,7 +49,7 @@ namespace Core { virtual ~WebServer() { - Close(::Thunder::Core::infinite); + EXPECT_EQ(Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); } public: @@ -70,7 +71,8 @@ namespace Core { ::Thunder::Core::ProxyType response(::Thunder::Core::ProxyType::Create()); response->ErrorCode = 200; response->Body(request->Body()); - Submit(response); + + EXPECT_TRUE(Submit(response)); } virtual void Send(const ::Thunder::Core::ProxyType<::Thunder::Web::Response>& response) @@ -93,6 +95,8 @@ namespace Core { private: typedef Web::WebLinkType<::Thunder::Core::SocketStream, Web::Response, Web::Request, ::Thunder::Core::ProxyPoolType&> BaseClass; + static constexpr uint32_t maxWaitTimeMs = 4000; + public: WebClient() = delete; WebClient(const WebClient& copy) = delete; @@ -106,7 +110,7 @@ namespace Core { virtual ~WebClient() { - Close(::Thunder::Core::infinite); + EXPECT_EQ(Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); } public: @@ -127,7 +131,8 @@ namespace Core { EXPECT_EQ(response->ContentLength.Value(), 19u); _dataReceived = *(response->Body()); - _dataPending.Unlock(); + + EXPECT_EQ(_dataPending.Unlock(), ::Thunder::Core::ERROR_NONE); } virtual void Send(const ::Thunder::Core::ProxyType<::Thunder::Web::Request>& request) @@ -140,7 +145,7 @@ namespace Core { { } - int Wait() const + uint32_t Wait() const { return _dataPending.Lock(); } @@ -163,38 +168,63 @@ namespace Core { TEST(WebLink, Text) { - std::string connector {"127.0.0.1"}; - auto lambdaFunc = [connector](IPTestAdministrator & testAdmin) { + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; + + const std::string connector {"127.0.0.1"}; + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { ::Thunder::Core::SocketServerType _webServer(::Thunder::Core::NodeId(connector.c_str(), 12343)); - _webServer.Open(::Thunder::Core::infinite); - testAdmin.Sync("setup server"); - testAdmin.Sync("client done"); + + ASSERT_EQ(_webServer.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(_webServer.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); }; - static std::function lambdaVar = lambdaFunc; + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + // a small delay so the child can be set up + SleepMs(maxInitTime); - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - IPTestAdministrator testAdmin(otherSide); - testAdmin.Sync("setup server"); - { WebClient webConnector(::Thunder::Core::NodeId(connector.c_str(), 12343)); + ::Thunder::Core::ProxyType webRequest(::Thunder::Core::ProxyType::Create()); ::Thunder::Core::ProxyType webRequestBody(::Thunder::Core::ProxyType::Create()); + webRequest->Body(webRequestBody); - webConnector.Open(::Thunder::Core::infinite); - while (!webConnector.IsOpen()); + + ASSERT_EQ(webConnector.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + ASSERT_TRUE(webConnector.IsOpen()); + webRequest->Verb = Web::Request::HTTP_GET; + + // True object + ASSERT_TRUE(webRequest->IsValid()); + string sent = "Just a body to send"; + *webRequestBody = sent; - webConnector.Submit(webRequest); - webConnector.Wait(); + EXPECT_TRUE(webConnector.Submit(webRequest)); + + ASSERT_EQ(webConnector.Wait(), ::Thunder::Core::ERROR_NONE); + string received; + webConnector.Retrieve(received); + EXPECT_STREQ(received.c_str(), sent.c_str()); - testAdmin.Sync("client done"); - } + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + }; + + // Code after this line is executed by both parent and child + ::Thunder::Core::Singleton::Dispose(); } diff --git a/Tests/unit/core/test_websocketjson.cpp b/Tests/unit/core/test_websocketjson.cpp index 744df0ac5..3b83157ea 100644 --- a/Tests/unit/core/test_websocketjson.cpp +++ b/Tests/unit/core/test_websocketjson.cpp @@ -174,9 +174,12 @@ namespace Core { virtual void Received(::Thunder::Core::ProxyType<::Thunder::Core::JSON::IElement>& jsonObject) { string textElement; - jsonObject->ToString(textElement); + + EXPECT_TRUE(jsonObject->ToString(textElement)); + _dataReceived = textElement; - _dataPending.Unlock(); + + EXPECT_EQ(_dataPending.Unlock(), ::Thunder::Core::ERROR_NONE); } virtual void Send(::Thunder::Core::ProxyType<::Thunder::Core::JSON::IElement>& jsonObject) @@ -197,7 +200,7 @@ namespace Core { return _objectFactory.Element(""); } - int Wait() const + uint32_t Wait() const { return _dataPending.Lock(); } @@ -214,46 +217,70 @@ namespace Core { mutable ::Thunder::Core::Event _dataPending; }; - TEST(WebSocket, DISABLED_Json) + TEST(WebSocket, Json) { - std::string connector {"/tmp/wpewebsocketjson0"}; - auto lambdaFunc = [connector](IPTestAdministrator & testAdmin) { + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; + + const std::string connector {"/tmp/wpewebsocketjson0"}; + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { ::Thunder::Core::SocketServerType> jsonWebSocketServer(::Thunder::Core::NodeId(connector.c_str())); - jsonWebSocketServer.Open(::Thunder::Core::infinite); - testAdmin.Sync("setup server"); + + ASSERT_EQ(jsonWebSocketServer.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); std::unique_lock lk(JsonSocketServer<::Thunder::Core::JSON::IElement>::_mutex); + while (!JsonSocketServer<::Thunder::Core::JSON::IElement>::GetState()) { JsonSocketServer<::Thunder::Core::JSON::IElement>::_cv.wait(lk); } - testAdmin.Sync("server open"); - testAdmin.Sync("client done"); + + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); }; - static std::function lambdaVar = lambdaFunc; + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + // a small delay so the child can be set up + SleepMs(maxInitTime); - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - IPTestAdministrator testAdmin(otherSide); - testAdmin.Sync("setup server"); - { ::Thunder::Core::ProxyType sendObject = ::Thunder::Core::ProxyType::Create(); + sendObject->EventType = _T("Test"); sendObject->Event = _T("TestSend"); + std::string sendString; - sendObject->ToString(sendString); + + EXPECT_TRUE(sendObject->ToString(sendString)); JsonSocketClient<::Thunder::Core::JSON::IElement> jsonWebSocketClient(::Thunder::Core::NodeId(connector.c_str())); - jsonWebSocketClient.Open(::Thunder::Core::infinite); - testAdmin.Sync("server open"); + + ASSERT_EQ(jsonWebSocketClient.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + ASSERT_TRUE(jsonWebSocketClient.IsOpen()); + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + jsonWebSocketClient.Submit(::Thunder::Core::ProxyType<::Thunder::Core::JSON::IElement>(sendObject)); - jsonWebSocketClient.Wait(); + + ASSERT_EQ(jsonWebSocketClient.Wait(), ::Thunder::Core::ERROR_NONE); + string received; + jsonWebSocketClient.Retrieve(received); + EXPECT_STREQ(sendString.c_str(), received.c_str()); - jsonWebSocketClient.Close(::Thunder::Core::infinite); - testAdmin.Sync("client done"); - } + + EXPECT_EQ(jsonWebSocketClient.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + }; + + // Code after this line is executed by both parent and child + ::Thunder::Core::Singleton::Dispose(); } diff --git a/Tests/unit/core/test_websockettext.cpp b/Tests/unit/core/test_websockettext.cpp index 27deafab9..583f1b83e 100644 --- a/Tests/unit/core/test_websockettext.cpp +++ b/Tests/unit/core/test_websockettext.cpp @@ -112,7 +112,8 @@ namespace Core { virtual void Received(string& text) { _dataReceived = text; - _dataPending.Unlock(); + + EXPECT_EQ(_dataPending.Unlock(), ::Thunder::Core::ERROR_NONE); } virtual void Send(const string& text) @@ -123,7 +124,7 @@ namespace Core { { } - int Wait() const + uint32_t Wait() const { return _dataPending.Lock(); } @@ -139,41 +140,65 @@ namespace Core { mutable ::Thunder::Core::Event _dataPending; }; - TEST(WebSocket, DISABLED_Text) + TEST(WebSocket, Text) { - std::string connector {"/tmp/wpewebsockettext0"}; - auto lambdaFunc = [connector](IPTestAdministrator & testAdmin) { + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 1; + + const std::string connector {"/tmp/wpewebsockettext0"}; + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { ::Thunder::Core::SocketServerType textWebSocketServer(::Thunder::Core::NodeId(connector.c_str())); - textWebSocketServer.Open(::Thunder::Core::infinite); - testAdmin.Sync("setup server"); + + ASSERT_EQ(textWebSocketServer.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + std::unique_lock lk(TextSocketServer::_mutex); + while (!TextSocketServer::GetState()) { TextSocketServer::_cv.wait(lk); } - testAdmin.Sync("server open"); - testAdmin.Sync("client done"); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(textWebSocketServer.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); }; - static std::function lambdaVar = lambdaFunc; + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + // a small delay so the child can be set up + SleepMs(maxInitTime); - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator& testAdmin ) { lambdaVar(testAdmin); }; + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); - IPTestAdministrator testAdmin(otherSide); - testAdmin.Sync("setup server"); - { TextSocketClient textWebSocketClient(::Thunder::Core::NodeId(connector.c_str())); - textWebSocketClient.Open(::Thunder::Core::infinite); - testAdmin.Sync("server open"); + + ASSERT_EQ(textWebSocketClient.Open(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + ASSERT_TRUE(textWebSocketClient.IsOpen()); + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + string sentString = "Test String"; + textWebSocketClient.Submit(sentString); - textWebSocketClient.Wait(); + + EXPECT_EQ(textWebSocketClient.Wait(), ::Thunder::Core::ERROR_NONE); + string received; + textWebSocketClient.Retrieve(received); + EXPECT_STREQ(sentString.c_str(), received.c_str()); - textWebSocketClient.Close(::Thunder::Core::infinite); - testAdmin.Sync("client done"); - } + + EXPECT_EQ(textWebSocketClient.Close(maxWaitTimeMs), ::Thunder::Core::ERROR_NONE); + + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + }; + + // Code after this line is executed by both parent and child + ::Thunder::Core::Singleton::Dispose(); } diff --git a/Tests/unit/tests/CMakeLists.txt b/Tests/unit/tests/CMakeLists.txt index 766c13346..aae8d8fb9 100644 --- a/Tests/unit/tests/CMakeLists.txt +++ b/Tests/unit/tests/CMakeLists.txt @@ -15,6 +15,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +if(LINUX) set(TEST_RUNNER_NAME "Thunder_test_tests") add_executable(${TEST_RUNNER_NAME} @@ -36,3 +37,5 @@ install( DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${NAMESPACE}_Test) add_test(${TEST_RUNNER_NAME} ${TEST_RUNNER_NAME}) + +endif() diff --git a/Tests/unit/tests/test_iptestmanager.cpp b/Tests/unit/tests/test_iptestmanager.cpp index 894448c5b..f51cc5929 100644 --- a/Tests/unit/tests/test_iptestmanager.cpp +++ b/Tests/unit/tests/test_iptestmanager.cpp @@ -31,21 +31,145 @@ namespace Thunder { namespace Tests { namespace Core { - TEST(Test_IPTestAdministrator, sync) + TEST(Test_IPTestAdministrator, BasicSync) { - IPTestAdministrator::OtherSideMain otherSide = [](IPTestAdministrator & testAdmin) { - testAdmin.Sync("str01"); + // Do not change the signal handshake value unless you use a timed loop and repeatedly check. + // Wait may return early with an error if it expect a different value. - testAdmin.Sync("str02_a"); - }; + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 0; - IPTestAdministrator testAdmin(otherSide); + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + }; - bool result = testAdmin.Sync("str01"); - ASSERT_TRUE(result); + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + // A small delay so the child can be set up + SleepMs(maxInitTime); - result = testAdmin.Sync("str02_b"); - ASSERT_FALSE(result); + // The child is awaiting the signal for this value to produce no error and continue. If the values mismatch the child continues with an error. + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + + ::Thunder::Core::Singleton::Dispose(); + } + + TEST(Test_IPTestAdministrator, SignalBeforeWait) + { + // Do not change the signal handshake value unless you use a timed loop and repeatedly check. + // Wait may return early with an error if it expect a different value. + + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 15; // Approximately 150% maxWaitTimePeriod + + constexpr auto seconds2milliseconds = std::chrono::duration_cast(std::chrono::seconds(1)).count(); + + static_assert((maxWaitTime * seconds2milliseconds * maxRetries) > (maxWaitTimeMs + maxInitTime), "Scheduling is unpredicatable. Keep a safe margin."); + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { + // A small delay so the signal comes first + SleepMs(maxInitTime); + + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + // The child is awaiting the signal for this value to produce no error and continue. If the values mismatch the child continues with an error. + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + + ::Thunder::Core::Singleton::Dispose(); + } + + TEST(Test_IPTestAdministrator, SuccessiveSignalWait) + { + // Do not change the signal handshake value unless you use a timed loop and repeatedly check. + // Wait may return early with an error if it expect a different value. + + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 2; + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + // A small delay so the child can be set up + SleepMs(maxInitTime); + + // The child is awaiting the signal for this value to produce no error and continue. If the values mismatch the child continues with an error. + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + + ::Thunder::Core::Singleton::Dispose(); + } + + TEST(Test_IPTestAdministrator, SuccessiveSignalWaitInterleavedDelay) + { + // Do not change the signal handshake value unless you use a timed loop and repeatedly check. + // Wait may return early with an error if it expect a different value. + + constexpr uint32_t initHandshakeValue = 0, maxWaitTime = 4, maxWaitTimeMs = 4000, maxInitTime = 2000; + constexpr uint8_t maxRetries = 15; // Approximately 150% maxWaitTimePeriod + + IPTestAdministrator::Callback callback_child = [&](IPTestAdministrator& testAdmin) { + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + + SleepMs(maxInitTime); + + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator::Callback callback_parent = [&](IPTestAdministrator& testAdmin) { + // A small delay so the child can be set up + SleepMs(maxInitTime); + // The child is awaiting the signal for this value to produce no error and continue. If the values mismatch the child continues with an error. + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + + SleepMs(maxInitTime); + + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Signal(initHandshakeValue, maxRetries), ::Thunder::Core::ERROR_NONE); + ASSERT_EQ(testAdmin.Wait(initHandshakeValue), ::Thunder::Core::ERROR_NONE); + }; + + IPTestAdministrator testAdmin(callback_parent, callback_child, initHandshakeValue, maxWaitTime); + + // Code after this line is executed by both parent and child + + ::Thunder::Core::Singleton::Dispose(); } } // Core