1 /*
2  * Copyright (C) 2018 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 
17 #pragma once
18 
19 #include <chrono>
20 #include <deque>
21 #include <mutex>
22 #include <optional>
23 #include <thread>
24 #include <tuple>
25 #include <type_traits>
26 #include <utility>
27 
28 #include <android-base/thread_annotations.h>
29 
30 namespace android {
31 
32 // This class helps record calls made by another thread when they are made
33 // asynchronously, with no other way for the tests to verify that the calls have
34 // been made.
35 //
36 // A normal Google Mock recorder, while thread safe, does not allow you to wait
37 // for asynchronous calls to be made.
38 //
39 // Usage:
40 //
41 // In the test, use a Google Mock expectation to invoke an instance of the
42 // recorder:
43 //
44 //     AsyncCallRecorder<void(int)> recorder;
45 //
46 //     EXPECT_CALL(someMock, someFunction(_)).
47 //             .WillRepeatedly(Invoke(recorder.getInvocable()));
48 //
49 // Then you can invoke the functionality being tested:
50 //
51 //     threadUnderTest.doSomethingAsync()
52 //
53 // And afterwards make a number of assertions using the recorder:
54 //
55 //     // Wait for one call (with reasonable default timeout), and get the args
56 //     // as a std::tuple inside a std::optional.
57 //     auto args = recorder.waitForCall();
58 //     // The returned std::optional will have a value if the recorder function
59 //     // was called.
60 //     ASSERT_TRUE(args.has_value());
61 //     // The arguments can be checked if needed using standard tuple
62 //     // operations.
63 //     EXPECT_EQ(123, std::get<0>(args.value()));
64 //
65 // Alternatively maybe you want to assert that a call was not made.
66 //
67 //     EXPECT_FALSE(recorder.waitForUnexpectedCall().has_value());
68 //
69 // However this check uses a really short timeout so as not to block the test
70 // unnecessarily. And it could be possible for the check to return false and
71 // then the recorder could observe a call being made after.
72 template <typename Func>
73 class AsyncCallRecorder;
74 
75 template <typename... Args>
76 class AsyncCallRecorder<void (*)(Args...)> {
77 public:
78     // For the tests, we expect the wait for an expected change to be signaled
79     // to be much shorter than this.
80     static constexpr std::chrono::milliseconds DEFAULT_CALL_EXPECTED_TIMEOUT{10};
81 
82     // The wait here is tricky. We don't expect a change, but we don't want to
83     // wait forever (or for longer than the typical test function runtime). As
84     // even the simplest Google Test can take 1ms (1000us) to run, we wait for
85     // half that time.
86     static constexpr std::chrono::microseconds UNEXPECTED_CALL_TIMEOUT{500};
87 
88     using ArgTuple = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>;
89 
recordCall(Args...args)90     void recordCall(Args... args) {
91         std::lock_guard<std::mutex> lock(mMutex);
92         mCalls.emplace_back(std::make_tuple(args...));
93         mCondition.notify_all();
94     }
95 
96     // Returns a functor which can be used with the Google Mock Invoke()
97     // function, or as a std::function to record calls.
getInvocable()98     auto getInvocable() {
99         return [this](Args... args) { recordCall(args...); };
100     }
101 
102     // Returns a set of arguments as a std::optional<std::tuple<...>> for the
103     // oldest call, waiting for the given timeout if necessary if there are no
104     // arguments in the FIFO.
105     std::optional<ArgTuple> waitForCall(
106             std::chrono::microseconds timeout = DEFAULT_CALL_EXPECTED_TIMEOUT)
107             NO_THREAD_SAFETY_ANALYSIS {
108         std::unique_lock<std::mutex> lock(mMutex);
109 
110         // Wait if necessary for us to have a record from a call.
111         mCondition.wait_for(lock, timeout,
112                             [this]() NO_THREAD_SAFETY_ANALYSIS { return !mCalls.empty(); });
113 
114         // Return the arguments from the oldest call, if one was made
115         bool called = !mCalls.empty();
116         std::optional<ArgTuple> result;
117         if (called) {
118             result.emplace(std::move(mCalls.front()));
119             mCalls.pop_front();
120         }
121         return result;
122     }
123 
124     // Waits using a small default timeout for when a call is not expected to be
125     // made. The returned std::optional<std:tuple<...>> should not have a value
126     // except if a set of arguments was unexpectedly received because a call was
127     // actually made.
128     //
129     // Note this function uses a small timeout to not block test execution, and
130     // it is possible the code under test could make the call AFTER the timeout
131     // expires.
waitForUnexpectedCall()132     std::optional<ArgTuple> waitForUnexpectedCall() { return waitForCall(UNEXPECTED_CALL_TIMEOUT); }
133 
134 private:
135     std::mutex mMutex;
136     std::condition_variable mCondition;
137     std::deque<ArgTuple> mCalls GUARDED_BY(mMutex);
138 };
139 
140 // Like AsyncCallRecorder, but for when the function being invoked
141 // asynchronously is expected to return a value.
142 //
143 // This helper allows a single constant return value to be set to be returned by
144 // all calls that were made.
145 template <typename Func>
146 class AsyncCallRecorderWithCannedReturn;
147 
148 template <typename Ret, typename... Args>
149 class AsyncCallRecorderWithCannedReturn<Ret (*)(Args...)>
150       : public AsyncCallRecorder<void (*)(Args...)> {
151 public:
AsyncCallRecorderWithCannedReturn(Ret returnvalue)152     explicit AsyncCallRecorderWithCannedReturn(Ret returnvalue) : mReturnValue(returnvalue) {}
153 
getInvocable()154     auto getInvocable() {
155         return [this](Args... args) {
156             this->recordCall(args...);
157             return mReturnValue;
158         };
159     }
160 
161 private:
162     const Ret mReturnValue;
163 };
164 
165 } // namespace android
166