• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     // This wait value needs to be large enough to avoid flakes caused by delays
79     // scheduling threads, but small enough that tests don't take forever if
80     // something really is wrong. Based on some empirical evidence, 100ms should
81     // be enough to avoid the former.
82     static constexpr std::chrono::milliseconds DEFAULT_CALL_EXPECTED_TIMEOUT{100};
83 
84     // The wait here is tricky. It's for when We don't expect to record a call,
85     // but we don't want to wait forever (or for longer than the typical test
86     // function runtime). As even the simplest Google Test can take 1ms (1000us)
87     // to run, we wait for half that time.
88     static constexpr std::chrono::microseconds UNEXPECTED_CALL_TIMEOUT{500};
89 
90     using ArgTuple = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>;
91 
recordCall(Args...args)92     void recordCall(Args... args) {
93         std::lock_guard<std::mutex> lock(mMutex);
94         mCalls.emplace_back(std::make_tuple(args...));
95         mCondition.notify_all();
96     }
97 
98     // Returns a functor which can be used with the Google Mock Invoke()
99     // function, or as a std::function to record calls.
getInvocable()100     auto getInvocable() {
101         return [this](Args... args) { recordCall(args...); };
102     }
103 
104     // Returns a set of arguments as a std::optional<std::tuple<...>> for the
105     // oldest call, waiting for the given timeout if necessary if there are no
106     // arguments in the FIFO.
107     std::optional<ArgTuple> waitForCall(
108             std::chrono::microseconds timeout = DEFAULT_CALL_EXPECTED_TIMEOUT)
109             NO_THREAD_SAFETY_ANALYSIS {
110         std::unique_lock<std::mutex> lock(mMutex);
111 
112         // Wait if necessary for us to have a record from a call.
113         mCondition.wait_for(lock, timeout,
114                             [this]() NO_THREAD_SAFETY_ANALYSIS { return !mCalls.empty(); });
115 
116         // Return the arguments from the oldest call, if one was made
117         bool called = !mCalls.empty();
118         std::optional<ArgTuple> result;
119         if (called) {
120             result.emplace(std::move(mCalls.front()));
121             mCalls.pop_front();
122         }
123         return result;
124     }
125 
126     // Waits using a small default timeout for when a call is not expected to be
127     // made. The returned std::optional<std:tuple<...>> should not have a value
128     // except if a set of arguments was unexpectedly received because a call was
129     // actually made.
130     //
131     // Note this function uses a small timeout to not block test execution, and
132     // it is possible the code under test could make the call AFTER the timeout
133     // expires.
waitForUnexpectedCall()134     std::optional<ArgTuple> waitForUnexpectedCall() { return waitForCall(UNEXPECTED_CALL_TIMEOUT); }
135 
136 private:
137     std::mutex mMutex;
138     std::condition_variable mCondition;
139     std::deque<ArgTuple> mCalls GUARDED_BY(mMutex);
140 };
141 
142 // Like AsyncCallRecorder, but for when the function being invoked
143 // asynchronously is expected to return a value.
144 //
145 // This helper allows a single constant return value to be set to be returned by
146 // all calls that were made.
147 template <typename Func>
148 class AsyncCallRecorderWithCannedReturn;
149 
150 template <typename Ret, typename... Args>
151 class AsyncCallRecorderWithCannedReturn<Ret (*)(Args...)>
152       : public AsyncCallRecorder<void (*)(Args...)> {
153 public:
AsyncCallRecorderWithCannedReturn(Ret returnvalue)154     explicit AsyncCallRecorderWithCannedReturn(Ret returnvalue) : mReturnValue(returnvalue) {}
155 
getInvocable()156     auto getInvocable() {
157         return [this](Args... args) {
158             this->recordCall(args...);
159             return mReturnValue;
160         };
161     }
162 
163 private:
164     const Ret mReturnValue;
165 };
166 
167 } // namespace android
168