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