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