blob: a957830719f6806d97560bf3f4f461800197ac0f [file] [log] [blame]
// Copyright 2018 The Chromium 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 <tuple>
#include <type_traits>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/run_loop.h"
namespace device {
namespace test {
// Serves as a testing callback target on which callbacks with an arbitrary
// signature `void(CallbackArgs...)` can be invoked on.
// Example usage:
// base::test::TaskEnvironment task_environment;
// TestCallbackReceiver<int> callback_receiver;
// // Manufacture the base::OnceCallback whose invovcation will be received
// // by this instance.
// auto callback = callback_receiver.callback();
// // Pass |callback| into testing code that will invoke it.
// DoStuffAndInvokeCallbackWithResult(std::move(callback));
// // Spin the message loop until the callback is invoked, and read result.
// callback_receiver.WaitForCallback();
// DoStuffWithResult(std::get<0>(*callback_receiver.result());
template <class... CallbackArgs>
class TestCallbackReceiver {
using TupleOfNonReferenceArgs = std::tuple<std::decay_t<CallbackArgs>...>;
TestCallbackReceiver() = default;
~TestCallbackReceiver() = default;
// Whether the |callback| was already called.
bool was_called() const { return was_called_; }
// The result, which is non-null exactly if the callback was already invoked
// and the result has not yet been taken with TakeResult().
const base::Optional<TupleOfNonReferenceArgs>& result() const {
return result_;
// Constructs a base::OnceCallback that can be passed into code under test and
// be waited, but must not be invoked after |this| instance goes out of scope.
// This method can only be called once during the lifetime of an instance.
// Construct multiple TestCallbackReceiver instances for multiple callbacks.
base::OnceCallback<void(CallbackArgs...)> callback() {
return base::BindOnce(&TestCallbackReceiver::ReceiverMethod,
// Takes a tuple containing the arguments the callback was called with.
TupleOfNonReferenceArgs TakeResult() {
auto value = std::move(result_).value();
return value;
// Returns immediately if the |callback()| was already called, otherwise pumps
// the current MessageLoop until it is called.
void WaitForCallback() {
if (was_called_)
void ReceiverMethod(CallbackArgs... args) {
was_called_ = true;
bool was_called_ = false;
base::RunLoop wait_for_callback_loop_;
base::Optional<TupleOfNonReferenceArgs> result_;
template <class Value>
class ValueCallbackReceiver : public TestCallbackReceiver<Value> {
const Value& value() const {
return std::get<0>(*TestCallbackReceiver<Value>::result());
template <class Status, class Value>
class StatusAndValueCallbackReceiver
: public TestCallbackReceiver<Status, Value> {
const Status& status() const {
return std::get<0>(*TestCallbackReceiver<Status, Value>::result());
const Value& value() const {
return std::get<1>(*TestCallbackReceiver<Status, Value>::result());
template <class Status, class... Values>
class StatusAndValuesCallbackReceiver
: public TestCallbackReceiver<Status, Values...> {
const Status& status() const {
return std::get<0>(*TestCallbackReceiver<Status, Values...>::result());
template <size_t I>
const std::tuple_element_t<I, std::tuple<Values...>>& value() const {
return std::get<I + 1>(*TestCallbackReceiver<Status, Values...>::result());
} // namespace test
} // namespace device