blob: 505d01425b57b6b946eca70d33305a8d50be3ab2 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "peridot/bin/ledger/testing/loop_controller_test_loop.h"
#include <lib/async/cpp/task.h>
#include <lib/fit/defer.h>
#include <memory>
namespace ledger {
namespace {
class SubLoopTestLoop : public SubLoop {
public:
explicit SubLoopTestLoop(LoopController* controller,
std::unique_ptr<async::LoopInterface> loop_interface)
: controller_(controller), loop_interface_(std::move(loop_interface)) {}
void DrainAndQuit() override {
// TODO(qsr): Implement drain on TestLoop.
auto waiter = controller_->NewWaiter();
async::PostTask(dispatcher(), waiter->GetCallback());
FXL_CHECK(waiter->RunUntilCalled());
}
async_dispatcher_t* dispatcher() override {
return loop_interface_->dispatcher();
}
private:
LoopController* controller_;
std::unique_ptr<async::LoopInterface> loop_interface_;
};
class CallbackWaiterImpl : public CallbackWaiter {
public:
explicit CallbackWaiterImpl(LoopController* loop) : loop_(loop) {}
CallbackWaiterImpl(const CallbackWaiterImpl&) = delete;
CallbackWaiterImpl& operator=(const CallbackWaiterImpl&) = delete;
~CallbackWaiterImpl() override = default;
fit::function<void()> GetCallback() override {
return [this] {
++callback_called_count_;
if (running_) {
loop_->StopLoop();
}
};
}
bool RunUntilCalled() override {
FXL_DCHECK(!running_);
running_ = true;
auto cleanup = fit::defer([this] { running_ = false; });
bool called = loop_->RunLoopUntil([this] { return !NotCalledYet(); });
if (called) {
++run_until_called_count_;
}
return called;
}
bool NotCalledYet() override {
return callback_called_count_ <= run_until_called_count_;
}
private:
LoopController* loop_;
size_t callback_called_count_ = 0;
size_t run_until_called_count_ = 0;
// Whether the waiter is currently in the |RunUntilCalled| method.
bool running_ = false;
};
} // namespace
LoopControllerTestLoop::LoopControllerTestLoop(async::TestLoop* loop)
: loop_(loop) {}
LoopControllerTestLoop::~LoopControllerTestLoop() {}
void LoopControllerTestLoop::RunLoop() { loop_->RunUntilIdle(); }
void LoopControllerTestLoop::StopLoop() { loop_->Quit(); }
std::unique_ptr<SubLoop> LoopControllerTestLoop::StartNewLoop() {
return std::make_unique<SubLoopTestLoop>(
this,
std::unique_ptr<async::LoopInterface>(loop_->StartNewLoop().release()));
}
std::unique_ptr<CallbackWaiter> LoopControllerTestLoop::NewWaiter() {
return std::make_unique<CallbackWaiterImpl>(this);
}
async_dispatcher_t* LoopControllerTestLoop::dispatcher() {
return loop_->dispatcher();
}
bool LoopControllerTestLoop::RunLoopUntil(fit::function<bool()> condition) {
if (condition()) {
return true;
}
// The condition is not true, but might be triggered after some delay due to a
// delayed task (for example, because of backoffs). Try to advance the loop in
// bigger and bigger increment. Fail if the event does not occur after ~100s
// as if something doesn't happen in 100 simulated s, then it will almost
// certainly be a problem for tests using a real loop.
for (size_t i : {0, 1, 10, 100}) {
loop_->RunFor(zx::sec(i));
if (condition()) {
return true;
}
}
return false;
}
void LoopControllerTestLoop::RunLoopFor(zx::duration duration) {
loop_->RunFor(duration);
}
} // namespace ledger