|
|
|
@ -30,6 +30,7 @@ |
|
|
|
|
#include "src/core/lib/promise/map.h" |
|
|
|
|
#include "src/core/lib/promise/pipe.h" |
|
|
|
|
#include "src/core/lib/promise/seq.h" |
|
|
|
|
#include "src/core/lib/promise/try_seq.h" |
|
|
|
|
#include "src/core/lib/resource_quota/arena.h" |
|
|
|
|
#include "src/core/lib/resource_quota/memory_quota.h" |
|
|
|
|
#include "src/core/lib/resource_quota/resource_quota.h" |
|
|
|
@ -79,6 +80,124 @@ TEST(ForEachTest, SendThriceWithPipe) { |
|
|
|
|
EXPECT_EQ(num_received, 3); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Pollable type that stays movable until it's polled, then causes the test to
|
|
|
|
|
// fail if it's moved again.
|
|
|
|
|
// Promises have the property that they can be moved until polled, and this
|
|
|
|
|
// helps us check that the internals of ForEach respect this rule.
|
|
|
|
|
class MoveableUntilPolled { |
|
|
|
|
public: |
|
|
|
|
MoveableUntilPolled() = default; |
|
|
|
|
MoveableUntilPolled(const MoveableUntilPolled&) = delete; |
|
|
|
|
MoveableUntilPolled& operator=(const MoveableUntilPolled&) = delete; |
|
|
|
|
MoveableUntilPolled(MoveableUntilPolled&& other) noexcept : polls_(0) { |
|
|
|
|
EXPECT_EQ(other.polls_, 0); |
|
|
|
|
} |
|
|
|
|
MoveableUntilPolled& operator=(MoveableUntilPolled&& other) noexcept { |
|
|
|
|
EXPECT_EQ(other.polls_, 0); |
|
|
|
|
polls_ = 0; |
|
|
|
|
return *this; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Poll<absl::Status> operator()() { |
|
|
|
|
Activity::current()->ForceImmediateRepoll(); |
|
|
|
|
++polls_; |
|
|
|
|
if (polls_ == 10) return absl::OkStatus(); |
|
|
|
|
return Pending(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
int polls_ = 0; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
TEST(ForEachTest, NoMoveAfterPoll) { |
|
|
|
|
int num_received = 0; |
|
|
|
|
StrictMock<MockFunction<void(absl::Status)>> on_done; |
|
|
|
|
EXPECT_CALL(on_done, Call(absl::OkStatus())); |
|
|
|
|
MakeActivity( |
|
|
|
|
[&num_received] { |
|
|
|
|
Pipe<int> pipe; |
|
|
|
|
auto sender = std::make_shared<std::unique_ptr<PipeSender<int>>>( |
|
|
|
|
std::make_unique<PipeSender<int>>(std::move(pipe.sender))); |
|
|
|
|
return Map( |
|
|
|
|
Join( |
|
|
|
|
// Push one things into a pipe, then close.
|
|
|
|
|
Seq((*sender)->Push(1), |
|
|
|
|
[sender] { |
|
|
|
|
sender->reset(); |
|
|
|
|
return absl::OkStatus(); |
|
|
|
|
}), |
|
|
|
|
// Use a ForEach loop to read them out and verify all
|
|
|
|
|
// values are seen.
|
|
|
|
|
// Inject a MoveableUntilPolled into the loop to ensure that
|
|
|
|
|
// ForEach doesn't internally move a promise post-polling.
|
|
|
|
|
ForEach(std::move(pipe.receiver), |
|
|
|
|
[&num_received](int i) { |
|
|
|
|
num_received++; |
|
|
|
|
EXPECT_EQ(num_received, i); |
|
|
|
|
return MoveableUntilPolled(); |
|
|
|
|
})), |
|
|
|
|
JustElem<1>()); |
|
|
|
|
}, |
|
|
|
|
NoWakeupScheduler(), |
|
|
|
|
[&on_done](absl::Status status) { on_done.Call(std::move(status)); }, |
|
|
|
|
MakeScopedArena(1024, g_memory_allocator)); |
|
|
|
|
Mock::VerifyAndClearExpectations(&on_done); |
|
|
|
|
EXPECT_EQ(num_received, 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TEST(ForEachTest, NextResultHeldThroughCallback) { |
|
|
|
|
int num_received = 0; |
|
|
|
|
StrictMock<MockFunction<void(absl::Status)>> on_done; |
|
|
|
|
EXPECT_CALL(on_done, Call(absl::OkStatus())); |
|
|
|
|
MakeActivity( |
|
|
|
|
[&num_received] { |
|
|
|
|
Pipe<int> pipe; |
|
|
|
|
auto sender = std::make_shared<std::unique_ptr<PipeSender<int>>>( |
|
|
|
|
std::make_unique<PipeSender<int>>(std::move(pipe.sender))); |
|
|
|
|
return Map( |
|
|
|
|
Join( |
|
|
|
|
// Push one things into a pipe, then close.
|
|
|
|
|
Seq((*sender)->Push(1), |
|
|
|
|
[sender] { |
|
|
|
|
sender->reset(); |
|
|
|
|
return absl::OkStatus(); |
|
|
|
|
}), |
|
|
|
|
// Use a ForEach loop to read them out and verify all
|
|
|
|
|
// values are seen.
|
|
|
|
|
ForEach(std::move(pipe.receiver), |
|
|
|
|
[&num_received, sender](int i) { |
|
|
|
|
// While we're processing a value NextResult
|
|
|
|
|
// should be held disallowing new items to be
|
|
|
|
|
// pushed.
|
|
|
|
|
// We also should not have reached the
|
|
|
|
|
// sender->reset() line above yet either, as
|
|
|
|
|
// the Push() should block until this code
|
|
|
|
|
// completes.
|
|
|
|
|
EXPECT_TRUE(absl::holds_alternative<Pending>( |
|
|
|
|
(*sender)->Push(2)())); |
|
|
|
|
num_received++; |
|
|
|
|
EXPECT_EQ(num_received, i); |
|
|
|
|
return TrySeq( |
|
|
|
|
// has the side effect of stalling for some
|
|
|
|
|
// iterations
|
|
|
|
|
MoveableUntilPolled(), [sender] { |
|
|
|
|
// Perform the same test verifying the same
|
|
|
|
|
// properties for NextResult holding: all should
|
|
|
|
|
// still be true.
|
|
|
|
|
EXPECT_TRUE(absl::holds_alternative<Pending>( |
|
|
|
|
(*sender)->Push(2)())); |
|
|
|
|
return absl::OkStatus(); |
|
|
|
|
}); |
|
|
|
|
})), |
|
|
|
|
JustElem<1>()); |
|
|
|
|
}, |
|
|
|
|
NoWakeupScheduler(), |
|
|
|
|
[&on_done](absl::Status status) { on_done.Call(std::move(status)); }, |
|
|
|
|
MakeScopedArena(1024, g_memory_allocator)); |
|
|
|
|
Mock::VerifyAndClearExpectations(&on_done); |
|
|
|
|
EXPECT_EQ(num_received, 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} // namespace grpc_core
|
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv) { |
|
|
|
|