icinga2/test/base-testloggerfixture.hpp
Julian Brost 4b4d15ce72
Some checks failed
Container Image / Container Image (push) Has been cancelled
Linux / alpine:bash (push) Has been cancelled
Linux / amazonlinux:2 (push) Has been cancelled
Linux / amazonlinux:2023 (push) Has been cancelled
Linux / debian:11 (linux/386) (push) Has been cancelled
Linux / debian:11 (push) Has been cancelled
Linux / debian:12 (linux/386) (push) Has been cancelled
Linux / debian:12 (push) Has been cancelled
Linux / debian:13 (push) Has been cancelled
Linux / fedora:41 (push) Has been cancelled
Linux / fedora:42 (push) Has been cancelled
Linux / fedora:43 (push) Has been cancelled
Linux / opensuse/leap:15.6 (push) Has been cancelled
Linux / opensuse/leap:16.0 (push) Has been cancelled
Linux / registry.suse.com/bci/bci-base:16.0 (push) Has been cancelled
Linux / registry.suse.com/suse/sle15:15.6 (push) Has been cancelled
Linux / registry.suse.com/suse/sle15:15.7 (push) Has been cancelled
Linux / rockylinux/rockylinux:10 (push) Has been cancelled
Linux / rockylinux:8 (push) Has been cancelled
Linux / rockylinux:9 (push) Has been cancelled
Linux / ubuntu:22.04 (push) Has been cancelled
Linux / ubuntu:24.04 (push) Has been cancelled
Linux / ubuntu:25.04 (push) Has been cancelled
Linux / ubuntu:25.10 (push) Has been cancelled
Windows / Windows (push) Has been cancelled
Merge pull request #10700 from Icinga/license-gpl3-later
Upgrade license to GPLv3 & replace existing copyright with SPDX header
2026-02-05 13:14:54 +01:00

137 lines
3.8 KiB
C++

// SPDX-FileCopyrightText: 2025 Icinga GmbH <https://icinga.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef TEST_LOGGER_FIXTURE_H
#define TEST_LOGGER_FIXTURE_H
#include <BoostTestTargetConfig.h>
#include "base/logger.hpp"
#include <boost/range/algorithm.hpp>
#include <boost/regex.hpp>
#include <boost/test/test_tools.hpp>
#include <future>
namespace icinga {
class TestLogger : public Logger
{
public:
DECLARE_PTR_TYPEDEFS(TestLogger);
struct Expect
{
std::string pattern;
std::promise<bool> prom;
};
auto ExpectLogPattern(const std::string& pattern,
const std::chrono::milliseconds& timeout = std::chrono::seconds(0))
{
std::unique_lock lock(m_Mutex);
for (const auto& logEntry : m_LogEntries) {
if (boost::regex_match(logEntry.Message.GetData(), boost::regex(pattern))) {
return boost::test_tools::assertion_result{true};
}
}
if (timeout == std::chrono::seconds(0)) {
return boost::test_tools::assertion_result{false};
}
auto expect = std::make_shared<Expect>(Expect{pattern, std::promise<bool>()});
m_Expects.emplace_back(expect);
lock.unlock();
auto future = expect->prom.get_future();
auto status = future.wait_for(timeout);
boost::test_tools::assertion_result ret{status == std::future_status::ready && future.get()};
ret.message() << "Pattern \"" << pattern << "\" in log within " << timeout.count() << "ms";
lock.lock();
m_Expects.erase(boost::range::remove(m_Expects, expect), m_Expects.end());
return ret;
}
void Clear()
{
std::lock_guard lock(m_Mutex);
m_Expects.clear();
m_LogEntries.clear();
}
private:
void ProcessLogEntry(const LogEntry& entry) override
{
std::unique_lock lock(m_Mutex);
m_LogEntries.push_back(entry);
auto it = boost::range::remove_if(m_Expects, [&entry](const std::shared_ptr<Expect>& expect) {
if (boost::regex_match(entry.Message.GetData(), boost::regex(expect->pattern))) {
expect->prom.set_value(true);
return true;
}
return false;
});
m_Expects.erase(it, m_Expects.end());
}
void Flush() override {}
std::mutex m_Mutex;
std::vector<std::shared_ptr<Expect>> m_Expects;
std::vector<LogEntry> m_LogEntries;
};
/**
* A fixture to capture log entries and assert their presence in tests.
*
* Currently, this only supports checking existing entries and waiting for new ones
* using ExpectLogPattern(), but more functionality can easily be added in the future,
* like only asserting on past log messages, only waiting for new ones, asserting log
* entry metadata (severity etc.) and so on.
*/
struct TestLoggerFixture
{
TestLoggerFixture()
{
testLogger->SetSeverity(testLogger->SeverityToString(LogDebug));
testLogger->SetActive(true);
testLogger->Activate(true);
}
~TestLoggerFixture()
{
testLogger->SetActive(false);
testLogger->Deactivate(true);
}
/**
* Asserts the presence of a log entry that matches the given regex pattern.
*
* First, the existing log entries are searched for the pattern. If the pattern isn't found,
* until the timeout is reached, the function will wait if a new log message is added that
* matches the pattern.
*
* A boost assertion result object is returned, that evaluates to bool, but contains an
* error message that is printed by the testing framework when the assert failed.
*
* @param pattern The regex pattern the log message needs to match
* @param timeout The maximum amount of time to wait for the log message to arrive
*
* @return A @c boost::test_tools::assertion_result object that can be used in BOOST_REQUIRE
*/
auto ExpectLogPattern(const std::string& pattern,
const std::chrono::milliseconds& timeout = std::chrono::seconds(0))
{
return testLogger->ExpectLogPattern(pattern, timeout);
}
void ClearTestLogger() const { testLogger->Clear(); }
TestLogger::Ptr testLogger = new TestLogger;
};
} // namespace icinga
#endif // TEST_LOGGER_FIXTURE_H