#include "util/MockExecutionContext.hpp" #include "util/MockOperation.hpp" #include "util/MockStrand.hpp" #include "util/async/AnyExecutionContext.hpp" #include "util/async/AnyOperation.hpp" #include "util/async/AnyStopToken.hpp" #include "util/async/AnyStrand.hpp" #include "util/async/Concepts.hpp" #include "util/async/Operation.hpp" #include "util/async/Outcome.hpp" #include "util/async/context/SyncExecutionContext.hpp" #include "util/async/context/impl/Cancellation.hpp" #include "util/async/impl/ErasedOperation.hpp" #include #include #include #include #include #include #include #include using namespace util::async; using namespace ::testing; static_assert(SomeStoppable>); static_assert(not SomeStoppable>); static_assert(SomeCancellable>); static_assert(not SomeCancellable); static_assert(SomeOperation>); static_assert(not SomeOperation); static_assert(SomeStoppableOperation>); static_assert(not SomeStoppableOperation>); static_assert(SomeCancellableOperation>); static_assert(not SomeCancellableOperation>); static_assert(SomeOutcome>); static_assert(not SomeOutcome); static_assert(SomeStopToken); static_assert(not SomeStopToken); static_assert(SomeYieldStopSource); static_assert(not SomeYieldStopSource); static_assert(not SomeYieldStopSource); static_assert(SomeSimpleStopSource); static_assert(not SomeSimpleStopSource); static_assert(SomeStopSource); static_assert(not SomeStopSource); static_assert(SomeStopSourceProvider>); static_assert(not SomeStopSourceProvider>); static_assert(SomeStoppableOutcome>); static_assert(not SomeStoppableOutcome>); static_assert(SomeHandlerWithoutStopToken); static_assert(not SomeHandlerWithoutStopToken); static_assert(SomeHandlerWith); static_assert(not SomeHandlerWith>); static_assert(SomeStdDuration); static_assert(not SomeStdDuration); static_assert(SomeStdOptional>); static_assert(not SomeStdOptional); static_assert(SomeOptStdDuration>); static_assert(not SomeOptStdDuration>); static_assert(not SomeOptStdDuration>); static_assert(NotSameAs); static_assert(not NotSameAs); static_assert(RValueNotSameAs); static_assert(not RValueNotSameAs); struct AnyExecutionContextTests : Test { using StrandType = NiceMock; template using OperationType = NiceMock>; template using StoppableOperationType = NiceMock>; template using ScheduledOperationType = NiceMock>; template using RepeatingOperationType = NiceMock>; NiceMock mockExecutionContext; AnyExecutionContext ctx{static_cast(mockExecutionContext)}; }; TEST_F(AnyExecutionContextTests, Move) { auto mockOp = OperationType{}; EXPECT_CALL(mockExecutionContext, execute(A>())) .WillOnce(ReturnRef(mockOp)); EXPECT_CALL(mockOp, get()); auto mineNow = std::move(ctx); ASSERT_TRUE(mineNow.execute([] { throw 0; }).get()); } TEST_F(AnyExecutionContextTests, CopyIsRefCounted) { auto mockOp = OperationType{}; EXPECT_CALL(mockExecutionContext, execute(A>())) .WillOnce(ReturnRef(mockOp)); EXPECT_CALL(mockOp, get()); auto yoink = ctx; ASSERT_TRUE(yoink.execute([] { throw 0; }).get()); } TEST_F(AnyExecutionContextTests, ExecuteWithoutTokenAndVoid) { auto mockOp = OperationType{}; EXPECT_CALL(mockExecutionContext, execute(A>())) .WillOnce(ReturnRef(mockOp)); EXPECT_CALL(mockOp, get()); auto op = ctx.execute([] { throw 0; }); static_assert(std::is_same_v>); ASSERT_TRUE(op.get()); } TEST_F(AnyExecutionContextTests, ExecuteWithoutTokenAndVoidThrowsException) { auto mockOp = OperationType{}; EXPECT_CALL(mockExecutionContext, execute(A>())) .WillOnce([](auto&&) -> OperationType const& { throw 0; }); EXPECT_ANY_THROW([[maybe_unused]] auto unused = ctx.execute([] { throw 0; })); } TEST_F(AnyExecutionContextTests, ExecuteWithStopTokenAndVoid) { auto mockOp = StoppableOperationType{}; EXPECT_CALL(mockExecutionContext, execute(A>(), _)) .WillOnce(ReturnRef(mockOp)); EXPECT_CALL(mockOp, get()); auto op = ctx.execute([](auto) { throw 0; }); static_assert(std::is_same_v>); ASSERT_TRUE(op.get()); } TEST_F(AnyExecutionContextTests, ExecuteWithStopTokenAndVoidThrowsException) { EXPECT_CALL(mockExecutionContext, execute(A>(), _)) .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); EXPECT_ANY_THROW([[maybe_unused]] auto unused = ctx.execute([](auto) { throw 0; })); } TEST_F(AnyExecutionContextTests, ExecuteWithStopTokenAndReturnValue) { auto mockOp = StoppableOperationType{}; EXPECT_CALL(mockOp, get()).WillOnce(Return(std::make_any(42))); EXPECT_CALL(mockExecutionContext, execute(A>(), _)) .WillOnce(ReturnRef(mockOp)); auto op = ctx.execute([](auto) -> int { throw 0; }); static_assert(std::is_same_v>); ASSERT_EQ(op.get().value(), 42); } TEST_F(AnyExecutionContextTests, ExecuteWithStopTokenAndReturnValueThrowsException) { EXPECT_CALL(mockExecutionContext, execute(A>(), _)) .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); EXPECT_ANY_THROW([[maybe_unused]] auto unused = ctx.execute([](auto) -> int { throw 0; })); } TEST_F(AnyExecutionContextTests, TimerCancellation) { auto mockScheduledOp = ScheduledOperationType{}; EXPECT_CALL(mockScheduledOp, cancel()); EXPECT_CALL( mockExecutionContext, scheduleAfter(std::chrono::milliseconds{12}, A>()) ) .WillOnce(ReturnRef(mockScheduledOp)); auto timer = ctx.scheduleAfter(std::chrono::milliseconds{12}, [](auto) { throw 0; }); static_assert(std::is_same_v>); timer.abort(); } TEST_F(AnyExecutionContextTests, TimerExecuted) { auto mockScheduledOp = ScheduledOperationType{}; EXPECT_CALL(mockScheduledOp, get()).WillOnce(Return(std::make_any(42))); EXPECT_CALL( mockExecutionContext, scheduleAfter(std::chrono::milliseconds{12}, A>()) ) .WillOnce([&mockScheduledOp](auto, auto&&) -> ScheduledOperationType const& { return mockScheduledOp; }); auto timer = ctx.scheduleAfter(std::chrono::milliseconds{12}, [](auto) -> int { throw 0; }); static_assert(std::is_same_v>); EXPECT_EQ(timer.get().value(), 42); } TEST_F(AnyExecutionContextTests, TimerWithBoolHandlerCancellation) { auto mockScheduledOp = ScheduledOperationType{}; EXPECT_CALL(mockScheduledOp, cancel()); EXPECT_CALL( mockExecutionContext, scheduleAfter( std::chrono::milliseconds{12}, A>() ) ) .WillOnce(ReturnRef(mockScheduledOp)); auto timer = ctx.scheduleAfter(std::chrono::milliseconds{12}, [](auto, bool) { throw 0; }); static_assert(std::is_same_v>); timer.abort(); } TEST_F(AnyExecutionContextTests, TimerWithBoolHandlerExecuted) { auto mockScheduledOp = ScheduledOperationType{}; EXPECT_CALL(mockScheduledOp, get()).WillOnce(Return(std::make_any(42))); EXPECT_CALL( mockExecutionContext, scheduleAfter( std::chrono::milliseconds{12}, A>() ) ) .WillOnce([&mockScheduledOp](auto, auto&&) -> ScheduledOperationType const& { return mockScheduledOp; }); auto timer = ctx.scheduleAfter(std::chrono::milliseconds{12}, [](auto, bool) -> int { throw 0; }); static_assert(std::is_same_v>); EXPECT_EQ(timer.get().value(), 42); } TEST_F(AnyExecutionContextTests, RepeatingOperation) { auto mockRepeatingOp = RepeatingOperationType{}; EXPECT_CALL(mockRepeatingOp, wait()); EXPECT_CALL( mockExecutionContext, executeRepeatedly(std::chrono::milliseconds{1}, A>()) ) .WillOnce([&mockRepeatingOp] -> RepeatingOperationType const& { return mockRepeatingOp; }); auto res = ctx.executeRepeatedly(std::chrono::milliseconds{1}, [] -> void { throw 0; }); static_assert(std::is_same_v>); res.wait(); } TEST_F(AnyExecutionContextTests, StrandExecuteWithVoid) { auto mockOp = OperationType{}; auto mockStrand = StrandType{}; EXPECT_CALL(mockOp, get()); EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); EXPECT_CALL(mockStrand, execute(A>())).WillOnce(ReturnRef(mockOp)); auto strand = ctx.makeStrand(); static_assert(std::is_same_v); auto op = strand.execute([] { throw 0; }); static_assert(std::is_same_v>); ASSERT_TRUE(op.get()); } TEST_F(AnyExecutionContextTests, StrandExecuteWithVoidThrowsException) { auto mockStrand = StrandType{}; EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); EXPECT_CALL(mockStrand, execute(A>())) .WillOnce([](auto&&) -> OperationType const& { throw 0; }); auto strand = ctx.makeStrand(); static_assert(std::is_same_v); EXPECT_ANY_THROW([[maybe_unused]] auto unused = strand.execute([] {})); } TEST_F(AnyExecutionContextTests, StrandExecuteWithReturnValue) { auto mockOp = OperationType{}; auto mockStrand = StrandType{}; EXPECT_CALL(mockOp, get()).WillOnce(Return(std::make_any(42))); EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); EXPECT_CALL(mockStrand, execute(A>())).WillOnce(ReturnRef(mockOp)); auto strand = ctx.makeStrand(); static_assert(std::is_same_v); auto op = strand.execute([]() -> int { throw 0; }); static_assert(std::is_same_v>); EXPECT_EQ(op.get().value(), 42); } TEST_F(AnyExecutionContextTests, StrandExecuteWithReturnValueThrowsException) { auto mockStrand = StrandType{}; EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); EXPECT_CALL(mockStrand, execute(A>())) .WillOnce([](auto&&) -> OperationType const& { throw 0; }); auto strand = ctx.makeStrand(); static_assert(std::is_same_v); EXPECT_ANY_THROW([[maybe_unused]] auto unused = strand.execute([]() -> int { throw 0; })); } TEST_F(AnyExecutionContextTests, StrandExecuteWithStopTokenAndVoid) { auto mockOp = StoppableOperationType{}; auto mockStrand = StrandType{}; EXPECT_CALL(mockOp, get()); EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); EXPECT_CALL(mockStrand, execute(A>(), _)) .WillOnce(ReturnRef(mockOp)); auto strand = ctx.makeStrand(); static_assert(std::is_same_v); auto op = strand.execute([](auto) { throw 0; }); static_assert(std::is_same_v>); ASSERT_TRUE(op.get()); } TEST_F(AnyExecutionContextTests, StrandExecuteWithStopTokenAndVoidThrowsException) { auto mockStrand = StrandType{}; EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); EXPECT_CALL(mockStrand, execute(A>(), _)) .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); auto strand = ctx.makeStrand(); static_assert(std::is_same_v); EXPECT_ANY_THROW([[maybe_unused]] auto unused = strand.execute([](auto) { throw 0; })); } TEST_F(AnyExecutionContextTests, StrandExecuteWithStopTokenAndReturnValue) { auto mockOp = StoppableOperationType{}; auto mockStrand = StrandType{}; EXPECT_CALL(mockOp, get()).WillOnce(Return(std::make_any(42))); EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); EXPECT_CALL(mockStrand, execute(A>(), _)) .WillOnce(ReturnRef(mockOp)); auto strand = ctx.makeStrand(); static_assert(std::is_same_v); auto op = strand.execute([](auto) -> int { throw 0; }); static_assert(std::is_same_v>); EXPECT_EQ(op.get().value(), 42); } TEST_F(AnyExecutionContextTests, StrandExecuteWithStopTokenAndReturnValueThrowsException) { auto mockStrand = StrandType{}; EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); EXPECT_CALL(mockStrand, execute(A>(), _)) .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); auto strand = ctx.makeStrand(); static_assert(std::is_same_v); EXPECT_ANY_THROW([[maybe_unused]] auto unused = strand.execute([](auto) -> int { throw 0; })); }