// 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 #include #include #include "base/check.h" #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 decltype(auto) get(Args&&... args) { return std::get(std::forward_as_tuple(std::forward(args)...)); } // Wraps `tuple` inside RefCountedData> to allow creating // shallow copies in lambda return of RunOnceCallback<>. // Since RefCountedData stores Tuple directly, the indirection via // std::unique_ptr is necessary to be able to CHECK() on second // invocation instead of running the callback with a default-constructed tuple. template auto WrapTupleAsRefCountedData(Tuple&& tuple) { return MakeRefCounted>>( std::make_unique(std::forward(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. template decltype(auto) RunImpl(Callback&& cb, Tuple&& tuple) { return std::apply( [&](auto&&... args) -> decltype(auto) { return std::forward(cb).Run( std::forward(args)...); }, std::forward(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(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 = RefCountedData; scoped_refptr copyable_cb = MakeRefCounted(std::move(cb)); return [copyable_cb](auto&&...) { CHECK(copyable_cb->data); std::move(copyable_cb->data).Run(); }; } // The Run[Once]Closure() action invokes the Run() method on the N-th // (0-based) argument of the mock function. template auto RunClosure() { return [](auto&&... args) -> decltype(auto) { return gmock_callback_support_internal::get(args...).Run(); }; } template auto RunOnceClosure() { return [](auto&&... args) -> decltype(auto) { return std::move(gmock_callback_support_internal::get(args...)).Run(); }; } // The Run[Once]Callback(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. There are two separate APIs for interacting with OnceCallback<> -- // RunOnceCallback<> and RunOnceCallbackRepeatedly<>. In the former, arguments // are copies during each run; in the latter, they are passed by move. // Note that RunOnceCallback<> cannot be used with WillRepeatedly() since its // arguments are moved out upon first invocation -- the code doing so will // crash with a CHECK(). // Using move-only arguments with `RunCallback()` is not supported. template auto RunOnceCallback(RunArgs&&... run_args) { // Mock actions have to be copyable. However, since this action is only // supposed to be invoked once and might contain move-only arguments, the arg // tuple is explicitly wrapped as RefCountedData<> to allow shallow copies. return [tuple_ptr = gmock_callback_support_internal::WrapTupleAsRefCountedData( std::make_tuple(std::forward(run_args)...))]( auto&&... args) -> decltype(auto) { CHECK(tuple_ptr->data) << "A RunOnceCallback() action must be called at most once. " "Use RunOnceCallbackRepeatedly() for invoking a " "OnceCallback<> more than once."; auto data = std::exchange(tuple_ptr->data, nullptr); return gmock_callback_support_internal::RunImpl( std::move(gmock_callback_support_internal::get(args...)), std::move(*data)); }; } template auto RunOnceCallbackRepeatedly(RunArgs&&... run_args) { return [tuple = std::make_tuple(std::forward(run_args)...)]( auto&&... args) -> decltype(auto) { return gmock_callback_support_internal::RunImpl( std::move(gmock_callback_support_internal::get(args...)), tuple); }; } template auto RunCallback(RunArgs&&... run_args) { return [tuple = std::make_tuple(std::forward(run_args)...)]( auto&&... args) -> decltype(auto) { return gmock_callback_support_internal::RunImpl( gmock_callback_support_internal::get(args...), tuple); }; } } // namespace test } // namespace base #endif // BASE_TEST_GMOCK_CALLBACK_SUPPORT_H_