blob: 2245ee1a8a147085a624da505820adf6e75f4372 [file] [log] [blame]
* Copyright (C) 2018 The Android Open Source Project
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* See the License for the specific language governing permissions and
* limitations under the License.
#pragma once
#include <chrono>
#include <deque>
#include <mutex>
#include <optional>
#include <thread>
#include <tuple>
#include <type_traits>
#include <utility>
#include <android-base/thread_annotations.h>
namespace android {
// This class helps record calls made by another thread when they are made
// asynchronously, with no other way for the tests to verify that the calls have
// been made.
// A normal Google Mock recorder, while thread safe, does not allow you to wait
// for asynchronous calls to be made.
// Usage:
// In the test, use a Google Mock expectation to invoke an instance of the
// recorder:
// AsyncCallRecorder<void(int)> recorder;
// EXPECT_CALL(someMock, someFunction(_)).
// .WillRepeatedly(Invoke(recorder.getInvocable()));
// Then you can invoke the functionality being tested:
// threadUnderTest.doSomethingAsync()
// And afterwards make a number of assertions using the recorder:
// // Wait for one call (with reasonable default timeout), and get the args
// // as a std::tuple inside a std::optional.
// auto args = recorder.waitForCall();
// // The returned std::optional will have a value if the recorder function
// // was called.
// ASSERT_TRUE(args.has_value());
// // The arguments can be checked if needed using standard tuple
// // operations.
// EXPECT_EQ(123, std::get<0>(args.value()));
// Alternatively maybe you want to assert that a call was not made.
// EXPECT_FALSE(recorder.waitForUnexpectedCall().has_value());
// However this check uses a really short timeout so as not to block the test
// unnecessarily. And it could be possible for the check to return false and
// then the recorder could observe a call being made after.
template <typename Func>
class AsyncCallRecorder;
template <typename... Args>
class AsyncCallRecorder<void (*)(Args...)> {
// For the tests, we expect the wait for an expected change to be signaled
// to be much shorter than this.
static constexpr std::chrono::milliseconds DEFAULT_CALL_EXPECTED_TIMEOUT{10};
// The wait here is tricky. We don't expect a change, but we don't want to
// wait forever (or for longer than the typical test function runtime). As
// even the simplest Google Test can take 1ms (1000us) to run, we wait for
// half that time.
static constexpr std::chrono::microseconds UNEXPECTED_CALL_TIMEOUT{500};
using ArgTuple = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>;
void recordCall(Args... args) {
std::lock_guard<std::mutex> lock(mMutex);
// Returns a functor which can be used with the Google Mock Invoke()
// function, or as a std::function to record calls.
auto getInvocable() {
return [this](Args... args) { recordCall(args...); };
// Returns a set of arguments as a std::optional<std::tuple<...>> for the
// oldest call, waiting for the given timeout if necessary if there are no
// arguments in the FIFO.
std::optional<ArgTuple> waitForCall(
std::chrono::microseconds timeout = DEFAULT_CALL_EXPECTED_TIMEOUT)
std::unique_lock<std::mutex> lock(mMutex);
// Wait if necessary for us to have a record from a call.
mCondition.wait_for(lock, timeout,
[this]() NO_THREAD_SAFETY_ANALYSIS { return !mCalls.empty(); });
// Return the arguments from the oldest call, if one was made
bool called = !mCalls.empty();
std::optional<ArgTuple> result;
if (called) {
return result;
// Waits using a small default timeout for when a call is not expected to be
// made. The returned std::optional<std:tuple<...>> should not have a value
// except if a set of arguments was unexpectedly received because a call was
// actually made.
// Note this function uses a small timeout to not block test execution, and
// it is possible the code under test could make the call AFTER the timeout
// expires.
std::optional<ArgTuple> waitForUnexpectedCall() { return waitForCall(UNEXPECTED_CALL_TIMEOUT); }
std::mutex mMutex;
std::condition_variable mCondition;
std::deque<ArgTuple> mCalls GUARDED_BY(mMutex);
// Like AsyncCallRecorder, but for when the function being invoked
// asynchronously is expected to return a value.
// This helper allows a single constant return value to be set to be returned by
// all calls that were made.
template <typename Func>
class AsyncCallRecorderWithCannedReturn;
template <typename Ret, typename... Args>
class AsyncCallRecorderWithCannedReturn<Ret (*)(Args...)>
: public AsyncCallRecorder<void (*)(Args...)> {
explicit AsyncCallRecorderWithCannedReturn(Ret returnvalue) : mReturnValue(returnvalue) {}
auto getInvocable() {
return [this](Args... args) {
return mReturnValue;
const Ret mReturnValue;
} // namespace android