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