1 // Copyright 2018 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef DEVICE_FIDO_TEST_CALLBACK_RECEIVER_H_ 6 #define DEVICE_FIDO_TEST_CALLBACK_RECEIVER_H_ 7 8 #include <tuple> 9 #include <type_traits> 10 #include <utility> 11 12 #include "base/bind.h" 13 #include "base/bind_helpers.h" 14 #include "base/callback.h" 15 #include "base/macros.h" 16 #include "base/optional.h" 17 #include "base/run_loop.h" 18 19 namespace device { 20 namespace test { 21 22 // Serves as a testing callback target on which callbacks with an arbitrary 23 // signature `void(CallbackArgs...)` can be invoked on. 24 // 25 // Example usage: 26 // base::test::TaskEnvironment task_environment; 27 // TestCallbackReceiver<int> callback_receiver; 28 // 29 // // Manufacture the base::OnceCallback whose invovcation will be received 30 // // by this instance. 31 // auto callback = callback_receiver.callback(); 32 // 33 // // Pass |callback| into testing code that will invoke it. 34 // DoStuffAndInvokeCallbackWithResult(std::move(callback)); 35 // 36 // // Spin the message loop until the callback is invoked, and read result. 37 // callback_receiver.WaitForCallback(); 38 // DoStuffWithResult(std::get<0>(*callback_receiver.result()); 39 // 40 template <class... CallbackArgs> 41 class TestCallbackReceiver { 42 public: 43 using TupleOfNonReferenceArgs = std::tuple<std::decay_t<CallbackArgs>...>; 44 45 TestCallbackReceiver() = default; 46 ~TestCallbackReceiver() = default; 47 48 // Whether the |callback| was already called. was_called()49 bool was_called() const { return was_called_; } 50 51 // The result, which is non-null exactly if the callback was already invoked 52 // and the result has not yet been taken with TakeResult(). result()53 const base::Optional<TupleOfNonReferenceArgs>& result() const { 54 return result_; 55 } 56 57 // Constructs a base::OnceCallback that can be passed into code under test and 58 // be waited, but must not be invoked after |this| instance goes out of scope. 59 // 60 // This method can only be called once during the lifetime of an instance. 61 // Construct multiple TestCallbackReceiver instances for multiple callbacks. callback()62 base::OnceCallback<void(CallbackArgs...)> callback() { 63 return base::BindOnce(&TestCallbackReceiver::ReceiverMethod, 64 base::Unretained(this)); 65 } 66 67 // Takes a tuple containing the arguments the callback was called with. TakeResult()68 TupleOfNonReferenceArgs TakeResult() { 69 auto value = std::move(result_).value(); 70 result_.reset(); 71 return value; 72 } 73 74 // Returns immediately if the |callback()| was already called, otherwise pumps 75 // the current MessageLoop until it is called. WaitForCallback()76 void WaitForCallback() { 77 if (was_called_) 78 return; 79 wait_for_callback_loop_.Run(); 80 } 81 82 private: ReceiverMethod(CallbackArgs...args)83 void ReceiverMethod(CallbackArgs... args) { 84 result_.emplace(std::forward<CallbackArgs>(args)...); 85 was_called_ = true; 86 wait_for_callback_loop_.Quit(); 87 } 88 89 bool was_called_ = false; 90 base::RunLoop wait_for_callback_loop_; 91 base::Optional<TupleOfNonReferenceArgs> result_; 92 93 DISALLOW_COPY_AND_ASSIGN(TestCallbackReceiver); 94 }; 95 96 template <class Value> 97 class ValueCallbackReceiver : public TestCallbackReceiver<Value> { 98 public: value()99 const Value& value() const { 100 return std::get<0>(*TestCallbackReceiver<Value>::result()); 101 } 102 }; 103 104 template <class Status, class Value> 105 class StatusAndValueCallbackReceiver 106 : public TestCallbackReceiver<Status, Value> { 107 public: status()108 const Status& status() const { 109 return std::get<0>(*TestCallbackReceiver<Status, Value>::result()); 110 } 111 value()112 const Value& value() const { 113 return std::get<1>(*TestCallbackReceiver<Status, Value>::result()); 114 } 115 }; 116 117 template <class Status, class... Values> 118 class StatusAndValuesCallbackReceiver 119 : public TestCallbackReceiver<Status, Values...> { 120 public: status()121 const Status& status() const { 122 return std::get<0>(*TestCallbackReceiver<Status, Values...>::result()); 123 } 124 125 template <size_t I> value()126 const std::tuple_element_t<I, std::tuple<Values...>>& value() const { 127 return std::get<I + 1>(*TestCallbackReceiver<Status, Values...>::result()); 128 } 129 }; 130 131 } // namespace test 132 } // namespace device 133 134 #endif // DEVICE_FIDO_TEST_CALLBACK_RECEIVER_H_ 135