1 /* 2 * Copyright 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