#include "util/MockOperation.hpp" #include "util/MockStrand.hpp" #include "util/async/AnyOperation.hpp" #include "util/async/AnyStopToken.hpp" #include "util/async/AnyStrand.hpp" #include #include #include #include #include #include #include #include using namespace util::async; using namespace ::testing; struct AnyStrandTests : ::testing::Test { template using OperationType = ::testing::NiceMock>; template using StoppableOperationType = ::testing::NiceMock>; template using RepeatingOperationType = NiceMock>; ::testing::NaggyMock mockStrand; AnyStrand strand{static_cast(mockStrand)}; }; TEST_F(AnyStrandTests, Move) { auto mockOp = OperationType{}; EXPECT_CALL(mockStrand, execute(An>())).WillOnce(ReturnRef(mockOp)); EXPECT_CALL(mockOp, get()); auto mineNow = std::move(strand); ASSERT_TRUE(mineNow.execute([] { throw 0; }).get()); } TEST_F(AnyStrandTests, CopyIsRefCounted) { auto mockOp = OperationType{}; EXPECT_CALL(mockStrand, execute(An>())).WillOnce(ReturnRef(mockOp)); auto yoink = strand; ASSERT_TRUE(yoink.execute([] { throw 0; }).get()); } TEST_F(AnyStrandTests, ExecuteWithoutTokenAndVoid) { auto mockOp = OperationType{}; EXPECT_CALL(mockStrand, execute(An>())).WillOnce(ReturnRef(mockOp)); auto op = strand.execute([] {}); static_assert(std::is_same_v>); ASSERT_TRUE(op.get()); } TEST_F(AnyStrandTests, ExecuteWithoutTokenAndVoidThrowsException) { auto mockOp = OperationType{}; EXPECT_CALL(mockStrand, execute(An>())) .WillOnce([](auto&&) -> OperationType const& { throw 0; }); EXPECT_ANY_THROW([[maybe_unused]] auto unused = strand.execute([] {})); } TEST_F(AnyStrandTests, ExecuteWithStopTokenAndVoid) { auto mockOp = StoppableOperationType{}; EXPECT_CALL(mockStrand, execute(An>(), _)) .WillOnce(ReturnRef(mockOp)); auto op = strand.execute([](auto) {}); static_assert(std::is_same_v>); ASSERT_TRUE(op.get()); } TEST_F(AnyStrandTests, ExecuteWithStopTokenAndVoidThrowsException) { EXPECT_CALL(mockStrand, execute(An>(), _)) .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); EXPECT_ANY_THROW([[maybe_unused]] auto unused = strand.execute([](auto) {})); } TEST_F(AnyStrandTests, ExecuteWithStopTokenAndReturnValue) { auto mockOp = StoppableOperationType{}; EXPECT_CALL(mockOp, get()).WillOnce(Return(std::make_any(42))); EXPECT_CALL(mockStrand, execute(An>(), _)) .WillOnce(ReturnRef(mockOp)); auto op = strand.execute([](auto) { return 42; }); static_assert(std::is_same_v>); ASSERT_EQ(op.get().value(), 42); } TEST_F(AnyStrandTests, ExecuteWithStopTokenAndReturnValueThrowsException) { EXPECT_CALL(mockStrand, execute(An>(), _)) .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); EXPECT_ANY_THROW([[maybe_unused]] auto unused = strand.execute([](auto) { return 42; })); } TEST_F(AnyStrandTests, ExecuteWithTimeoutAndStopTokenAndReturnValue) { auto mockOp = StoppableOperationType{}; EXPECT_CALL(mockOp, get()).WillOnce(Return(std::make_any(42))); EXPECT_CALL(mockStrand, execute(An>(), _)) .WillOnce(ReturnRef(mockOp)); auto op = strand.execute([](auto) { return 42; }, std::chrono::milliseconds{1}); static_assert(std::is_same_v>); ASSERT_EQ(op.get().value(), 42); } TEST_F(AnyStrandTests, ExecuteWithTimeoutAndStopTokenAndReturnValueThrowsException) { EXPECT_CALL(mockStrand, execute(An>(), _)) .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); EXPECT_ANY_THROW( [[maybe_unused]] auto unused = strand.execute([](auto) { return 42; }, std::chrono::milliseconds{1}) ); } TEST_F(AnyStrandTests, RepeatingOperation) { auto mockRepeatingOp = RepeatingOperationType{}; EXPECT_CALL(mockRepeatingOp, wait()); EXPECT_CALL( mockStrand, executeRepeatedly(std::chrono::milliseconds{1}, A>()) ) .WillOnce([&mockRepeatingOp] -> RepeatingOperationType const& { return mockRepeatingOp; }); auto res = strand.executeRepeatedly(std::chrono::milliseconds{1}, [] -> void { throw 0; }); static_assert(std::is_same_v>); res.wait(); }