// 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. #ifndef DEVICE_FIDO_TEST_CALLBACK_RECEIVER_H_ #define DEVICE_FIDO_TEST_CALLBACK_RECEIVER_H_ #include #include #include #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 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 TestCallbackReceiver { public: using TupleOfNonReferenceArgs = std::tuple...>; 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& 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 callback() { return base::BindOnce(&TestCallbackReceiver::ReceiverMethod, base::Unretained(this)); } // Takes a tuple containing the arguments the callback was called with. TupleOfNonReferenceArgs TakeResult() { auto value = std::move(result_).value(); result_.reset(); return value; } // Returns immediately if the |callback()| was already called, otherwise pumps // the current MessageLoop until it is called. void WaitForCallback() { if (was_called_) return; wait_for_callback_loop_.Run(); } private: void ReceiverMethod(CallbackArgs... args) { result_.emplace(std::forward(args)...); was_called_ = true; wait_for_callback_loop_.Quit(); } bool was_called_ = false; base::RunLoop wait_for_callback_loop_; base::Optional result_; DISALLOW_COPY_AND_ASSIGN(TestCallbackReceiver); }; template class ValueCallbackReceiver : public TestCallbackReceiver { public: const Value& value() const { return std::get<0>(*TestCallbackReceiver::result()); } }; template class StatusAndValueCallbackReceiver : public TestCallbackReceiver { public: const Status& status() const { return std::get<0>(*TestCallbackReceiver::result()); } const Value& value() const { return std::get<1>(*TestCallbackReceiver::result()); } }; template class StatusAndValuesCallbackReceiver : public TestCallbackReceiver { public: const Status& status() const { return std::get<0>(*TestCallbackReceiver::result()); } template const std::tuple_element_t>& value() const { return std::get(*TestCallbackReceiver::result()); } }; } // namespace test } // namespace device #endif // DEVICE_FIDO_TEST_CALLBACK_RECEIVER_H_