// Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/test/rectify_callback.h" #include #include #include "base/functional/bind.h" #include "base/functional/callback.h" #include "base/functional/callback_forward.h" #include "base/functional/callback_helpers.h" #include "base/test/bind.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { // Test rectifying functions with no return value. #define CALLBACK_TESTS_VOID(TestNameSuffix, Signature, LambdaToRectify, ...) \ TEST(RectifyCallbackTest, RepeatingCallbackSignatureVoid##TestNameSuffix) { \ auto cb = RectifyCallback(BindRepeating(LambdaToRectify)); \ cb.Run(__VA_ARGS__); \ } \ TEST(RectifyCallbackTest, RepeatingCallbackTypeVoid##TestNameSuffix) { \ auto cb = RectifyCallback>( \ BindRepeating(LambdaToRectify)); \ cb.Run(__VA_ARGS__); \ } \ TEST(RectifyCallbackTest, RepeatingToOnceVoid##TestNameSuffix) { \ auto cb = RectifyCallback>( \ BindRepeating(LambdaToRectify)); \ std::move(cb).Run(__VA_ARGS__); \ } \ TEST(RectifyCallbackTest, OnceCallbackSignatureVoid##TestNameSuffix) { \ auto cb = RectifyCallback(BindOnce(LambdaToRectify)); \ std::move(cb).Run(__VA_ARGS__); \ } \ TEST(RectifyCallbackTest, OnceCallbackTypeVoid##TestNameSuffix) { \ auto cb = \ RectifyCallback>(BindOnce(LambdaToRectify)); \ std::move(cb).Run(__VA_ARGS__); \ } CALLBACK_TESTS_VOID(NoArgsSameSignature, void(), []() {}) CALLBACK_TESTS_VOID( OneArgRemoveOneArg, void(int), []() {}, 1) CALLBACK_TESTS_VOID( TwoArgsRemoveTwoArgs, void(float, int), []() {}, 0.25f, 1) CALLBACK_TESTS_VOID( OneArgSameSignature, void(int), [](int x) { EXPECT_EQ(1, x); }, 1) CALLBACK_TESTS_VOID( TwoArgsRemoveOneArg, void(float, int), [](int x) { EXPECT_EQ(1, x); }, 0.25f, 1) CALLBACK_TESTS_VOID( ThreeArgsRemoveTwoArgs, void(bool, float, int), [](int x) { EXPECT_EQ(1, x); }, true, 0.25f, 1) // Test rectifying functions that do return a value. #define CALLBACK_TESTS_RETURN(TestNameSuffix, Signature, LambdaToRectify, \ ExpectedReturn, ...) \ TEST(RectifyCallbackTest, \ RepeatingCallbackSignatureReturn##TestNameSuffix) { \ auto cb = RectifyCallback(BindRepeating(LambdaToRectify)); \ EXPECT_EQ(ExpectedReturn, cb.Run(__VA_ARGS__)); \ } \ TEST(RectifyCallbackTest, RepeatingCallbackTypeReturn##TestNameSuffix) { \ auto cb = RectifyCallback>( \ BindRepeating(LambdaToRectify)); \ EXPECT_EQ(ExpectedReturn, cb.Run(__VA_ARGS__)); \ } \ TEST(RectifyCallbackTest, RepeatingToOnceReturn##TestNameSuffix) { \ auto cb = RectifyCallback>( \ BindRepeating(LambdaToRectify)); \ EXPECT_EQ(ExpectedReturn, std::move(cb).Run(__VA_ARGS__)); \ } \ TEST(RectifyCallbackTest, OnceCallbackSignatureReturn##TestNameSuffix) { \ auto cb = RectifyCallback(BindOnce(LambdaToRectify)); \ EXPECT_EQ(ExpectedReturn, std::move(cb).Run(__VA_ARGS__)); \ } \ TEST(RectifyCallbackTest, OnceCallbackTypeReturn##TestNameSuffix) { \ auto cb = \ RectifyCallback>(BindOnce(LambdaToRectify)); \ EXPECT_EQ(ExpectedReturn, std::move(cb).Run(__VA_ARGS__)); \ } CALLBACK_TESTS_RETURN( NoArgsSameSignature, int(), []() { return 2; }, 2) CALLBACK_TESTS_RETURN( OneArgRemoveOneArg, int(int), []() { return 2; }, 2, 1) CALLBACK_TESTS_RETURN( TwoArgsRemoveTwoArgs, int(float, int), []() { return 2; }, 2, 0.25f, 1) CALLBACK_TESTS_RETURN( OneArgSameSignature, int(int), [](int x) { return x; }, 2, 2) CALLBACK_TESTS_RETURN( TwoArgsRemoveOneArg, int(float, int), [](int x) { return x; }, 2, 0.25f, 2) CALLBACK_TESTS_RETURN( ThreeArgsRemoveTwoArgs, int(bool, float, int), [](int x) { return x; }, 2, true, 0.25f, 2) // Test proper forwarding of move-only arguments. CALLBACK_TESTS_RETURN( DiscardsMoveOnlyArgs, bool(int, std::unique_ptr), []() { return true; }, true, 2, std::make_unique(3)) CALLBACK_TESTS_RETURN( UsesMoveOnlyArg, int(float, int, std::unique_ptr), [](std::unique_ptr p) { return *p; }, 3, 0.25f, 2, std::make_unique(3)) // Test rectifying DoNothing(). #define CALLBACK_TESTS_DO_NOTHING(TestNameSuffix, Signature, ...) \ TEST(RectifyCallbackTest, SignatureDoNothing##TestNameSuffix) { \ auto cb = RectifyCallback(DoNothing()); \ cb.Run(__VA_ARGS__); \ } \ TEST(RectifyCallbackTest, RepeatingCallbackDoNothing##TestNameSuffix) { \ auto cb = RectifyCallback>(DoNothing()); \ cb.Run(__VA_ARGS__); \ } \ TEST(RectifyCallbackTest, OnceCallbackDoNothing##TestNameSuffix) { \ auto cb = RectifyCallback>(DoNothing()); \ std::move(cb).Run(__VA_ARGS__); \ } CALLBACK_TESTS_DO_NOTHING(NoArgs, void()) CALLBACK_TESTS_DO_NOTHING(OneArg, void(int), 2) CALLBACK_TESTS_DO_NOTHING(TwoArgs, void(float, int), 0.25, 2) CALLBACK_TESTS_DO_NOTHING(ThreeArgs, void(bool, float, int), false, 0.25, 2) // Test passing callbacks to RectifyCallback() in different ways. TEST(RectifyCallbackTest, RepeatingMove) { auto cb = BindRepeating([](int x) { return x != 0; }); auto cb2 = RectifyCallback(std::move(cb)); EXPECT_EQ(true, cb2.Run(1.0, 1)); EXPECT_EQ(false, cb2.Run(1.0, 0)); } TEST(RectifyCallbackTest, RepeatingCopy) { const auto cb = BindRepeating([](int x) { return x != 0; }); auto cb2 = RectifyCallback(cb); EXPECT_EQ(true, cb2.Run(1.0, 1)); EXPECT_EQ(false, cb2.Run(1.0, 0)); } TEST(RectifyCallbackTest, RepeatingConstReference) { const auto cb = BindRepeating([](int x) { return x != 0; }); const auto& cb_ref = cb; auto cb2 = RectifyCallback(cb_ref); EXPECT_EQ(true, cb2.Run(1.0, 1)); EXPECT_EQ(false, cb2.Run(1.0, 0)); } TEST(RectifyCallbackTest, OnceMove) { auto cb = BindOnce([](int x) { return x != 0; }); auto cb2 = RectifyCallback(std::move(cb)); EXPECT_EQ(true, std::move(cb2).Run(1.0, 1)); } TEST(RectifyCallbackTest, OnceFromRepeating) { auto cb = BindRepeating([](int x) { return x != 0; }); auto cb2 = RectifyCallback>(std::move(cb)); EXPECT_EQ(true, std::move(cb2).Run(1.0, 1)); } // Test that we can write a function that can rectify its callback argument into // the signature it wants. // // If this is implemented incorrectly, it can result in strange/bad behavior, // even if it manages to compile. namespace { using ExampleRepeatingTargetType = RepeatingCallback; using ExampleOnceTargetType = OnceCallback; // This is the call we'll be rectifying into the expected signature. bool ExampleTargetFunction(int x) { return x != 0; } // Base version of the function that takes a repeating callback. // This is the actual implementation. void ExampleFunctionRepeatingCallback(ExampleRepeatingTargetType callback) { EXPECT_EQ(true, std::move(callback).Run(1.0, 1)); } // Template version of the function that wants a repeating callback; it will // rectify its input and call the base version. // // If the rectify goes awry (e.g. the wrong kind of callback is generated), // this can result in an infinite loop. template void ExampleFunctionRepeatingCallback(T&& callback) { ExampleFunctionRepeatingCallback( RectifyCallback(std::forward(callback))); } // Base version of the function that takes a once callback. // This is the actual implementation. void ExampleFunctionOnceCallback(ExampleOnceTargetType callback) { EXPECT_EQ(true, std::move(callback).Run(1.0, 1)); } // Template version of the function that wants a once callback; it will // rectify its input and call the base version. // // If the rectify goes awry (e.g. the wrong kind of callback is generated), // this can result in an infinite loop. template void ExampleFunctionOnceCallback(T&& callback) { ExampleFunctionOnceCallback( RectifyCallback(std::forward(callback))); } } // namespace TEST(RectifyCallbackTest, TemplateOverloadRectifiesOnceCallback) { ExampleFunctionOnceCallback(BindOnce(&ExampleTargetFunction)); ExampleFunctionOnceCallback(BindOnce([]() { return true; })); ExampleFunctionOnceCallback(BindOnce([](double d, int i) { return d && i; })); auto cb = BindOnce(&ExampleTargetFunction); ExampleFunctionOnceCallback(std::move(cb)); } TEST(RectifyCallbackTest, TemplateOverloadRectifiesRepeatingCallback) { ExampleFunctionOnceCallback(BindRepeating(&ExampleTargetFunction)); ExampleFunctionOnceCallback(BindRepeating([]() { return true; })); ExampleFunctionOnceCallback( BindRepeating([](double d, int i) { return d && i; })); auto cb = BindRepeating(&ExampleTargetFunction); ExampleFunctionOnceCallback(cb); bool result = true; ExampleFunctionOnceCallback(BindLambdaForTesting([&]() { return result; })); } TEST(RectifyCallbackTest, TemplateOverloadCoerceRepeatingTarget) { ExampleFunctionRepeatingCallback(BindRepeating(&ExampleTargetFunction)); ExampleFunctionRepeatingCallback(BindRepeating([]() { return true; })); ExampleFunctionRepeatingCallback( BindRepeating([](double d, int i) { return d && i; })); auto cb = BindRepeating(&ExampleTargetFunction); ExampleFunctionRepeatingCallback(cb); bool result = true; ExampleFunctionRepeatingCallback( BindLambdaForTesting([&]() { return result; })); } TEST(RectifyCallbackTest, NullCallbackPassthrough) { { OnceCallback once; RepeatingCallback repeating; EXPECT_TRUE(RectifyCallback(std::move(once)).is_null()); EXPECT_TRUE(RectifyCallback(repeating).is_null()); EXPECT_TRUE(RectifyCallback(NullCallback()).is_null()); } { OnceCallback once; RepeatingCallback repeating; EXPECT_TRUE(RectifyCallback(std::move(once)).is_null()); EXPECT_TRUE(RectifyCallback(repeating).is_null()); EXPECT_TRUE(RectifyCallback(NullCallback()).is_null()); } } } // namespace base