199 lines
7.5 KiB
C++
199 lines
7.5 KiB
C++
// Copyright 2012 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#ifndef BASE_TEST_GMOCK_CALLBACK_SUPPORT_H_
|
|
#define BASE_TEST_GMOCK_CALLBACK_SUPPORT_H_
|
|
|
|
#include <functional>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include "base/functional/callback.h"
|
|
#include "base/memory/ref_counted.h"
|
|
#include "base/memory/scoped_refptr.h"
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
|
|
|
namespace base {
|
|
|
|
namespace gmock_callback_support_internal {
|
|
|
|
// Small helper to get the `I`th argument.
|
|
template <size_t I, typename... Args>
|
|
decltype(auto) get(Args&&... args) {
|
|
return std::get<I>(std::forward_as_tuple(std::forward<Args>(args)...));
|
|
}
|
|
|
|
// Invokes `cb` with the arguments stored in `tuple`. Both `cb` and `tuple` are
|
|
// perfectly forwarded, allowing callers to specify whether they should be
|
|
// passed by move or copy.
|
|
template <typename Callback, typename Tuple, size_t... Is>
|
|
decltype(auto) RunImpl(Callback&& cb,
|
|
Tuple&& tuple,
|
|
std::index_sequence<Is...>) {
|
|
return std::forward<Callback>(cb).Run(
|
|
std::get<Is>(std::forward<Tuple>(tuple))...);
|
|
}
|
|
|
|
// Invokes `cb` with the arguments stored in `tuple`. Both `cb` and `tuple` are
|
|
// perfectly forwarded, allowing callers to specify whether they should be
|
|
// passed by move or copy. Needs to dispatch to the three arguments version to
|
|
// be able to construct a `std::index_sequence` of the corresponding size.
|
|
template <typename Callback, typename Tuple>
|
|
decltype(auto) RunImpl(Callback&& cb, Tuple&& tuple) {
|
|
return RunImpl(std::forward<Callback>(cb), std::forward<Tuple>(tuple),
|
|
std::make_index_sequence<
|
|
std::tuple_size<std::remove_reference_t<Tuple>>::value>());
|
|
}
|
|
|
|
// Invoked when the arguments to a OnceCallback are copy constructible. In this
|
|
// case the returned lambda will pass the arguments to the provided callback by
|
|
// copy, allowing it to be used multiple times.
|
|
template <size_t I,
|
|
typename Tuple,
|
|
std::enable_if_t<std::is_copy_constructible<Tuple>::value, int> = 0>
|
|
auto RunOnceCallbackImpl(Tuple&& tuple) {
|
|
return
|
|
[tuple = std::forward<Tuple>(tuple)](auto&&... args) -> decltype(auto) {
|
|
return RunImpl(
|
|
std::move(::base::gmock_callback_support_internal::get<I>(args...)),
|
|
tuple);
|
|
};
|
|
}
|
|
|
|
// Invoked when the arguments to a OnceCallback are not copy constructible. In
|
|
// this case the returned lambda will pass the arguments to the provided
|
|
// callback by move, allowing it to be only used once.
|
|
template <size_t I,
|
|
typename Tuple,
|
|
std::enable_if_t<!std::is_copy_constructible<Tuple>::value, int> = 0>
|
|
auto RunOnceCallbackImpl(Tuple&& tuple) {
|
|
// Mock actions need to be copyable, but `tuple` is not. Wrap it in in a
|
|
// `scoped_refptr` to allow it to be copied.
|
|
auto tuple_ptr = base::MakeRefCounted<base::RefCountedData<Tuple>>(
|
|
std::forward<Tuple>(tuple));
|
|
return [tuple_ptr =
|
|
std::move(tuple_ptr)](auto&&... args) mutable -> decltype(auto) {
|
|
// Since running the action will move out of the arguments, `tuple_ptr` is
|
|
// nulled out, so that attempting to run it twice will result in a run-time
|
|
// crash.
|
|
return RunImpl(
|
|
std::move(::base::gmock_callback_support_internal::get<I>(args...)),
|
|
std::move(std::exchange(tuple_ptr, nullptr)->data));
|
|
};
|
|
}
|
|
|
|
// Invoked for RepeatingCallbacks. In this case the returned lambda will pass
|
|
// the arguments to the provided callback by copy, allowing it to be used
|
|
// multiple times. Move-only arguments are not supported.
|
|
template <size_t I, typename Tuple>
|
|
auto RunRepeatingCallbackImpl(Tuple&& tuple) {
|
|
return
|
|
[tuple = std::forward<Tuple>(tuple)](auto&&... args) -> decltype(auto) {
|
|
return RunImpl(::base::gmock_callback_support_internal::get<I>(args...),
|
|
tuple);
|
|
};
|
|
}
|
|
|
|
} // namespace gmock_callback_support_internal
|
|
|
|
namespace test {
|
|
|
|
// Matchers for base::{Once,Repeating}Callback and
|
|
// base::{Once,Repeating}Closure.
|
|
MATCHER(IsNullCallback, "a null callback") {
|
|
return (arg.is_null());
|
|
}
|
|
|
|
MATCHER(IsNotNullCallback, "a non-null callback") {
|
|
return (!arg.is_null());
|
|
}
|
|
|
|
// The Run[Once]Closure() action invokes the Run() method on the closure
|
|
// provided when the action is constructed. Function arguments passed when the
|
|
// action is run will be ignored.
|
|
ACTION_P(RunClosure, closure) {
|
|
closure.Run();
|
|
}
|
|
|
|
// This action can be invoked at most once. Any further invocation will trigger
|
|
// a CHECK failure.
|
|
inline auto RunOnceClosure(base::OnceClosure cb) {
|
|
// Mock actions need to be copyable, but OnceClosure is not. Wrap the closure
|
|
// in a base::RefCountedData<> to allow it to be copied.
|
|
using RefCountedOnceClosure = base::RefCountedData<base::OnceClosure>;
|
|
scoped_refptr<RefCountedOnceClosure> copyable_cb =
|
|
base::MakeRefCounted<RefCountedOnceClosure>(std::move(cb));
|
|
return [copyable_cb](auto&&...) {
|
|
CHECK(copyable_cb->data);
|
|
std::move(copyable_cb->data).Run();
|
|
};
|
|
}
|
|
|
|
// The Run[Once]Closure<N>() action invokes the Run() method on the N-th
|
|
// (0-based) argument of the mock function.
|
|
template <size_t I>
|
|
auto RunClosure() {
|
|
return [](auto&&... args) -> decltype(auto) {
|
|
return ::base::gmock_callback_support_internal::get<I>(args...).Run();
|
|
};
|
|
}
|
|
|
|
template <size_t I>
|
|
auto RunOnceClosure() {
|
|
return [](auto&&... args) -> decltype(auto) {
|
|
return std::move(::base::gmock_callback_support_internal::get<I>(args...))
|
|
.Run();
|
|
};
|
|
}
|
|
|
|
// The Run[Once]Callback<N>(p1, p2, ..., p_k) action invokes the Run() method on
|
|
// the N-th (0-based) argument of the mock function, with arguments p1, p2, ...,
|
|
// p_k.
|
|
//
|
|
// Notes:
|
|
//
|
|
// 1. The arguments are passed by value by default. If you need to
|
|
// pass an argument by reference, wrap it inside ByRef(). For example,
|
|
//
|
|
// RunCallback<1>(5, string("Hello"), ByRef(foo))
|
|
//
|
|
// passes 5 and string("Hello") by value, and passes foo by reference.
|
|
//
|
|
// 2. If the callback takes an argument by reference but ByRef() is
|
|
// not used, it will receive the reference to a copy of the value,
|
|
// instead of the original value. For example, when the 0-th
|
|
// argument of the callback takes a const string&, the action
|
|
//
|
|
// RunCallback<0>(string("Hello"))
|
|
//
|
|
// makes a copy of the temporary string("Hello") object and passes a
|
|
// reference of the copy, instead of the original temporary object,
|
|
// to the callback. This makes it easy for a user to define an
|
|
// RunCallback action from temporary values and have it performed later.
|
|
//
|
|
// 3. In order to facilitate re-use of the `RunOnceCallback()` action,
|
|
// the arguments are copied during each run if possible. If this can't
|
|
// be done (e.g. one of the arguments is move-only), the arguments will
|
|
// be passed by move. However, since moving potentially invalidates the
|
|
// arguments, the resulting action is only allowed to run once in this
|
|
// case. Attempting to run it twice will result in a runtime crash.
|
|
// Using move-only arguments with `RunCallback()` is not supported.
|
|
template <size_t I, typename... RunArgs>
|
|
auto RunOnceCallback(RunArgs&&... run_args) {
|
|
return ::base::gmock_callback_support_internal::RunOnceCallbackImpl<I>(
|
|
std::make_tuple(std::forward<RunArgs>(run_args)...));
|
|
}
|
|
|
|
template <size_t I, typename... RunArgs>
|
|
auto RunCallback(RunArgs&&... run_args) {
|
|
return ::base::gmock_callback_support_internal::RunRepeatingCallbackImpl<I>(
|
|
std::make_tuple(std::forward<RunArgs>(run_args)...));
|
|
}
|
|
|
|
} // namespace test
|
|
} // namespace base
|
|
|
|
#endif // BASE_TEST_GMOCK_CALLBACK_SUPPORT_H_
|