feat: ETLng monitor (#1898)

For #1594
This commit is contained in:
Alex Kremer
2025-02-21 16:10:25 +00:00
committed by GitHub
parent 25296f8ffa
commit 491cd58f93
30 changed files with 756 additions and 53 deletions

View File

@@ -128,7 +128,18 @@ TEST_F(AnyOperationTests, RepeatingOpRequestStopCallPropagated)
repeatingOp.abort();
}
TEST_F(AnyOperationTests, RepeatingOpInvokeCallPropagated)
{
EXPECT_CALL(mockRepeatingOp, invoke());
repeatingOp.invoke();
}
TEST_F(AnyOperationDeathTest, CallAbortOnNonStoppableOrCancellableOperation)
{
EXPECT_DEATH(voidOp.abort(), ".*");
}
TEST_F(AnyOperationDeathTest, CallInvokeOnNonForceInvocableOperation)
{
EXPECT_DEATH(voidOp.invoke(), ".*");
}

View File

@@ -43,6 +43,9 @@ struct AnyStrandTests : ::testing::Test {
template <typename T>
using StoppableOperationType = ::testing::NiceMock<MockStoppableOperation<T>>;
template <typename T>
using RepeatingOperationType = NiceMock<MockRepeatingOperation<T>>;
::testing::NaggyMock<MockStrand> mockStrand;
AnyStrand strand{static_cast<MockStrand&>(mockStrand)};
};
@@ -146,3 +149,15 @@ TEST_F(AnyStrandTests, ExecuteWithTimoutAndStopTokenAndReturnValueThrowsExceptio
[[maybe_unused]] auto unused = strand.execute([](auto) { return 42; }, std::chrono::milliseconds{1})
);
}
TEST_F(AnyStrandTests, RepeatingOperation)
{
auto mockRepeatingOp = RepeatingOperationType<std::any>{};
EXPECT_CALL(mockRepeatingOp, wait());
EXPECT_CALL(mockStrand, executeRepeatedly(std::chrono::milliseconds{1}, A<std::function<std::any()>>()))
.WillOnce([&mockRepeatingOp] -> RepeatingOperationType<std::any> const& { return mockRepeatingOp; });
auto res = strand.executeRepeatedly(std::chrono::milliseconds{1}, [] -> void { throw 0; });
static_assert(std::is_same_v<decltype(res), AnyOperation<void>>);
res.wait();
}

View File

@@ -24,8 +24,10 @@
#include <gtest/gtest.h>
#include <atomic>
#include <chrono>
#include <cstddef>
#include <ranges>
#include <semaphore>
#include <stdexcept>
#include <string>
@@ -220,6 +222,24 @@ TYPED_TEST(ExecutionContextTests, repeatingOperation)
EXPECT_LE(callCount, expectedActualCount); // never should be called more times than possible before timeout
}
TYPED_TEST(ExecutionContextTests, repeatingOperationForceInvoke)
{
std::atomic_size_t callCount = 64uz;
std::binary_semaphore unblock(0);
auto res = this->ctx.executeRepeatedly(std::chrono::seconds{10}, [&] {
if (--callCount == 0uz)
unblock.release();
});
for ([[maybe_unused]] auto unused : std::views::iota(0uz, callCount.load()))
res.invoke();
unblock.acquire();
res.abort();
EXPECT_EQ(callCount, 0uz);
}
TYPED_TEST(ExecutionContextTests, strandMove)
{
auto strand = this->ctx.makeStrand();
@@ -273,6 +293,43 @@ TYPED_TEST(ExecutionContextTests, strandWithTimeout)
EXPECT_EQ(res.get().value(), 42);
}
TYPED_TEST(ExecutionContextTests, strandedRepeatingOperation)
{
auto strand = this->ctx.makeStrand();
auto const repeatDelay = std::chrono::milliseconds{1};
auto const timeout = std::chrono::milliseconds{15};
auto callCount = 0uz;
auto res = strand.executeRepeatedly(repeatDelay, [&] { ++callCount; });
auto timeSpent = util::timed([timeout] { std::this_thread::sleep_for(timeout); }); // calculate actual time spent
res.abort(); // outside of the above stopwatch because it blocks and can take arbitrary time
auto const expectedPureCalls = timeout.count() / repeatDelay.count();
auto const expectedActualCount = timeSpent / repeatDelay.count();
EXPECT_GE(callCount, expectedPureCalls / 2u); // expect at least half of the scheduled calls
EXPECT_LE(callCount, expectedActualCount); // never should be called more times than possible before timeout
}
TYPED_TEST(ExecutionContextTests, strandedRepeatingOperationForceInvoke)
{
auto strand = this->ctx.makeStrand();
auto callCount = 64uz; // does not need to be atomic since we are on a strand
std::binary_semaphore unblock(0);
auto res = strand.executeRepeatedly(std::chrono::seconds{10}, [&] {
if (--callCount == 0uz)
unblock.release();
});
for ([[maybe_unused]] auto unused : std::views::iota(0uz, callCount))
res.invoke();
unblock.acquire();
res.abort();
EXPECT_EQ(callCount, 0uz);
}
TYPED_TEST(AsyncExecutionContextTests, executeAutoAborts)
{
auto value = 0;