1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #ifndef ANDROID_HARDWARE_BROADCASTRADIO_VTS_MOCK_TIMEOUT
17 #define ANDROID_HARDWARE_BROADCASTRADIO_VTS_MOCK_TIMEOUT
18 
19 #include <gmock/gmock.h>
20 #include <thread>
21 
22 #ifndef EGMOCK_VERBOSE
23 #define EGMOCK_VERBOSE 0
24 #endif
25 
26 /**
27  * Print log message.
28  *
29  * INTERNAL IMPLEMENTATION - don't use in user code.
30  */
31 #if EGMOCK_VERBOSE
32 #define EGMOCK_LOG_(...) LOG(VERBOSE) << "egmock: " << __VA_ARGS__
33 #else
34 #define EGMOCK_LOG_(...)
35 #endif
36 
37 /**
38  * Common helper objects for gmock timeout extension.
39  *
40  * INTERNAL IMPLEMENTATION - don't use in user code.
41  */
42 #define EGMOCK_TIMEOUT_METHOD_DEF_(Method, ...) \
43     std::atomic<bool> egmock_called_##Method;   \
44     std::mutex egmock_mut_##Method;             \
45     std::condition_variable egmock_cond_##Method;
46 
47 /**
48  * Function similar to comma operator, to make it possible to return any value returned by mocked
49  * function (which may be void) and discard the result of the other operation (notification about
50  * a call).
51  *
52  * We need to invoke the mocked function (which result is returned) before the notification (which
53  * result is dropped) - that's exactly the opposite of comma operator.
54  *
55  * INTERNAL IMPLEMENTATION - don't use in user code.
56  */
57 template <typename T>
EGMockFlippedComma_(std::function<T ()> returned,std::function<void ()> discarded)58 static T EGMockFlippedComma_(std::function<T()> returned, std::function<void()> discarded) {
59     auto ret = returned();
60     discarded();
61     return ret;
62 }
63 
64 template <>
EGMockFlippedComma_(std::function<void ()> returned,std::function<void ()> discarded)65 inline void EGMockFlippedComma_(std::function<void()> returned, std::function<void()> discarded) {
66     returned();
67     discarded();
68 }
69 
70 /**
71  * Common method body for gmock timeout extension.
72  *
73  * INTERNAL IMPLEMENTATION - don't use in user code.
74  */
75 #define EGMOCK_TIMEOUT_METHOD_BODY_(Method, ...)                      \
76     auto invokeMock = [&]() { return egmock_##Method(__VA_ARGS__); }; \
77     auto notify = [&]() {                                             \
78         std::lock_guard<std::mutex> lk(egmock_mut_##Method);          \
79         EGMOCK_LOG_(#Method " called");                               \
80         egmock_called_##Method = true;                                \
81         egmock_cond_##Method.notify_all();                            \
82     };                                                                \
83     return EGMockFlippedComma_<decltype(invokeMock())>(invokeMock, notify);
84 
85 // We define this as a variadic macro in case F contains unprotected
86 // commas (the same reason that we use variadic macros in other places
87 // in this file).
88 #define EGMOCK_RESULT_(tn, ...) \
89     tn ::testing::internal::Function<__VA_ARGS__>::Result
90 
91 // The type of argument N of the given function type.
92 // INTERNAL IMPLEMENTATION - DON'T USE IN USER CODE!!!
93 #define EGMOCK_ARG_(tn, N, ...) \
94     tn ::testing::internal::Function<__VA_ARGS__>::template Arg<N-1>::type
95 
96 /**
97  * Gmock MOCK_METHOD0 timeout-capable extension.
98  */
99 #define MOCK_TIMEOUT_METHOD0(Method, ...)       \
100     MOCK_METHOD0(egmock_##Method, __VA_ARGS__); \
101     EGMOCK_TIMEOUT_METHOD_DEF_(Method);         \
102     virtual EGMOCK_RESULT_(, __VA_ARGS__) Method() { EGMOCK_TIMEOUT_METHOD_BODY_(Method); }
103 
104 /**
105  * Gmock MOCK_METHOD1 timeout-capable extension.
106  */
107 #define MOCK_TIMEOUT_METHOD1(Method, ...)                                                   \
108     MOCK_METHOD1(egmock_##Method, __VA_ARGS__);                                             \
109     EGMOCK_TIMEOUT_METHOD_DEF_(Method);                                                     \
110     virtual EGMOCK_RESULT_(, __VA_ARGS__) Method(EGMOCK_ARG_(, 1, __VA_ARGS__) egmock_a1) { \
111         EGMOCK_TIMEOUT_METHOD_BODY_(Method, egmock_a1);                                     \
112     }
113 
114 /**
115  * Gmock MOCK_METHOD2 timeout-capable extension.
116  */
117 #define MOCK_TIMEOUT_METHOD2(Method, ...)                                                          \
118     MOCK_METHOD2(egmock_##Method, __VA_ARGS__);                                                    \
119     EGMOCK_TIMEOUT_METHOD_DEF_(Method);                                                            \
120     virtual EGMOCK_RESULT_(, __VA_ARGS__)                                                          \
121         Method(EGMOCK_ARG_(, 1, __VA_ARGS__) egmock_a1, EGMOCK_ARG_(, 2, __VA_ARGS__) egmock_a2) { \
122         EGMOCK_TIMEOUT_METHOD_BODY_(Method, egmock_a1, egmock_a2);                                 \
123     }
124 
125 /**
126  * Gmock EXPECT_CALL timeout-capable extension.
127  *
128  * It has slightly different syntax from the original macro, to make method name accessible.
129  * So, instead of typing
130  *     EXPECT_CALL(account, charge(100, Currency::USD));
131  * you need to inline arguments
132  *     EXPECT_TIMEOUT_CALL(account, charge, 100, Currency::USD);
133  */
134 #define EXPECT_TIMEOUT_CALL(obj, Method, ...) \
135     EGMOCK_LOG_(#Method " expected to call"); \
136     (obj).egmock_called_##Method = false;     \
137     EXPECT_CALL(obj, egmock_##Method(__VA_ARGS__))
138 
139 /**
140  * Waits for an earlier EXPECT_TIMEOUT_CALL to execute.
141  *
142  * It does not fully support special constraints of the EXPECT_CALL clause, just proceeds when the
143  * first call to a given method comes. For example, in the following code:
144  *     EXPECT_TIMEOUT_CALL(account, charge, 100, _);
145  *     account.charge(50, Currency::USD);
146  *     EXPECT_TIMEOUT_CALL_WAIT(account, charge, 500ms);
147  * the wait clause will just continue, as the charge method was called.
148  *
149  * @param obj object for a call
150  * @param Method the method to wait for
151  * @param timeout the maximum time for waiting
152  */
153 #define EXPECT_TIMEOUT_CALL_WAIT(obj, Method, timeout)                      \
154     {                                                                       \
155         EGMOCK_LOG_("waiting for " #Method " call");                        \
156         std::unique_lock<std::mutex> lk((obj).egmock_mut_##Method);         \
157         if (!(obj).egmock_called_##Method) {                                \
158             auto status = (obj).egmock_cond_##Method.wait_for(lk, timeout); \
159             EXPECT_EQ(std::cv_status::no_timeout, status);                  \
160         }                                                                   \
161     }
162 
163 #endif  // ANDROID_HARDWARE_BROADCASTRADIO_VTS_MOCK_TIMEOUT
164