20 Commits

Author SHA1 Message Date
edb7189013 disable gmock build and remove obsolete tests 2026-03-21 13:31:29 +03:00
fe3b30bd49 version 5.6.1
patch deploy_tool: procDpkg now prioritize "non-cross" and "non-dev" packages, then only "non-cross"
add PIP_MANUAL_TEST CMake option
2026-03-20 13:00:24 +03:00
a1be5be5a1 increase timeout and remove sleep 2026-03-18 15:01:17 +03:00
99c99c39c2 separate cmake opts for tests 2026-03-18 13:39:01 +03:00
ba63e72bfa Merge pull request 'disable exeptions in cmake' (#199) from disable_exeptions into master
Reviewed-on: #199
2026-03-18 11:48:54 +03:00
79fa549201 fix matrix test 2026-03-18 11:46:08 +03:00
35140ee002 Merge pull request 'some fixes' (#198) from some_fixes into master
Reviewed-on: #198
2026-03-18 11:08:48 +03:00
f0c8bfef0a Merge pull request 'simplify piprotectedvariable' (#197) from protected_var_refact into master
Reviewed-on: #197
2026-03-18 10:58:58 +03:00
ba57aa0144 disable exceptions on win 2026-03-18 09:40:32 +03:00
ccbf86f781 disable exeptions in cmake 2026-03-18 09:30:28 +03:00
ac415ebbb6 revert can_unlock flag 2026-03-18 08:54:06 +03:00
c02b627d47 some fixes
PIEthernet::listen
PIClientServer::Server::~Server
PISystemTime const sleep
ClientServer tests fast and stable
2026-03-17 20:14:22 +03:00
9dc1af921c more simplify Pointer 2026-03-17 19:18:44 +03:00
e761625eab using recursive mutex 2026-03-17 19:14:14 +03:00
449978bda0 revert main and more tests 2026-03-17 19:07:01 +03:00
fe01c353e6 simplify tests 2026-03-17 18:56:20 +03:00
9f57f0107e remove picout 2026-03-17 18:34:48 +03:00
ac877f1024 fixes 2026-03-17 18:13:23 +03:00
8ccc05ee78 simplify piprotectedvariable 2026-03-17 17:33:27 +03:00
2798d7de9c fix gitignore 2026-03-17 09:33:10 +03:00
22 changed files with 229 additions and 788 deletions

2
.gitignore vendored
View File

@@ -1,5 +1,5 @@
/src_main/piversion.h /src_main/piversion.h
/.svn /.*
/doc/rtf /doc/rtf
_unsused _unsused
CMakeLists.txt.user* CMakeLists.txt.user*

View File

@@ -97,8 +97,10 @@ public:
{ {
int code = lua_pcall (L, nargs, nresults, msgh); int code = lua_pcall (L, nargs, nresults, msgh);
if (code != LUABRIDGE_LUA_OK) if (code != LUABRIDGE_LUA_OK) {
Throw (LuaException (L, code)); // Throw (LuaException (L, code));
assert(true);
}
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@@ -128,7 +130,8 @@ protected:
private: private:
static int throwAtPanic (lua_State* L) static int throwAtPanic (lua_State* L)
{ {
throw LuaException (L, -1); // throw LuaException (L, -1);
return -1;
} }
}; };

View File

@@ -101,7 +101,8 @@ protected:
{ {
if (m_stackSize == 0) if (m_stackSize == 0)
{ {
throw std::logic_error ("Unable to continue registration"); std::cerr << ("Unable to continue registration");
assert(true);
} }
} }
}; };
@@ -1054,7 +1055,8 @@ public:
{ {
if (m_stackSize == 1) if (m_stackSize == 1)
{ {
throw std::logic_error ("endNamespace () called on global namespace"); std::cerr << ("endNamespace () called on global namespace");
assert(true);
} }
assert (m_stackSize > 1); assert (m_stackSize > 1);
@@ -1150,7 +1152,8 @@ public:
{ {
if (m_stackSize == 1) if (m_stackSize == 1)
{ {
throw std::logic_error ("addProperty () called on global namespace"); std::cerr << ("addProperty () called on global namespace");
assert(true);
} }
assert (lua_istable (L, -1)); // Stack: namespace table (ns) assert (lua_istable (L, -1)); // Stack: namespace table (ns)

View File

@@ -33,6 +33,7 @@
#include <cassert> #include <cassert>
#include <stdexcept> #include <stdexcept>
#include <iostream>
namespace luabridge { namespace luabridge {
@@ -320,7 +321,8 @@ public:
lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ()); lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ());
if (!lua_istable (L, -1)) if (!lua_istable (L, -1))
{ {
throw std::logic_error ("The class is not registered in LuaBridge"); std::cerr << ("The class is not registered in LuaBridge");
assert(true);
} }
lua_setmetatable (L, -2); lua_setmetatable (L, -2);
return ud; return ud;
@@ -375,7 +377,8 @@ private:
lua_rawgetp (L, LUA_REGISTRYINDEX, key); lua_rawgetp (L, LUA_REGISTRYINDEX, key);
if (!lua_istable (L, -1)) if (!lua_istable (L, -1))
{ {
throw std::logic_error ("The class is not registered in LuaBridge"); std::cerr << ("The class is not registered in LuaBridge");
assert(true);
} }
lua_setmetatable (L, -2); lua_setmetatable (L, -2);
} }

View File

@@ -6,7 +6,7 @@ endif()
project(PIP) project(PIP)
set(PIP_MAJOR 5) set(PIP_MAJOR 5)
set(PIP_MINOR 6) set(PIP_MINOR 6)
set(PIP_REVISION 0) set(PIP_REVISION 1)
set(PIP_SUFFIX ) set(PIP_SUFFIX )
set(PIP_COMPANY SHS) set(PIP_COMPANY SHS)
set(PIP_DOMAIN org.SHS) set(PIP_DOMAIN org.SHS)
@@ -67,11 +67,13 @@ set(PIP_DLL_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE STRING "")
option(ICU "ICU support for convert codepages" ${_ICU_DEFAULT}) option(ICU "ICU support for convert codepages" ${_ICU_DEFAULT})
option(STD_IOSTREAM "Building with std iostream operators support" OFF) option(STD_IOSTREAM "Building with std iostream operators support" OFF)
option(INTROSPECTION "Build with introspection" OFF) option(INTROSPECTION "Build with introspection" OFF)
option(TESTS "Build tests and perform their before install step" OFF) option(TESTS "Build tests" OFF)
option(TESTS_RUN "Run tests before install step" OFF)
option(COVERAGE "Build project with coverage info" OFF) option(COVERAGE "Build project with coverage info" OFF)
option(PIP_FFTW_F "Support fftw module for float" ON) option(PIP_FFTW_F "Support fftw module for float" ON)
option(PIP_FFTW_L "Support fftw module for long double" ON) option(PIP_FFTW_L "Support fftw module for long double" ON)
option(PIP_FFTW_Q "Support fftw module for quad double" OFF) option(PIP_FFTW_Q "Support fftw module for quad double" OFF)
option(PIP_MANUAL_TEST "Build dev test (main.cpp)" OFF)
set(PIP_UTILS 1) set(PIP_UTILS 1)
set(CMAKE_CXX_STANDARD_REQUIRED TRUE) set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
@@ -360,10 +362,10 @@ endif()
if(WIN32) if(WIN32)
add_definitions(-DPSAPI_VERSION=1) add_definitions(-DPSAPI_VERSION=1)
if(${C_COMPILER} STREQUAL "cl.exe") if(${C_COMPILER} STREQUAL "cl.exe")
set(CMAKE_CXX_FLAGS "/O2 /Ob2 /Ot /W0") set(CMAKE_CXX_FLAGS "/O2 /Ob2 /Ot /W0 /EH-")
endif() endif()
else() else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -fno-exceptions")
if(DEFINED ENV{QNX_HOST} OR PIP_FREERTOS) if(DEFINED ENV{QNX_HOST} OR PIP_FREERTOS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-32") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-32")
endif() endif()
@@ -592,12 +594,14 @@ if (NOT CROSSTOOLS)
#target_link_libraries(pip_plugin pip) #target_link_libraries(pip_plugin pip)
if (NOT DEFINED ANDROID_PLATFORM) if (NOT DEFINED ANDROID_PLATFORM)
if(microhttpd_FOUND AND curl_FOUND) if (PIP_MANUAL_TEST)
add_executable(pip_test "main.cpp") if(microhttpd_FOUND AND curl_FOUND)
target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server pip_http_client) add_executable(pip_test "main.cpp")
if(sodium_FOUND) target_link_libraries(pip_test pip pip_io_utils pip_client_server pip_http_server pip_http_client)
add_executable(pip_cloud_test "main_picloud_test.cpp") if(sodium_FOUND)
target_link_libraries(pip_cloud_test pip_cloud) add_executable(pip_cloud_test "main_picloud_test.cpp")
target_link_libraries(pip_cloud_test pip_cloud)
endif()
endif() endif()
endif() endif()
endif() endif()
@@ -823,6 +827,9 @@ if (PIP_TESTS_LIST)
foreach(_test ${PIP_TESTS_LIST}) foreach(_test ${PIP_TESTS_LIST})
message(" * ${_test}") message(" * ${_test}")
endforeach() endforeach()
if (TESTS_RUN)
message("TESTS_RUN ON -> Run tests before install step")
endif()
else() else()
message(" Tests: skip (tests off)") message(" Tests: skip (tests off)")
endif() endif()

View File

@@ -72,11 +72,7 @@ PIClientServer::Server::~Server() {
clean_thread->waitForFinish(); clean_thread->waitForFinish();
piDeleteSafety(clean_thread); piDeleteSafety(clean_thread);
stopServer(); stopServer();
for (auto c: clients) { closeAll();
c->aboutDelete();
c->destroy();
delete c;
}
piDeleteSafety(tcp_server); piDeleteSafety(tcp_server);
} }

View File

@@ -726,6 +726,7 @@ inline bool piDeleteSafety(T *& pointer) {
//! \~english In this example "Error!" will be printed on every \b false function return. //! \~english In this example "Error!" will be printed on every \b false function return.
//! \~russian В данном примере будет выведен "Error!" при каждом \b false возврате из функции. //! \~russian В данном примере будет выведен "Error!" при каждом \b false возврате из функции.
class PIP_EXPORT PIScopeExitCall { class PIP_EXPORT PIScopeExitCall {
NO_COPY_CLASS(PIScopeExitCall)
public: public:
//! \~\brief //! \~\brief
//! \~english Constructor that takes a function to execute //! \~english Constructor that takes a function to execute
@@ -758,8 +759,6 @@ public:
} }
private: private:
NO_COPY_CLASS(PIScopeExitCall)
std::function<void()> func; std::function<void()> func;
}; };
@@ -768,14 +767,14 @@ private:
//! \~english Inherit from this class to make your class non-trivially copyable. //! \~english Inherit from this class to make your class non-trivially copyable.
//! \~russian Наследуйтесь от этого класса чтобы сделать свой класс нетривиально копируемым. //! \~russian Наследуйтесь от этого класса чтобы сделать свой класс нетривиально копируемым.
struct PIP_EXPORT PINonTriviallyCopyable { struct PIP_EXPORT PINonTriviallyCopyable {
PINonTriviallyCopyable() noexcept = default; PINonTriviallyCopyable() = default;
PINonTriviallyCopyable(const PINonTriviallyCopyable &) noexcept = default; PINonTriviallyCopyable(const PINonTriviallyCopyable &) = default;
PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept; PINonTriviallyCopyable(PINonTriviallyCopyable &&) ;
PINonTriviallyCopyable & operator=(const PINonTriviallyCopyable &) noexcept = default; PINonTriviallyCopyable & operator=(const PINonTriviallyCopyable &) = default;
PINonTriviallyCopyable & operator=(PINonTriviallyCopyable &&) noexcept = default; PINonTriviallyCopyable & operator=(PINonTriviallyCopyable &&) = default;
~PINonTriviallyCopyable() = default; ~PINonTriviallyCopyable() = default;
}; };
inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) noexcept = default; inline PINonTriviallyCopyable::PINonTriviallyCopyable(PINonTriviallyCopyable &&) = default;
//! \~\brief //! \~\brief

View File

@@ -527,6 +527,7 @@ bool PIEthernet::listen(bool threaded) {
listen_threaded = true; listen_threaded = true;
server_bounded = false; server_bounded = false;
server_thread_.start(server_func); server_thread_.start(server_func);
server_thread_.waitForStart();
return true; return true;
} }
listen_threaded = server_bounded = false; listen_threaded = server_bounded = false;

View File

@@ -4,22 +4,22 @@
//! \~english Thread-safe variable //! \~english Thread-safe variable
//! \~russian Потокобезопасная переменная //! \~russian Потокобезопасная переменная
/* /*
PIP - Platform Independent Primitives PIP - Platform Independent Primitives
Thread-safe variable Thread-safe variable
Ivan Pelipenko peri4ko@yandex.ru Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details. GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef PIPROTECTEDVARIABLE_H #ifndef PIPROTECTEDVARIABLE_H
@@ -34,21 +34,29 @@
template<typename T> template<typename T>
class PIP_EXPORT PIProtectedVariable { class PIP_EXPORT PIProtectedVariable {
public: public:
//! \~english Constructs %PIProtectedVariable and initialize variable by value `v`.
//! \~russian Создает %PIProtectedVariable и инициализирует переменную значением `v`.
PIProtectedVariable(T v = T()): var(std::move(v)) {}
//! \~\brief //! \~\brief
//! \~english Pointer-like wrapper returned by \a getRef() while the protected value remains locked. //! \~english Pointer-like wrapper returned by \a getRef() while the protected value remains locked.
//! \~russian Указателеподобная обертка, возвращаемая \a getRef(), пока защищенное значение остается заблокированным. //! \~russian Указателеподобная обертка, возвращаемая \a getRef(), пока защищенное значение остается заблокированным.
class PIP_EXPORT Pointer { class PIP_EXPORT Pointer {
friend class PIProtectedVariable<T>; friend class PIProtectedVariable<T>;
NO_COPY_CLASS(Pointer);
Pointer & operator=(Pointer && other) = delete;
public: public:
//! \~english Copies wrapper state for access to the same protected value. //! \~english Move constructor - transfers ownership of the lock.
//! \~russian Копирует состояние обертки для доступа к тому же защищенному значению. //! \~russian Конструктор перемещения - передает владение блокировкой.
Pointer(const Pointer & v): pv(v.pv), counter(v.counter + 1) {} Pointer(Pointer && other): pv(other.pv) { other.can_unlock = false; };
//! \~english Destroys wrapper and releases the mutex when it owns the original lock. //! \~english Destroys wrapper and releases the mutex.
//! \~russian Уничтожает обертку и освобождает мьютекс, когда она владеет исходной блокировкой. //! \~russian Уничтожает обертку и освобождает мьютекс.
~Pointer() { ~Pointer() {
if (counter == 0) pv.mutex.unlock(); if (can_unlock) {
pv.mutex.unlock();
}
} }
//! \~english Returns pointer access to the protected value. //! \~english Returns pointer access to the protected value.
@@ -60,11 +68,11 @@ public:
T & operator*() { return pv.var; } T & operator*() { return pv.var; }
private: private:
Pointer() = delete; explicit Pointer() = delete;
Pointer(PIProtectedVariable<T> & v): pv(v) {} explicit Pointer(PIProtectedVariable<T> & v): pv(v) { pv.mutex.lock(); }
PIProtectedVariable<T> & pv; PIProtectedVariable<T> & pv;
int counter = 0; bool can_unlock = true;
}; };
//! \~english Replaces the protected value with \a v. //! \~english Replaces the protected value with \a v.
@@ -76,10 +84,7 @@ public:
//! \~english Locks the value and returns wrapper-based access to it. //! \~english Locks the value and returns wrapper-based access to it.
//! \~russian Блокирует значение и возвращает обертку для доступа к нему. //! \~russian Блокирует значение и возвращает обертку для доступа к нему.
Pointer getRef() { Pointer getRef() { return Pointer(*this); }
mutex.lock();
return Pointer(*this);
}
//! \~english Returns a copy of the protected value. //! \~english Returns a copy of the protected value.
//! \~russian Возвращает копию защищенного значения. //! \~russian Возвращает копию защищенного значения.
@@ -98,7 +103,7 @@ public:
private: private:
mutable PIMutex mutex; mutable PIMutex mutex;
T var; T var = {};
}; };

View File

@@ -105,7 +105,7 @@ extern clock_serv_t __pi_mac_clock;
//! Используйте этот метод для ожидания разниц системных времен или своего времени. //! Используйте этот метод для ожидания разниц системных времен или своего времени.
//! Если метод будет вызван для системного времени \a PISystemTime::current(), то //! Если метод будет вызван для системного времени \a PISystemTime::current(), то
//! ожидание будет почти бесконечным //! ожидание будет почти бесконечным
void PISystemTime::sleep() { void PISystemTime::sleep() const {
piUSleep(piFloord(toMicroseconds())); piUSleep(piFloord(toMicroseconds()));
} }
@@ -118,7 +118,7 @@ void PISystemTime::toTimespec(void * ts) {
} }
PISystemTime::Frequency PISystemTime::toFrequency() { PISystemTime::Frequency PISystemTime::toFrequency() const {
return PISystemTime::Frequency::fromSystemTime(*this); return PISystemTime::Frequency::fromSystemTime(*this);
} }

View File

@@ -253,7 +253,7 @@ public:
//! \~english Sleep for this time //! \~english Sleep for this time
//! \~russian Ожидать это время //! \~russian Ожидать это время
void sleep(); void sleep() const;
//! \~english On *nix system assign current value to timespec struct //! \~english On *nix system assign current value to timespec struct
//! \~russian На *nix системах присваивает время к timespec структуре //! \~russian На *nix системах присваивает время к timespec структуре
@@ -261,7 +261,7 @@ public:
//! \~english Returns \a Frequency that corresponds this time interval //! \~english Returns \a Frequency that corresponds this time interval
//! \~russian Возвращает \a Frequency соответствующую этому временному интервалу //! \~russian Возвращает \a Frequency соответствующую этому временному интервалу
PISystemTime::Frequency toFrequency(); PISystemTime::Frequency toFrequency() const;
//! \~english Returns "yyyy-MM-dd hh:mm:ss.zzz" for absolute time and "<V> <d|h|m|s|ms|us|ns> ..." for relative //! \~english Returns "yyyy-MM-dd hh:mm:ss.zzz" for absolute time and "<V> <d|h|m|s|ms|us|ns> ..." for relative
//! \~russian Возвращает "yyyy-MM-dd hh:mm:ss.zzz" для абсолютного времени и "<V> <d|h|m|s|ms|us|ns> ..." для относительного //! \~russian Возвращает "yyyy-MM-dd hh:mm:ss.zzz" для абсолютного времени и "<V> <d|h|m|s|ms|us|ns> ..." для относительного

View File

@@ -13,6 +13,7 @@ FetchContent_Declare(
) )
# For Windows: Prevent overriding the parent project's compiler/linker settings # For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
set(BUILD_GMOCK OFF CACHE BOOL "Build Google Mock" FORCE)
FetchContent_MakeAvailable(googletest) FetchContent_MakeAvailable(googletest)
enable_testing() enable_testing()
@@ -24,16 +25,18 @@ macro(pip_test NAME)
file(GLOB _HDRS "${NAME}/*.h") file(GLOB _HDRS "${NAME}/*.h")
set(_target pip_${NAME}_test) set(_target pip_${NAME}_test)
add_executable(${_target} ${_CPPS} ${_HDRS}) add_executable(${_target} ${_CPPS} ${_HDRS})
target_link_libraries(${_target} pip ${ARGN} gtest_main) target_link_libraries(${_target} pip ${ARGN} gtest_main)
gtest_discover_tests(${_target}) if (TESTS_RUN)
gtest_discover_tests(${_target})
endif()
list(APPEND PIP_TESTS_LIST "${NAME}") list(APPEND PIP_TESTS_LIST "${NAME}")
set(PIP_TESTS_LIST ${PIP_TESTS_LIST} PARENT_SCOPE) set(PIP_TESTS_LIST ${PIP_TESTS_LIST} PARENT_SCOPE)
endmacro() endmacro()
#pip_test(concurrent)
pip_test(math) pip_test(math)
pip_test(core) pip_test(core)
pip_test(piobject) pip_test(piobject)
pip_test(client_server pip_client_server) pip_test(client_server pip_client_server)
pip_test(io) pip_test(io)
pip_test(system) pip_test(system)
pip_test(thread)

View File

@@ -32,7 +32,8 @@ Client * createAndConnectClient() {
TEST(ClientServer, OneClient) { TEST(ClientServer, OneClient) {
auto const loop_timeout = 1000_ms; auto const loop_timeout = 1000_ms;
auto s = createServer<false, true>(); auto s = createServer<false, true>();
auto c = createAndConnectClient<TestClient<false, false>>(); piMinSleep();
auto c = createAndConnectClient<TestClient<false, false>>();
waitLoop([s]() { return s->clientsCount() > 0; }, loop_timeout); waitLoop([s]() { return s->clientsCount() > 0; }, loop_timeout);
EXPECT_EQ(1, s->clientsCount()); EXPECT_EQ(1, s->clientsCount());
@@ -102,10 +103,11 @@ int getClientsPings(const PIVector<ClientSendThread *> & clients) {
TEST(ClientServer, ManyClients) { TEST(ClientServer, ManyClients) {
auto const loop_timeout = 100_ms; auto const loop_timeout = 1_s;
constexpr int clients_count = 20; constexpr int clients_count = 20;
PIVector<ClientSendThread *> clients; PIVector<ClientSendThread *> clients;
auto s = createServer<false, false, 100_KiB>(); auto s = createServer<false, false, 100_KiB>();
piMinSleep();
piForTimes(clients_count) { piForTimes(clients_count) {
clients.append(new ClientSendThread()); clients.append(new ClientSendThread());
@@ -137,8 +139,9 @@ TEST(ClientServer, ManyClients) {
for (const auto c: clients) { for (const auto c: clients) {
c->startSend(); c->startSend();
} }
(100_ms).sleep(); waitLoop([&clients]() { return getClientsPings(clients) > clients_count * 2; }, loop_timeout);
EXPECT_TRUE(getClientsPings(clients) > clients_count * 2); EXPECT_TRUE(getClientsPings(clients) > clients_count * 2);
waitLoop([s]() { return getServerPongs(s) > clients_count * 2; }, loop_timeout);
EXPECT_TRUE(getServerPongs(s) > clients_count * 2); EXPECT_TRUE(getServerPongs(s) > clients_count * 2);
piDeleteAllAndClear(clients); piDeleteAllAndClear(clients);
waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout); waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout);
@@ -147,7 +150,7 @@ TEST(ClientServer, ManyClients) {
} }
TEST(ClientServer, DynamicClients) { TEST(ClientServer, DynamicClients) {
auto const loop_timeout = 100_ms; auto const loop_timeout = 3_s;
constexpr int clients_count = 20; constexpr int clients_count = 20;
PIVector<ClientSendThread *> clients; PIVector<ClientSendThread *> clients;
PIMutex clients_mutex; PIMutex clients_mutex;
@@ -160,7 +163,6 @@ TEST(ClientServer, DynamicClients) {
clients_mutex.lock(); clients_mutex.lock();
clients << c; clients << c;
clients_mutex.unlock(); clients_mutex.unlock();
piCout << "new client" << clients.size();
}; };
piForTimes(clients_count) { piForTimes(clients_count) {
@@ -178,9 +180,8 @@ TEST(ClientServer, DynamicClients) {
piForTimes(new_cnt) { piForTimes(new_cnt) {
spawnClient(); spawnClient();
} }
piCout << "+++++++";
}, },
12_Hz); 120_Hz);
deleteThread.start( deleteThread.start(
[&clients, &clients_mutex]() { [&clients, &clients_mutex]() {
@@ -194,26 +195,17 @@ TEST(ClientServer, DynamicClients) {
clients_mutex.unlock(); clients_mutex.unlock();
if (c) { if (c) {
delete c; delete c;
piCout << "remove client" << clients.size();
} }
} }
piCout << "----------";
}, },
13_Hz); 130_Hz);
(2_s).sleep();
waitLoop([s]() { return s->clientsCount() >= 10; }, loop_timeout);
EXPECT_GE(s->clientsCount(), 10); EXPECT_GE(s->clientsCount(), 10);
piCout << "now clients" << clients.size();
deleteThread.stopAndWait(); deleteThread.stopAndWait();
spawnThread.stopAndWait(); spawnThread.stopAndWait();
piCout << "total clients" << clients.size();
piDeleteAllAndClear(clients); piDeleteAllAndClear(clients);
waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout); waitLoop([s]() { return s->clientsCount() == 0; }, loop_timeout);
EXPECT_EQ(0, s->clientsCount()); EXPECT_EQ(0, s->clientsCount());

View File

@@ -1,262 +0,0 @@
#include "piblockingqueue.h"
#include "gtest/gtest.h"
class MockConditionVar: public PIConditionVariable {
public:
bool isWaitCalled = false;
bool isWaitForCalled = false;
bool isTrueCondition = false;
int timeout = -1;
void wait(PIMutex & lk) override { isWaitCalled = true; }
void wait(PIMutex & lk, const std::function<bool()> & condition) override {
isWaitCalled = true;
isTrueCondition = condition();
}
bool waitFor(PIMutex & lk, int timeoutMs) override {
isWaitForCalled = true;
timeout = timeoutMs;
return false;
}
bool waitFor(PIMutex & lk, int timeoutMs, const std::function<bool()> & condition) override {
isWaitForCalled = true;
isTrueCondition = condition();
timeout = timeoutMs;
return isTrueCondition;
}
};
TEST(BlockingDequeueUnitTest, put_is_block_when_capacity_reach) {
size_t capacity = 0;
auto conditionVarAdd = new MockConditionVar();
auto conditionVarRem = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVarAdd, conditionVarRem);
dequeue.put(11);
ASSERT_TRUE(conditionVarRem->isWaitCalled);
ASSERT_FALSE(conditionVarRem->isTrueCondition);
}
TEST(BlockingDequeueUnitTest, offer_timedout_is_false_when_capacity_reach) {
size_t capacity = 0;
int timeout = 11;
auto conditionVarAdd = new MockConditionVar();
auto conditionVarRem = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVarAdd, conditionVarRem);
ASSERT_FALSE(dequeue.offer(11, timeout));
}
TEST(BlockingDequeueUnitTest, offer_timedout_is_block_when_capacity_reach) {
size_t capacity = 0;
int timeout = 11;
auto conditionVarAdd = new MockConditionVar();
auto conditionVarRem = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVarAdd, conditionVarRem);
dequeue.offer(11, timeout);
EXPECT_TRUE(conditionVarRem->isWaitForCalled);
EXPECT_EQ(timeout, conditionVarRem->timeout);
ASSERT_FALSE(conditionVarRem->isTrueCondition);
}
TEST(BlockingDequeueUnitTest, offer_is_true_before_capacity_reach) {
size_t capacity = 1;
PIBlockingQueue<int> dequeue(capacity);
ASSERT_TRUE(dequeue.offer(10));
}
TEST(BlockingDequeueUnitTest, offer_is_false_when_capacity_reach) {
size_t capacity = 1;
PIBlockingQueue<int> dequeue(capacity);
dequeue.offer(11);
ASSERT_FALSE(dequeue.offer(10));
}
// TODO change take_is_block_when_empty to prevent segfault
TEST(DISABLED_BlockingDequeueUnitTest, take_is_block_when_empty) {
size_t capacity = 1;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
// May cause segfault because take front of empty queue
dequeue.take();
EXPECT_TRUE(conditionVar->isWaitCalled);
ASSERT_FALSE(conditionVar->isTrueCondition);
}
TEST(BlockingDequeueUnitTest, take_is_not_block_when_not_empty) {
size_t capacity = 1;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.offer(111);
dequeue.take();
EXPECT_TRUE(conditionVar->isWaitCalled);
ASSERT_TRUE(conditionVar->isTrueCondition);
}
TEST(BlockingDequeueUnitTest, take_is_value_eq_to_offer_value) {
size_t capacity = 1;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.offer(111);
ASSERT_EQ(dequeue.take(), 111);
}
TEST(BlockingDequeueUnitTest, take_is_last) {
size_t capacity = 10;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
EXPECT_TRUE(dequeue.offer(111));
EXPECT_TRUE(dequeue.offer(222));
ASSERT_EQ(dequeue.take(), 111);
ASSERT_EQ(dequeue.take(), 222);
}
TEST(BlockingDequeueUnitTest, poll_is_not_block_when_empty) {
size_t capacity = 1;
bool isOk;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.poll(0, 111, &isOk);
EXPECT_FALSE(conditionVar->isWaitForCalled);
}
TEST(BlockingDequeueUnitTest, poll_is_default_value_when_empty) {
size_t capacity = 1;
bool isOk;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
ASSERT_EQ(dequeue.poll(0, 111, &isOk), 111);
}
TEST(BlockingDequeueUnitTest, poll_is_offer_value_when_not_empty) {
size_t capacity = 1;
bool isOk;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.offer(111);
ASSERT_EQ(dequeue.poll(0, -1, &isOk), 111);
}
TEST(BlockingDequeueUnitTest, poll_timeouted_is_block_when_empty) {
size_t capacity = 1;
int timeout = 11;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.poll(timeout, 111);
EXPECT_TRUE(conditionVar->isWaitForCalled);
EXPECT_EQ(timeout, conditionVar->timeout);
ASSERT_FALSE(conditionVar->isTrueCondition);
}
TEST(BlockingDequeueUnitTest, poll_timeouted_is_default_value_when_empty) {
size_t capacity = 1;
int timeout = 11;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
ASSERT_EQ(dequeue.poll(timeout, 111), 111);
}
TEST(BlockingDequeueUnitTest, poll_timeouted_is_not_block_when_not_empty) {
size_t capacity = 1;
int timeout = 11;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.offer(111);
dequeue.poll(timeout, -1);
EXPECT_TRUE(conditionVar->isWaitForCalled);
ASSERT_TRUE(conditionVar->isTrueCondition);
}
TEST(BlockingDequeueUnitTest, poll_timeouted_is_offer_value_when_not_empty) {
size_t capacity = 1;
int timeout = 11;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.offer(111);
ASSERT_EQ(dequeue.poll(timeout, -1), 111);
}
TEST(BlockingDequeueUnitTest, poll_timeouted_is_last) {
size_t capacity = 10;
auto conditionVar = new MockConditionVar();
PIBlockingQueue<int> dequeue(capacity, conditionVar);
dequeue.offer(111);
dequeue.offer(222);
ASSERT_EQ(dequeue.poll(10, -1), 111);
ASSERT_EQ(dequeue.poll(10, -1), 222);
}
TEST(BlockingDequeueUnitTest, capacity_is_eq_constructor_capacity) {
size_t capacity = 10;
PIBlockingQueue<int> dequeue(capacity);
ASSERT_EQ(dequeue.capacity(), capacity);
}
TEST(BlockingDequeueUnitTest, remainingCapacity_is_dif_of_capacity_and_size) {
size_t capacity = 2;
PIBlockingQueue<int> dequeue(capacity);
ASSERT_EQ(dequeue.remainingCapacity(), capacity);
dequeue.offer(111);
ASSERT_EQ(dequeue.remainingCapacity(), capacity - 1);
}
TEST(BlockingDequeueUnitTest, remainingCapacity_is_zero_when_capacity_reach) {
size_t capacity = 1;
PIBlockingQueue<int> dequeue(capacity);
dequeue.offer(111);
dequeue.offer(111);
ASSERT_EQ(dequeue.remainingCapacity(), 0);
}
TEST(BlockingDequeueUnitTest, size_is_eq_to_num_of_elements) {
size_t capacity = 1;
PIBlockingQueue<int> dequeue(capacity);
ASSERT_EQ(dequeue.size(), 0);
dequeue.offer(111);
ASSERT_EQ(dequeue.size(), 1);
}
TEST(BlockingDequeueUnitTest, size_is_eq_to_capacity_when_capacity_reach) {
size_t capacity = 1;
PIBlockingQueue<int> dequeue(capacity);
dequeue.offer(111);
dequeue.offer(111);
ASSERT_EQ(dequeue.size(), capacity);
}
TEST(BlockingDequeueUnitTest, drainTo_is_elements_moved) {
size_t capacity = 10;
PIDeque<int> refDeque;
for (size_t i = 0; i < capacity / 2; ++i)
refDeque.push_back(i * 10);
PIBlockingQueue<int> blockingDequeue(refDeque);
PIDeque<int> deque;
blockingDequeue.drainTo(deque);
ASSERT_EQ(blockingDequeue.size(), 0);
ASSERT_TRUE(deque == refDeque);
}
TEST(BlockingDequeueUnitTest, drainTo_is_ret_eq_to_size_when_all_moved) {
size_t capacity = 10;
PIDeque<int> refDeque;
for (size_t i = 0; i < capacity / 2; ++i)
refDeque.push_back(i * 10);
PIBlockingQueue<int> blockingDequeue(refDeque);
PIDeque<int> deque;
ASSERT_EQ(blockingDequeue.drainTo(deque), refDeque.size());
}
TEST(BlockingDequeueUnitTest, drainTo_is_ret_eq_to_maxCount) {
size_t capacity = 10;
PIDeque<int> refDeque;
for (size_t i = 0; i < capacity / 2; ++i)
refDeque.push_back(i * 10);
PIBlockingQueue<int> blockingDequeue(refDeque);
PIDeque<int> deque;
ASSERT_EQ(blockingDequeue.drainTo(deque, refDeque.size() - 1), refDeque.size() - 1);
}

View File

@@ -1,57 +0,0 @@
#include "piconditionvar.h"
#include "pithread.h"
#include "testutil.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
class ConditionLock
: public ::testing::Test
, public TestUtil {
public:
PIMutex * m = new PIMutex();
bool isProtect;
bool isReleased;
};
TEST_F(ConditionLock, DISABLED_lock_is_protect) {
m->lock();
isProtect = true;
createThread([&]() {
m->lock();
isProtect = false;
});
EXPECT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
ASSERT_TRUE(isProtect);
}
TEST_F(ConditionLock, DISABLED_unlock_is_release) {
m->lock();
isReleased = false;
m->unlock();
createThread([&]() {
m->lock();
isReleased = true;
m->unlock();
});
ASSERT_TRUE(isReleased);
}
TEST_F(ConditionLock, tryLock_is_false_when_locked) {
createThread([&]() {
m->lock();
piMSleep(WAIT_THREAD_TIME_MS);
});
ASSERT_FALSE(m->tryLock());
}
TEST_F(ConditionLock, tryLock_is_true_when_unlocked) {
ASSERT_TRUE(m->tryLock());
}
TEST_F(ConditionLock, tryLock_is_recursive_lock_enable) {
m->lock();
ASSERT_TRUE(m->tryLock());
}

View File

@@ -1,209 +0,0 @@
#include "piconditionvar.h"
#include "pithread.h"
#include "testutil.h"
#include "gtest/gtest.h"
class ConditionVariable
: public ::testing::Test
, public TestUtil {
public:
~ConditionVariable() { delete variable; }
PIMutex m;
PIConditionVariable * variable;
protected:
void SetUp() override {
variable = new PIConditionVariable();
adapterFunctionDefault = [&]() {
m.lock();
variable->wait(m);
m.unlock();
};
}
};
TEST_F(ConditionVariable, wait_is_block) {
createThread();
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
}
TEST_F(ConditionVariable, wait_is_block_when_notifyOne_before_wait) {
variable->notifyOne();
createThread();
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
}
TEST_F(ConditionVariable, wait_is_block_when_notifyAll_before_wait) {
variable->notifyAll();
createThread();
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
}
TEST_F(ConditionVariable, wait_is_unblock_when_notifyOne_after_wait) {
createThread();
variable->notifyOne();
ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
}
TEST_F(ConditionVariable, wait_is_unblock_when_notifyAll_after_wait) {
PIVector<PIThread *> threads;
for (int i = 0; i < THREAD_COUNT; ++i) {
threads.push_back(new PIThread([=]() { adapterFunctionDefault(); }));
}
piForeach(PIThread * thread, threads)
thread->startOnce();
piMSleep(WAIT_THREAD_TIME_MS * THREAD_COUNT);
variable->notifyAll();
PITimeMeasurer measurer;
piForeach(PIThread * thread, threads) {
int timeout = WAIT_THREAD_TIME_MS * THREAD_COUNT - (int)measurer.elapsed_m();
thread->waitForFinish(timeout > 0 ? timeout : 0);
}
for (size_t i = 0; i < threads.size(); ++i)
EXPECT_FALSE(threads[i]->isRunning()) << "Thread " << i << " still running";
piForeach(PIThread * thread, threads)
delete thread;
}
TEST_F(ConditionVariable, wait_is_one_unblock_when_notifyOne) {
PIVector<PIThread *> threads;
for (int i = 0; i < THREAD_COUNT; ++i) {
threads.push_back(new PIThread(adapterFunctionDefault));
}
piForeach(PIThread * thread, threads)
thread->startOnce();
piMSleep(WAIT_THREAD_TIME_MS * THREAD_COUNT);
variable->notifyOne();
piMSleep(WAIT_THREAD_TIME_MS * THREAD_COUNT);
int runningThreadCount = 0;
piForeach(PIThread * thread, threads)
if (thread->isRunning()) runningThreadCount++;
ASSERT_EQ(runningThreadCount, THREAD_COUNT - 1);
}
TEST_F(ConditionVariable, wait_is_protected_unblock_when_notifyOne) {
createThread([&]() {
m.lock();
variable->wait(m);
piMSleep(2 * WAIT_THREAD_TIME_MS);
// Missing unlock
});
variable->notifyOne();
piMSleep(WAIT_THREAD_TIME_MS);
ASSERT_FALSE(m.tryLock());
}
TEST_F(ConditionVariable, wait_condition_is_block) {
createThread([&]() {
m.lock();
variable->wait(m, []() { return false; });
m.unlock();
});
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
}
TEST_F(ConditionVariable, wait_condition_is_check_condition_before_block) {
bool isConditionChecked = false;
createThread([&]() {
m.lock();
variable->wait(m, [&]() {
isConditionChecked = true;
return false;
});
m.unlock();
});
m.lock();
ASSERT_TRUE(isConditionChecked);
m.unlock();
}
TEST_F(ConditionVariable, wait_condition_is_check_condition_when_notifyOne) {
bool isConditionChecked;
createThread([&]() {
m.lock();
variable->wait(m, [&]() {
isConditionChecked = true;
return false;
});
m.unlock();
});
m.lock();
isConditionChecked = false;
m.unlock();
variable->notifyOne();
piMSleep(threadStartTime + 1);
m.lock();
ASSERT_TRUE(isConditionChecked);
m.unlock();
}
TEST_F(ConditionVariable, wait_condition_is_unblock_when_condition_and_notifyOne) {
bool condition = false;
createThread([&]() {
m.lock();
variable->wait(m, [&]() { return condition; });
m.unlock();
});
m.lock();
condition = true;
m.unlock();
variable->notifyOne();
ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
}
TEST_F(ConditionVariable, DISABLED_waitFor_is_block_before_timeout) {
createThread([&]() {
PITimeMeasurer measurer;
m.lock();
variable->waitFor(m, WAIT_THREAD_TIME_MS * 2);
m.unlock();
// Not reliable because spurious wakeup may happen
ASSERT_GE(measurer.elapsed_m(), WAIT_THREAD_TIME_MS);
});
EXPECT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS * 3));
}
TEST_F(ConditionVariable, waitFor_is_unblock_when_timeout) {
std::atomic_bool isUnblock(false);
createThread([&]() {
m.lock();
variable->waitFor(m, WAIT_THREAD_TIME_MS);
isUnblock = true;
m.unlock();
});
// Test failed if suspend forever
EXPECT_TRUE(thread->waitForFinish(2 * WAIT_THREAD_TIME_MS));
ASSERT_TRUE(isUnblock);
}
TEST_F(ConditionVariable, waitFor_is_false_when_timeout) {
bool waitRet = true;
createThread([&]() {
m.lock();
waitRet = variable->waitFor(m, WAIT_THREAD_TIME_MS);
m.unlock();
});
EXPECT_TRUE(thread->waitForFinish(2 * WAIT_THREAD_TIME_MS));
ASSERT_FALSE(waitRet);
}
TEST_F(ConditionVariable, waitFor_is_unblock_when_condition_and_notifyOne) {
bool condition = false;
createThread([&]() {
m.lock();
variable->waitFor(m, 3 * WAIT_THREAD_TIME_MS, [&]() { return condition; });
m.unlock();
});
EXPECT_TRUE(thread->isRunning());
m.lock();
condition = true;
m.unlock();
variable->notifyOne();
piMSleep(WAIT_THREAD_TIME_MS);
ASSERT_FALSE(thread->isRunning());
}

View File

@@ -1,51 +0,0 @@
#include "pimutex.h"
#include "pithreadpoolexecutor.h"
#include "gtest/gtest.h"
const int WAIT_THREAD_TIME_MS = 30;
TEST(ExcutorIntegrationTest, execute_is_runnable_invoke) {
PIMutex m;
int invokedRunnables = 0;
PIThreadPoolExecutor executorService(1);
executorService.execute([&]() {
m.lock();
invokedRunnables++;
m.unlock();
});
piMSleep(WAIT_THREAD_TIME_MS);
ASSERT_EQ(invokedRunnables, 1);
}
TEST(ExcutorIntegrationTest, execute_is_not_execute_after_shutdown) {
bool isRunnableInvoke = false;
PIThreadPoolExecutor executorService(1);
executorService.shutdown();
executorService.execute([&]() { isRunnableInvoke = true; });
piMSleep(WAIT_THREAD_TIME_MS);
ASSERT_FALSE(isRunnableInvoke);
}
TEST(ExcutorIntegrationTest, execute_is_execute_before_shutdown) {
bool isRunnableInvoke = false;
PIThreadPoolExecutor executorService(1);
executorService.execute([&]() {
piMSleep(WAIT_THREAD_TIME_MS);
isRunnableInvoke = true;
});
executorService.shutdown();
piMSleep(2 * WAIT_THREAD_TIME_MS);
ASSERT_TRUE(isRunnableInvoke);
}
TEST(ExcutorIntegrationTest, execute_is_awaitTermination_wait) {
PIThreadPoolExecutor executorService(1);
executorService.execute([&]() { piMSleep(2 * WAIT_THREAD_TIME_MS); });
executorService.shutdown();
PITimeMeasurer measurer;
ASSERT_TRUE(executorService.awaitTermination(3 * WAIT_THREAD_TIME_MS));
double waitTime = measurer.elapsed_m();
ASSERT_GE(waitTime, WAIT_THREAD_TIME_MS);
ASSERT_LE(waitTime, 4 * WAIT_THREAD_TIME_MS);
}

View File

@@ -1,57 +0,0 @@
#include "pithreadnotifier.h"
#include "gtest/gtest.h"
TEST(PIThreadNotifierTest, One) {
PIThreadNotifier n;
int cnt = 0;
PIThread t1(
[&n, &cnt]() {
n.wait();
cnt++;
},
true);
piMSleep(10);
n.notifyOnce();
piMSleep(10);
ASSERT_EQ(cnt, 1);
n.notifyOnce();
piMSleep(10);
ASSERT_EQ(cnt, 2);
}
TEST(PIThreadNotifierTest, Two) {
PIThreadNotifier n;
int cnt1 = 0;
int cnt2 = 0;
int cnt3 = 0;
PIThread t1(
[&n, &cnt1]() {
n.wait();
cnt1++;
piMSleep(2);
},
true);
PIThread t2(
[&n, &cnt2]() {
n.wait();
cnt2++;
piMSleep(2);
},
true);
PIThread t3(
[&n, &cnt3]() {
n.notifyOnce();
cnt3++;
piMSleep(1);
},
true);
piMSleep(20);
t3.stop(true);
piMSleep(100);
t1.stop();
t2.stop();
ASSERT_EQ(cnt1 + cnt2, cnt3);
}

View File

@@ -1,62 +0,0 @@
#ifndef AWRCANFLASHER_TESTUTIL_H
#define AWRCANFLASHER_TESTUTIL_H
#include "pithread.h"
#include <atomic>
/**
* Minimum wait thread start, switch context or another interthread communication action time. Increase it if tests
* write "Start thread timeout reach!" message. You can reduce it if you want increase test performance.
*/
const int WAIT_THREAD_TIME_MS = 400;
const int THREAD_COUNT = 5;
class TestUtil: public PIObject {
PIOBJECT(TestUtil)
public:
double threadStartTime;
PIThread * thread = new PIThread();
std::atomic_bool isRunning;
std::function<void()> adapterFunctionDefault;
TestUtil(): isRunning(false) {}
bool createThread(const std::function<void()> & fun = nullptr, PIThread * thread_ = nullptr) {
std::function<void()> actualFun = fun == nullptr ? adapterFunctionDefault : fun;
if (thread_ == nullptr) thread_ = thread;
thread_->startOnce([=](void *) {
isRunning = true;
actualFun();
});
return waitThread(thread_);
}
bool waitThread(PIThread * thread_, bool runningStatus = true) {
PITimeMeasurer measurer;
bool isTimeout = !thread_->waitForStart(WAIT_THREAD_TIME_MS);
while (!isRunning) {
isTimeout = WAIT_THREAD_TIME_MS <= measurer.elapsed_m();
if (isTimeout) break;
piUSleep(100);
}
threadStartTime = measurer.elapsed_m();
if (isTimeout) piCout << "Start thread timeout reach!";
if (threadStartTime > 1) {
piCout << "Start time" << threadStartTime << "ms";
} else if (threadStartTime > 0.001) {
piCout << "Start time" << threadStartTime * 1000 << "mcs";
} else {
piCout << "Start time" << threadStartTime * 1000 * 1000 << "ns";
}
return !isTimeout;
}
};
#endif // AWRCANFLASHER_TESTUTIL_H

View File

@@ -376,7 +376,7 @@ TEST(PIMathMatrixT_Test, invert) {
matrix3 = matrix1; matrix3 = matrix1;
matrix1.invert(); matrix1.invert();
EXPECT_EQ(matrix1, matrix3); EXPECT_EQ(matrix1, matrix3);
EXPECT_DOUBLE_EQ(std::abs(d1 - (1. / d2)), 0.); EXPECT_NEAR(std::abs(d1 - (1. / d2)), 0., 1e-12);
} }
TEST(PIMathMatrixT_Test, inverted) { TEST(PIMathMatrixT_Test, inverted) {

View File

@@ -0,0 +1,118 @@
#include "piprotectedvariable.h"
#include "pistring.h"
#include "pithread.h"
#include "piliterals_time.h"
#include "gtest/gtest.h"
#include <atomic>
// Basic functionality tests
TEST(PIProtectedVariable_Basic, BasicFunctionality) {
// Test basic set/get with different types
PIProtectedVariable<int> pvInt;
PIProtectedVariable<double> pvDouble;
PIProtectedVariable<PIString> pvString;
pvInt.set(123);
pvDouble.set(3.14159);
pvString.set(PIString("Hello, World!"));
EXPECT_EQ(pvInt.get(), 123);
EXPECT_DOUBLE_EQ(pvDouble.get(), 3.14159);
EXPECT_EQ(pvString.get(), PIString("Hello, World!"));
// Test operator=
pvInt = 999;
EXPECT_EQ(pvInt.get(), 999);
// Test getRef() with Pointer
struct TestStruct {
int x = 10;
int y = 20;
int getValue() const { return x + y; }
};
PIProtectedVariable<TestStruct> pvStruct;
auto ptr = pvStruct.getRef();
EXPECT_EQ(ptr->x, 10);
EXPECT_EQ(ptr->y, 20);
EXPECT_EQ(ptr->getValue(), 30);
// Modify through pointer
*ptr = TestStruct();
ptr->x = 100;
EXPECT_EQ(pvStruct.get().x, 100);
// Test for Pointer
pvInt.set(42);
auto ptr1 = pvInt.getRef();
EXPECT_EQ(*ptr1, 42);
*ptr1 = 55;
EXPECT_EQ(*ptr1, 55);
auto ptr2 = std::move(ptr1);
EXPECT_EQ(*ptr2, 55);
*ptr2 = 100;
EXPECT_EQ(*ptr2, 100);
auto ptr3 = pvInt.getRef();
EXPECT_EQ(*ptr3, 100);
*ptr3 = 333;
EXPECT_EQ(*ptr3, 333);
EXPECT_EQ(pvInt.get(), 333);
}
// Thread safety tests
TEST(PIProtectedVariable_ThreadSafety, ConcurrentReadWrite) {
PIProtectedVariable<int> pv;
std::atomic<int> writeCount(0);
std::atomic<int> readCount(0);
std::atomic<int> invalidReads(0);
const int NUM_ITERATIONS = 1000;
const int NUM_WRITERS = 10;
const int NUM_READERS = 20;
const int TOTAL_WRITES = NUM_WRITERS * NUM_ITERATIONS;
// Collect thread handles for joining
PIVector<PIThread *> threads;
// Create writer threads
for (int i = 0; i < NUM_WRITERS; ++i) {
threads.push_back(new PIThread([&pv, &writeCount]() {
for (int j = 0; j < NUM_ITERATIONS; ++j) {
auto val = pv.getRef();
(*val)++;
auto val2 = pv.getRef();
writeCount++;
ASSERT_EQ(writeCount, *val2);
}
}));
}
// Create reader threads
for (int i = 0; i < NUM_READERS; ++i) {
threads.push_back(new PIThread([&pv, &invalidReads, &readCount]() {
for (int j = 0; j < NUM_ITERATIONS; ++j) {
auto val = pv.get();
readCount++;
// Value should always be in valid range [0, TOTAL_WRITES]
if (val < 0 || val > TOTAL_WRITES) {
invalidReads++;
}
}
}));
}
threads.forEach([](PIThread * & t) {t->startOnce();});
threads.forEach([](PIThread * & t) {t->waitForFinish(2_s);});
piDeleteAll(threads);
// Verify results
EXPECT_EQ(writeCount, TOTAL_WRITES);
EXPECT_EQ(readCount, NUM_READERS * NUM_ITERATIONS);
EXPECT_EQ(invalidReads, 0) << "All reads should return valid values in range [0, " << TOTAL_WRITES << "]";
// Final value should be TOTAL_WRITES
int finalVal = pv.get();
EXPECT_EQ(finalVal, TOTAL_WRITES);
}

View File

@@ -467,11 +467,20 @@ bool procDpkg(const PIString & l) {
if (!vs.isEmpty()) { if (!vs.isEmpty()) {
PIStringList lines = vs.split('\n').reverse(); PIStringList lines = vs.split('\n').reverse();
for (auto l: lines) { for (auto l: lines) {
auto sl = l; l = l.left(l.find(":"));
l = l.left(l.find(":")); if (!l.isEmpty() && !l.contains(' ') && !l.endsWith("-cross") && !l.endsWith("-dev")) {
if (!l.isEmpty() && !l.endsWith("-cross") && !l.contains(' ')) all_deps << l; // PICout(true) << "** found \"" << l << "\" in \"" << sl << "\"";
// PICout(true) << "** found \"" << l << "\" in \"" << sl << "\""; all_deps << l;
return true; return true;
}
}
for (auto l: lines) {
l = l.left(l.find(":"));
if (!l.isEmpty() && !l.contains(' ') && !l.endsWith("-cross")) {
// PICout(true) << "** found \"" << l << "\" in \"" << sl << "\"";
all_deps << l;
return true;
}
} }
} }
// piCout << "No dep on" << l; // piCout << "No dep on" << l;