mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-04-20 03:08:17 +00:00
45
tests/include/basic_test_helper.h
Normal file
45
tests/include/basic_test_helper.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "../../global/exec_dir_helper.h"
|
||||
#include <filesystem>
|
||||
|
||||
inline std::filesystem::path GetRootDirectory()
|
||||
{
|
||||
std::filesystem::path path = GetExecDirectory();
|
||||
std::filesystem::path root = path.root_path();
|
||||
// Check for three existing directories: sdv_services, export and tests. This is the root!
|
||||
do
|
||||
{
|
||||
if (!std::filesystem::exists(path / "sdv_services"))
|
||||
continue;
|
||||
if (!std::filesystem::exists(path / "export"))
|
||||
continue;
|
||||
if (!std::filesystem::exists(path / "tests"))
|
||||
continue;
|
||||
|
||||
// Root directory found
|
||||
break;
|
||||
} while ((path = path.parent_path()), path != root);
|
||||
;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
inline std::filesystem::path GetConfigFilePath(const std::filesystem::path& filename)
|
||||
{
|
||||
return GetExecDirectory() / "config" / filename;
|
||||
}
|
||||
|
||||
inline std::string GetConfigFile(const std::filesystem::path& filename)
|
||||
{
|
||||
std::filesystem::path configPath = GetConfigFilePath(filename);
|
||||
|
||||
std::ifstream inputStream(configPath);
|
||||
std::string configFileContent((std::istreambuf_iterator<char>(inputStream)), std::istreambuf_iterator<char>());
|
||||
|
||||
return configFileContent;
|
||||
}
|
||||
|
||||
inline std::string GetRepositoryServiceModulePath()
|
||||
{
|
||||
std::filesystem::path currentPath = GetExecDirectory() / "../../bin/repository_service.sdv";
|
||||
return currentPath.generic_u8string();
|
||||
}
|
||||
59
tests/include/gtest_custom.h
Normal file
59
tests/include/gtest_custom.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef GTEST_CUSTOM_H
|
||||
#define GTEST_CUSTOM_H
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable: 4102)
|
||||
#endif
|
||||
|
||||
#define GTEST_HAS_EXCEPTIONS 1
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef countof
|
||||
/**
|
||||
* @brief Count the amount of elements in the array.
|
||||
* @tparam T Array base type.
|
||||
* @tparam N The amount of elements.
|
||||
* @return The amount of elements in the array.
|
||||
*/
|
||||
template <typename T, int N>
|
||||
constexpr int countof(T const (&)[N]) noexcept
|
||||
{
|
||||
return N;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Equality test macro for container functions.
|
||||
*/
|
||||
#define EXPECT_ARREQ(val1, ...) { bool b = val1 == decltype(val1)({__VA_ARGS__}); EXPECT_TRUE(b); }
|
||||
|
||||
/**
|
||||
* @brief Unequality test macro for container functions.
|
||||
*/
|
||||
#define EXPECT_ARRNE(val1, ...) { bool b = val1 == decltype(val1)({__VA_ARGS__}); EXPECT_FALSE(b); }
|
||||
|
||||
/**
|
||||
* @brief Check for equality fo floating point values.
|
||||
*/
|
||||
#define EXPECT_FPEQ(val1, val2) EXPECT_TRUE(std::fabs(val1 - val2) < std::numeric_limits<decltype(val1)>::epsilon());
|
||||
|
||||
#include "simple_cpp_decomposer.h"
|
||||
|
||||
/**
|
||||
* @brief Compare CPP code for equality.
|
||||
*/
|
||||
#define EXPECT_CPPEQ(s1, s2) \
|
||||
EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperCPPEQ, s1, s2)
|
||||
|
||||
/**
|
||||
* @brief Compare CPP code for inequality.
|
||||
*/
|
||||
#define EXPECT_CPPNE(s1, s2) \
|
||||
EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperCPPNE, s1, s2)
|
||||
|
||||
|
||||
#endif // ! defined GTEST_CUSTOM_H
|
||||
112
tests/include/logger_test_helper.h
Normal file
112
tests/include/logger_test_helper.h
Normal file
@@ -0,0 +1,112 @@
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include "../../global/exec_dir_helper.h"
|
||||
|
||||
inline int GetLoggerFilesCount(const std::string& prefix)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
std::string path = GetExecDirectory().generic_string();
|
||||
for (const auto& entry : std::filesystem::directory_iterator(path))
|
||||
{
|
||||
std::string filename = entry.path().filename().generic_string().c_str();
|
||||
if (filename.rfind(prefix, 0) == 0)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
inline void DeleteLoggerFile(const std::string& filename)
|
||||
{
|
||||
auto path = GetExecDirectory() / filename;
|
||||
try
|
||||
{
|
||||
std::filesystem::remove(path);
|
||||
} catch (const std::filesystem::filesystem_error&)
|
||||
{}
|
||||
}
|
||||
|
||||
inline uint32_t GetErrorCountFromLogFile(const std::string& filename)
|
||||
{
|
||||
uint32_t errorCount = 0;
|
||||
auto path = GetExecDirectory() / filename;
|
||||
std::ifstream infile(path);
|
||||
if (infile.is_open())
|
||||
{
|
||||
std::string line;
|
||||
while (std::getline(infile, line))
|
||||
{
|
||||
size_t foundStart = line.find("Error;");
|
||||
if (foundStart != std::string::npos)
|
||||
{
|
||||
std::cout << line << std::endl;
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errorCount != 0)
|
||||
{
|
||||
std::cout << "Checking " << filename << ", found: " << std::to_string(errorCount) << std::endl;
|
||||
}
|
||||
|
||||
return errorCount;
|
||||
}
|
||||
|
||||
inline bool FileNameStartsWith(const std::string& fileName, const std::string& startOfLogName)
|
||||
{
|
||||
if (!startOfLogName.empty())
|
||||
{
|
||||
if (fileName.rfind(startOfLogName, 0) != 0) // returns std::string::npos (i.e. not found)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline uint32_t GetStringCountFromLogFiles(const std::string& startOfLogName, const std::string& subString)
|
||||
{
|
||||
uint32_t errorCount = 0;
|
||||
|
||||
auto path = GetExecDirectory();
|
||||
for (const auto& entry : std::filesystem::directory_iterator(path))
|
||||
{
|
||||
if ((entry.path().extension() == ".log") &&
|
||||
(FileNameStartsWith(entry.path().filename().string(), startOfLogName)))
|
||||
{
|
||||
std::ifstream infile(entry.path());
|
||||
if (infile.is_open())
|
||||
{
|
||||
std::string line;
|
||||
while (std::getline(infile, line))
|
||||
{
|
||||
size_t foundStart = line.find(subString);
|
||||
if (foundStart != std::string::npos)
|
||||
{
|
||||
errorCount++;
|
||||
}
|
||||
}
|
||||
infile.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errorCount;
|
||||
}
|
||||
|
||||
inline uint32_t GetErrorCountFromLogFiles(const std::string& logName) { return GetStringCountFromLogFiles(logName, ";Error;"); }
|
||||
inline uint32_t GetWarningCountFromLogFiles(const std::string& logName) { return GetStringCountFromLogFiles(logName, "Warning;"); }
|
||||
inline uint32_t GetTraceCountFromLogFiles(const std::string& logName) { return GetStringCountFromLogFiles(logName, "Trace;"); }
|
||||
|
||||
|
||||
#else
|
||||
inline int GetLoggerFilesCount(const std::string& ) { return 0; }
|
||||
inline void DeleteLoggerFile(const std::string& ) { return; }
|
||||
inline uint32_t GetErrorCountFromLogFile(const std::string&) { return 0; }
|
||||
inline uint32_t GetErrorCountFromLogFiles(const std::string&) { return 0; }
|
||||
inline uint32_t GetWarningCountFromLogFiles(const std::string&) { return 0; }
|
||||
inline uint32_t GetTraceCountFromLogFiles(const std::string&) { return 0; }
|
||||
#endif
|
||||
96
tests/include/simple_cpp_decomposer.h
Normal file
96
tests/include/simple_cpp_decomposer.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#ifndef SIMPLE_CPP_DECOMPOSER_H
|
||||
#define SIMPLE_CPP_DECOMPOSER_H
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief Decomposes C++ code into text chunks. Supports simple grammar.
|
||||
*/
|
||||
class CSimpleCppDecomposer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param[in] rssCppCode Reference to the source code to decompose.
|
||||
*/
|
||||
CSimpleCppDecomposer(const std::string& rssCppCode);
|
||||
|
||||
/**
|
||||
* @brief Get the list of decomposed source code chunks.
|
||||
* @return A reference to the list of source code chunks.
|
||||
*/
|
||||
const std::list<std::string>& GetDecomposedCode() const;
|
||||
|
||||
/**
|
||||
* @brief Compare with another decomposed code for equality.
|
||||
* @param[in] rdecompChunk Reference to the decomposed chunk of code to use for this comparison.
|
||||
* @return Returns 'true' when equal; 'false' when not.
|
||||
*/
|
||||
bool operator==(const CSimpleCppDecomposer& rdecompChunk) const;
|
||||
|
||||
/**
|
||||
* @brief Compare with another decomposed code for un-equality.
|
||||
* @param[in] rdecompChunk Reference to the decomposed chunk of code to use for this comparison.
|
||||
* @return Returns 'true' when not equal; 'false' when equal.
|
||||
*/
|
||||
bool operator!=(const CSimpleCppDecomposer& rdecompChunk) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Iterate through the source code collecting the source code chunks.
|
||||
*/
|
||||
void Process();
|
||||
|
||||
/**
|
||||
* @brief Skip whitespace
|
||||
*/
|
||||
void SkipWhitespace();
|
||||
|
||||
/**
|
||||
* @brief Skip the rest of the line.
|
||||
*/
|
||||
void SkipLine();
|
||||
|
||||
/**
|
||||
*@brief Skip C-comments.
|
||||
*/
|
||||
void SkipComment();
|
||||
|
||||
/**
|
||||
* @brief Process text and numbers
|
||||
*/
|
||||
void ProcessText();
|
||||
|
||||
/**
|
||||
* @brief Process strings
|
||||
*/
|
||||
void ProcessString();
|
||||
|
||||
/**
|
||||
* @brief Process symbols.
|
||||
*/
|
||||
void ProcessSymbol();
|
||||
|
||||
std::string m_ssCode; ///< Current position within the source code.
|
||||
size_t m_nPos = 0; ///< The current position.
|
||||
std::list<std::string> m_lstCode; ///< Decomposed code.
|
||||
bool m_bNewLine = true; ///< Set when a new line has occurred (ignoring any whitespace).
|
||||
};
|
||||
|
||||
namespace testing::internal
|
||||
{
|
||||
// Additional helpers for testing C++ code
|
||||
GTEST_API_ AssertionResult CmpHelperCPPEQ(const char* szLeftExpression, const char* szRightExpression,
|
||||
const char* szLeft, const char* szRight);
|
||||
GTEST_API_ AssertionResult CmpHelperCPPNE(const char* szLeftExpression, const char* szRightExpression,
|
||||
const char* szLeft, const char* szRight);
|
||||
GTEST_API_ AssertionResult CmpHelperCPPEQ(const char* szLeftExpression, const char* szRightExpression,
|
||||
const std::string& rssLeft, const std::string& rssRight);
|
||||
GTEST_API_ AssertionResult CmpHelperCPPNE(const char* szLeftExpression, const char* szRightExpression,
|
||||
const std::string& rssLeft, const std::string& rssRight);
|
||||
}
|
||||
|
||||
#include "simple_cpp_decomposer.inl"
|
||||
|
||||
#endif //! SIMPLE_CPP_DECOMPOSER_H
|
||||
241
tests/include/simple_cpp_decomposer.inl
Normal file
241
tests/include/simple_cpp_decomposer.inl
Normal file
@@ -0,0 +1,241 @@
|
||||
#ifndef SIMPLE_CPP_DECOMPOSER_INL
|
||||
#define SIMPLE_CPP_DECOMPOSER_INL
|
||||
|
||||
#ifndef SIMPLE_CPP_DECOMPOSER_H
|
||||
#error Do not include "simple_cpp_composer.inl" directly. Include "simple_cpp_composer.h" instead!
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace testing::internal
|
||||
{
|
||||
// Additional helpers for testing C++ code
|
||||
inline GTEST_API_ AssertionResult CmpHelperCPPEQ(const char* szLeftExpression, const char* szRightExpression,
|
||||
const char* szLeft, const char* szRight)
|
||||
{
|
||||
if (CSimpleCppDecomposer(szLeft) == CSimpleCppDecomposer(szRight))
|
||||
{
|
||||
return AssertionSuccess();
|
||||
}
|
||||
|
||||
return EqFailure(szLeftExpression, szRightExpression, PrintToString(szLeft), PrintToString(szRight), false);
|
||||
}
|
||||
inline GTEST_API_ AssertionResult CmpHelperCPPNE(const char* szLeftExpression, const char* szRightExpression,
|
||||
const char* szLeft, const char* szRight)
|
||||
{
|
||||
if (CSimpleCppDecomposer(szLeft) != CSimpleCppDecomposer(szRight))
|
||||
{
|
||||
return AssertionSuccess();
|
||||
}
|
||||
|
||||
return EqFailure(szLeftExpression, szRightExpression, PrintToString(szLeft), PrintToString(szRight), false);
|
||||
}
|
||||
inline GTEST_API_ AssertionResult CmpHelperCPPEQ(const char* szLeftExpression, const char* szRightExpression,
|
||||
const std::string& rssLeft, const std::string& rssRight)
|
||||
{
|
||||
if (CSimpleCppDecomposer(rssLeft) == CSimpleCppDecomposer(rssRight))
|
||||
{
|
||||
return AssertionSuccess();
|
||||
}
|
||||
|
||||
return EqFailure(szLeftExpression, szRightExpression, PrintToString(rssLeft), PrintToString(rssRight), false);
|
||||
}
|
||||
inline GTEST_API_ AssertionResult CmpHelperCPPNE(const char* szLeftExpression, const char* szRightExpression,
|
||||
const std::string& rssLeft, const std::string& rssRight)
|
||||
{
|
||||
if (CSimpleCppDecomposer(rssLeft) != CSimpleCppDecomposer(rssRight))
|
||||
{
|
||||
return AssertionSuccess();
|
||||
}
|
||||
|
||||
return EqFailure(szLeftExpression, szRightExpression, PrintToString(rssLeft), PrintToString(rssRight), false);
|
||||
}
|
||||
}
|
||||
|
||||
inline CSimpleCppDecomposer::CSimpleCppDecomposer(const std::string& rssCppCode) : m_ssCode(rssCppCode)
|
||||
{
|
||||
Process();
|
||||
}
|
||||
|
||||
inline const std::list<std::string>& CSimpleCppDecomposer::GetDecomposedCode() const
|
||||
{
|
||||
return m_lstCode;
|
||||
}
|
||||
|
||||
inline bool CSimpleCppDecomposer::operator==(const CSimpleCppDecomposer& rdecompChunk) const
|
||||
{
|
||||
return m_lstCode == rdecompChunk.GetDecomposedCode();
|
||||
}
|
||||
|
||||
inline bool CSimpleCppDecomposer::operator!=(const CSimpleCppDecomposer& rdecompChunk) const
|
||||
{
|
||||
return m_lstCode != rdecompChunk.GetDecomposedCode();
|
||||
}
|
||||
|
||||
inline void CSimpleCppDecomposer::Process()
|
||||
{
|
||||
while (m_nPos < m_ssCode.size())
|
||||
{
|
||||
// First skip whitespace...
|
||||
SkipWhitespace();
|
||||
if (m_nPos >= m_ssCode.size()) break;
|
||||
|
||||
// Check for preproc directives... skip those
|
||||
if (m_ssCode[m_nPos] == '#')
|
||||
{
|
||||
SkipLine();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for comment
|
||||
if (m_ssCode[m_nPos] == '/' && (m_ssCode[m_nPos + 1] == '/' || m_ssCode[m_nPos + 1] == '*'))
|
||||
{
|
||||
SkipComment();
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the current character represents an underscore a string or a number, it is a keyword, variable, prefix or a number.
|
||||
if (std::isalnum(m_ssCode[m_nPos]) || m_ssCode[m_nPos] == '_')
|
||||
{
|
||||
ProcessText();
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the current character represents a quote (single or double), it is a string or a character.
|
||||
// NOTE: Cannot deal with raw strings
|
||||
if (m_ssCode[m_nPos] == '\"' || m_ssCode[m_nPos] == '\'')
|
||||
{
|
||||
ProcessString();
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the current character is anything else, it represents a symbol.
|
||||
ProcessSymbol();
|
||||
}
|
||||
}
|
||||
|
||||
inline void CSimpleCppDecomposer::SkipWhitespace()
|
||||
{
|
||||
while (m_nPos < m_ssCode.size() && std::isspace(m_ssCode[m_nPos]))
|
||||
{
|
||||
// Skip concatinating lines (not setting the new line flag).
|
||||
if (m_ssCode[m_nPos] == '\\' && m_ssCode[m_nPos + 1] == '\r' && m_ssCode[m_nPos + 2] == '\n')
|
||||
{
|
||||
m_nPos += 3;
|
||||
continue;
|
||||
}
|
||||
else if (m_ssCode[m_nPos] == '\\' && m_ssCode[m_nPos + 2] == '\n')
|
||||
{
|
||||
m_nPos += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remember the new line
|
||||
if (m_ssCode[m_nPos] == '\n') m_bNewLine = true;
|
||||
|
||||
// Skip the whitespace
|
||||
m_nPos++;
|
||||
}
|
||||
}
|
||||
|
||||
inline void CSimpleCppDecomposer::SkipLine()
|
||||
{
|
||||
while (m_nPos < m_ssCode.size())
|
||||
{
|
||||
// Ignore the line ending with concatinating lines
|
||||
if (m_ssCode[m_nPos] == '\\' && m_ssCode[m_nPos + 1] == '\r' && m_ssCode[m_nPos + 2] == '\n')
|
||||
{
|
||||
m_nPos += 3;
|
||||
continue;
|
||||
}
|
||||
else if (m_ssCode[m_nPos] == '\\' && m_ssCode[m_nPos + 2] == '\n')
|
||||
{
|
||||
m_nPos += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Finished when a new line has been reached
|
||||
if (m_ssCode[m_nPos] == '\n')
|
||||
{
|
||||
m_nPos++; // Skip the newline
|
||||
m_bNewLine = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip the character
|
||||
m_nPos++;
|
||||
}
|
||||
}
|
||||
|
||||
inline void CSimpleCppDecomposer::SkipComment()
|
||||
{
|
||||
if (m_nPos >= m_ssCode.size()) return;
|
||||
if (m_ssCode[m_nPos] != '/') return;
|
||||
if (m_ssCode[m_nPos + 1] == '/') // C++ comment
|
||||
{
|
||||
SkipLine();
|
||||
return;
|
||||
}
|
||||
if (m_ssCode[m_nPos + 1] != '*') return;
|
||||
|
||||
// C comment
|
||||
m_nPos += 2;
|
||||
while (m_nPos < m_ssCode.size())
|
||||
{
|
||||
// End of comment
|
||||
if (m_ssCode[m_nPos] == '*' && m_ssCode[m_nPos + 1] == '/')
|
||||
{
|
||||
m_nPos += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
// Ignore the line ending with concatinating lines
|
||||
if (m_ssCode[m_nPos] == '\\' && m_ssCode[m_nPos + 1] == '\r' && m_ssCode[m_nPos + 2] == '\n')
|
||||
{
|
||||
m_nPos += 3;
|
||||
continue;
|
||||
}
|
||||
else if (m_ssCode[m_nPos] == '\\' && m_ssCode[m_nPos + 2] == '\n')
|
||||
{
|
||||
m_nPos += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Detect a new line
|
||||
if (m_ssCode[m_nPos] == '\n') m_bNewLine = true;
|
||||
|
||||
// Skip the character
|
||||
m_nPos++;
|
||||
}
|
||||
}
|
||||
|
||||
inline void CSimpleCppDecomposer::ProcessText()
|
||||
{
|
||||
m_bNewLine = false;
|
||||
size_t nStart = m_nPos;
|
||||
while (m_nPos < m_ssCode.size() && (std::isalnum(m_ssCode[m_nPos]) || m_ssCode[m_nPos] == '_'))
|
||||
m_nPos++;
|
||||
if (m_nPos == nStart) return;
|
||||
m_lstCode.push_back(m_ssCode.substr(nStart, m_nPos - nStart));
|
||||
}
|
||||
|
||||
inline void CSimpleCppDecomposer::ProcessString()
|
||||
{
|
||||
m_bNewLine = false;
|
||||
size_t nStart = m_nPos;
|
||||
m_nPos++; // Skip quote
|
||||
while (m_nPos < m_ssCode.size() && m_ssCode[m_nPos] != '\"' && m_ssCode[m_nPos] == '\'') m_nPos++;
|
||||
if (m_nPos < m_ssCode.size()) m_nPos++; // Skip quote
|
||||
if (m_nPos == nStart) return;
|
||||
m_lstCode.push_back(m_ssCode.substr(nStart, m_nPos - nStart));
|
||||
}
|
||||
|
||||
inline void CSimpleCppDecomposer::ProcessSymbol()
|
||||
{
|
||||
m_bNewLine = false;
|
||||
if (m_nPos >= m_ssCode.size()) return;
|
||||
m_lstCode.push_back(std::string(1, m_ssCode[m_nPos]));
|
||||
m_nPos++; // Skip symbol
|
||||
}
|
||||
|
||||
#endif // !defined SIMPLE_CPP_DECOMPOSER_INL
|
||||
103
tests/include/test_watchdog.h
Normal file
103
tests/include/test_watchdog.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#ifndef TEST_WATCHDOG_H
|
||||
#define TEST_WATCHDOG_H
|
||||
|
||||
#include <thread>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#ifdef _WIN32
|
||||
// Prevent reassignment of "interface"
|
||||
#pragma push_macro("interface")
|
||||
#undef interface
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
// Include windows headers
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#include <objbase.h>
|
||||
|
||||
// Use previous assignment of "interface"
|
||||
#pragma pop_macro("interface")
|
||||
|
||||
// Remove "GetObject" assignment
|
||||
#ifdef GetObject
|
||||
#undef GetObject
|
||||
#endif
|
||||
|
||||
// Remove "GetClassInfo" assignment
|
||||
#ifdef GetClassInfo
|
||||
#undef GetClassInfo
|
||||
#endif
|
||||
|
||||
#elif defined __unix__
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#error The OS is not supported!
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Test watchdog class; ends test process when runtime duration has superseded.
|
||||
*/
|
||||
class CProcessWatchdog
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param[in] m_iWatchdogTimeS The duration the watchdog is monitoring before process termination in seconds (default 300s).
|
||||
*/
|
||||
CProcessWatchdog(int64_t iWatchdogTimeS = 300ll)
|
||||
{
|
||||
m_threadWatchdog = std::thread(&CProcessWatchdog::WatchdogThreadFunc, this, iWatchdogTimeS );
|
||||
}
|
||||
|
||||
/**
|
||||
* @breif Destructor; cancels the watch thread.
|
||||
*/
|
||||
~CProcessWatchdog()
|
||||
{
|
||||
m_bTerminateWatchdog = true;
|
||||
if (m_threadWatchdog.joinable())
|
||||
m_threadWatchdog.join();
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Watch thread function.
|
||||
* @param[in] iWatchdogTimeS The duration the watchdog is monitoring before process termination in seconds (default 120s).
|
||||
*/
|
||||
void WatchdogThreadFunc(int64_t iWatchdogTimeS = 120ll)
|
||||
{
|
||||
// Run for the most the set time; then terminate...
|
||||
auto tpStart = std::chrono::steady_clock::now();
|
||||
while (!m_bTerminateWatchdog)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
auto tpNow = std::chrono::steady_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::seconds>(tpNow - tpStart).count() > iWatchdogTimeS)
|
||||
{
|
||||
std::cerr << "ERROR PROCESS WATCHDOG: POTENTIAL DEADLOCK DETECTED - TERMINATION ENFORCED!!!" << std::endl;
|
||||
std::cerr.flush();
|
||||
#ifdef _WIN32
|
||||
// Get the current process handle
|
||||
HANDLE hProcess = GetCurrentProcess();
|
||||
// Terminate the current process with exit code -100
|
||||
TerminateProcess(hProcess, static_cast<UINT>(-100));
|
||||
#elif defined __unix__
|
||||
|
||||
// Send SIGTERM signal to the current process
|
||||
kill(getpid(), SIGTERM);
|
||||
#else
|
||||
#error The OS is not supported!
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool m_bTerminateWatchdog = false; ///< When set, allows the thread to terminate.
|
||||
std::thread m_threadWatchdog; ///< The watchdog thread.
|
||||
};
|
||||
|
||||
#endif // !defined TEST_WATCHDOG_H
|
||||
Reference in New Issue
Block a user