#ifndef DEVICE_FIDO_TEST_CALLBACK_RECEIVER_H_
#define DEVICE_FIDO_TEST_CALLBACK_RECEIVER_H_
#include <tuple>
#include <type_traits>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/run_loop.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace device {
namespace test {
template <class... CallbackArgs>
class TestCallbackReceiver {
public:
using TupleOfNonReferenceArgs = std::tuple<std::decay_t<CallbackArgs>...>;
TestCallbackReceiver() = default;
TestCallbackReceiver(const TestCallbackReceiver&) = delete;
TestCallbackReceiver& operator=(const TestCallbackReceiver&) = delete;
~TestCallbackReceiver() = default;
bool was_called() const { return was_called_; }
const absl::optional<TupleOfNonReferenceArgs>& result() const {
return result_;
}
base::OnceCallback<void(CallbackArgs...)> callback() {
return base::BindOnce(&TestCallbackReceiver::ReceiverMethod,
base::Unretained(this));
}
TupleOfNonReferenceArgs TakeResult() {
auto value = std::move(result_).value();
result_.reset();
return value;
}
void WaitForCallback() {
if (was_called_)
return;
wait_for_callback_loop_.Run();
}
private:
void ReceiverMethod(CallbackArgs... args) {
result_.emplace(std::forward<CallbackArgs>(args)...);
was_called_ = true;
wait_for_callback_loop_.Quit();
}
bool was_called_ = false;
base::RunLoop wait_for_callback_loop_;
absl::optional<TupleOfNonReferenceArgs> result_;
};
template <class Value>
class ValueCallbackReceiver : public TestCallbackReceiver<Value> {
public:
const Value& value() const {
return std::get<0>(*TestCallbackReceiver<Value>::result());
}
};
template <class Status, class Value>
class StatusAndValueCallbackReceiver
: public TestCallbackReceiver<Status, Value> {
public:
const Status& status() const {
return std::get<0>(*TestCallbackReceiver<Status, Value>::result());
}
const Value& value() const {
return std::get<1>(*TestCallbackReceiver<Status, Value>::result());
}
Value TakeValue() {
return std::move(
std::get<1>(TestCallbackReceiver<Status, Value>::TakeResult()));
}
};
template <class Status, class... Values>
class StatusAndValuesCallbackReceiver
: public TestCallbackReceiver<Status, Values...> {
public:
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());
}
};
}
}
#endif