Skip to content

Commit d82059e

Browse files
authored
Add common WaitFor helper to the test actor runtime (#7725)
1 parent 63599f6 commit d82059e

File tree

2 files changed

+121
-6
lines changed

2 files changed

+121
-6
lines changed

ydb/core/testlib/actors/test_runtime.h

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,22 +84,42 @@ namespace NActors {
8484
void SimulateSleep(TDuration duration);
8585

8686
template<class TResult>
87-
inline TResult WaitFuture(NThreading::TFuture<TResult> f) {
87+
inline TResult WaitFuture(NThreading::TFuture<TResult> f, TDuration simTimeout = TDuration::Max()) {
8888
if (!f.HasValue() && !f.HasException()) {
8989
TDispatchOptions options;
9090
options.CustomFinalCondition = [&]() {
9191
return f.HasValue() || f.HasException();
9292
};
93-
options.FinalEvents.emplace_back([&](IEventHandle&) {
94-
return f.HasValue() || f.HasException();
95-
});
93+
// Quirk: non-empty FinalEvents enables full simulation
94+
options.FinalEvents.emplace_back([](IEventHandle&) { return false; });
9695

97-
this->DispatchEvents(options);
96+
this->DispatchEvents(options, simTimeout);
9897

9998
Y_ABORT_UNLESS(f.HasValue() || f.HasException());
10099
}
101100

102-
return f.ExtractValue();
101+
if constexpr (!std::is_same_v<TResult, void>) {
102+
return f.ExtractValue();
103+
} else {
104+
return f.GetValue();
105+
}
106+
}
107+
108+
template<class TCondition>
109+
inline void WaitFor(const TString& description, const TCondition& condition, TDuration simTimeout = TDuration::Max()) {
110+
if (!condition()) {
111+
TDispatchOptions options;
112+
options.CustomFinalCondition = [&]() {
113+
return condition();
114+
};
115+
// Quirk: non-empty FinalEvents enables full simulation
116+
options.FinalEvents.emplace_back([](IEventHandle&) { return false; });
117+
118+
Cerr << "... waiting for " << description << Endl;
119+
this->DispatchEvents(options, simTimeout);
120+
121+
Y_ABORT_UNLESS(condition(), "Timeout while waiting for %s", description.c_str());
122+
}
103123
}
104124

105125
void SendToPipe(ui64 tabletId, const TActorId& sender, IEventBase* payload, ui32 nodeIndex = 0,

ydb/core/testlib/actors/test_runtime_ut.cpp

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,101 @@ Y_UNIT_TEST_SUITE(TActorTest) {
622622
UNIT_ASSERT_VALUES_EQUAL(event->Get()->Index, 12u);
623623
}
624624
}
625+
626+
Y_UNIT_TEST(TestWaitFuture) {
627+
enum EEv {
628+
EvTrigger = EventSpaceBegin(TEvents::ES_PRIVATE)
629+
};
630+
631+
struct TEvTrigger : public TEventLocal<TEvTrigger, EvTrigger> {
632+
TEvTrigger() = default;
633+
};
634+
635+
class TTriggerActor : public TActorBootstrapped<TTriggerActor> {
636+
public:
637+
TTriggerActor(NThreading::TPromise<void> promise)
638+
: Promise(std::move(promise))
639+
{}
640+
641+
void Bootstrap() {
642+
Schedule(TDuration::Seconds(1), new TEvTrigger);
643+
Become(&TThis::StateWork);
644+
}
645+
646+
private:
647+
STFUNC(StateWork) {
648+
switch (ev->GetTypeRewrite()) {
649+
hFunc(TEvTrigger, Handle);
650+
}
651+
}
652+
653+
void Handle(TEvTrigger::TPtr&) {
654+
Promise.SetValue();
655+
PassAway();
656+
}
657+
658+
private:
659+
NThreading::TPromise<void> Promise;
660+
};
661+
662+
TTestActorRuntime runtime;
663+
runtime.Initialize(MakeEgg());
664+
665+
NThreading::TPromise<void> promise = NThreading::NewPromise<void>();
666+
NThreading::TFuture<void> future = promise.GetFuture();
667+
668+
auto actor = runtime.Register(new TTriggerActor(std::move(promise)));
669+
runtime.EnableScheduleForActor(actor);
670+
671+
runtime.WaitFuture(std::move(future));
672+
}
673+
674+
Y_UNIT_TEST(TestWaitFor) {
675+
enum EEv {
676+
EvTrigger = EventSpaceBegin(TEvents::ES_PRIVATE)
677+
};
678+
679+
struct TEvTrigger : public TEventLocal<TEvTrigger, EvTrigger> {
680+
TEvTrigger() = default;
681+
};
682+
683+
class TTriggerActor : public TActorBootstrapped<TTriggerActor> {
684+
public:
685+
TTriggerActor(int* ptr)
686+
: Ptr(ptr)
687+
{}
688+
689+
void Bootstrap() {
690+
Schedule(TDuration::Seconds(1), new TEvTrigger);
691+
Become(&TThis::StateWork);
692+
}
693+
694+
private:
695+
STFUNC(StateWork) {
696+
switch (ev->GetTypeRewrite()) {
697+
hFunc(TEvTrigger, Handle);
698+
}
699+
}
700+
701+
void Handle(TEvTrigger::TPtr&) {
702+
*Ptr = 42;
703+
PassAway();
704+
}
705+
706+
private:
707+
int* Ptr;
708+
};
709+
710+
TTestActorRuntime runtime;
711+
runtime.Initialize(MakeEgg());
712+
713+
int value = 0;
714+
auto actor = runtime.Register(new TTriggerActor(&value));
715+
runtime.EnableScheduleForActor(actor);
716+
717+
runtime.WaitFor("value = 42", [&]{ return value == 42; });
718+
UNIT_ASSERT_VALUES_EQUAL(value, 42);
719+
}
625720
};
626721

627722
}

0 commit comments

Comments
 (0)