blob: 13785db92d0aade33b04255b5e9c0bffdcc0e861 [file] [log] [blame]
// Copyright 2019 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 "src/ledger/bin/testing/run_in_coroutine.h"
#include <lib/fit/function.h>
#include "src/ledger/bin/app/flags.h"
#include "src/ledger/lib/coroutine/coroutine.h"
namespace ledger {
namespace {
// Wrapper around a real CoroutineHandler for test.
//
// The wrapper allows to delay re-entering the coroutine body when the run loop is running. When
// |Resume| is called, it quits the loop, and the main method calls |ResumeIfNeeded| when the loop
// exits.
class TestCoroutineHandler : public coroutine::CoroutineHandler {
public:
explicit TestCoroutineHandler(coroutine::CoroutineHandler* delegate, fit::closure quit_callback)
: delegate_(delegate), quit_callback_(std::move(quit_callback)) {}
coroutine::ContinuationStatus Yield() override { return delegate_->Yield(); }
void Resume(coroutine::ContinuationStatus status) override {
// If interrupting, no need to delay the call as the test will not run the loop itself.
if (status == coroutine::ContinuationStatus::INTERRUPTED) {
delegate_->Resume(status);
return;
}
quit_callback_();
need_to_continue_ = true;
}
// Re-enters the coroutine body if the handler delayed the call. Returns true if the coroutine was
// indeed resumed, false otherwise.
bool ResumeIfNeeded() {
if (need_to_continue_) {
need_to_continue_ = false;
delegate_->Resume(coroutine::ContinuationStatus::OK);
return true;
}
return false;
}
private:
coroutine::CoroutineHandler* delegate_;
fit::closure quit_callback_;
bool need_to_continue_ = false;
};
} // namespace
bool RunInCoroutine(async::TestLoop* test_loop, coroutine::CoroutineService* coroutine_service,
fit::function<void(coroutine::CoroutineHandler*)> run_test,
zx::duration delay) {
std::unique_ptr<TestCoroutineHandler> test_handler;
volatile bool ended = false;
coroutine_service->StartCoroutine([&](coroutine::CoroutineHandler* handler) {
test_handler =
std::make_unique<TestCoroutineHandler>(handler, [test_loop] { test_loop->Quit(); });
run_test(test_handler.get());
ended = true;
});
while (!ended) {
bool has_resumed = test_handler->ResumeIfNeeded();
bool tasks_executed = test_loop->RunFor(delay);
if (!has_resumed && !tasks_executed) {
// Coroutine stopped executing but did not end.
return false;
}
}
return true;
}
} // namespace ledger