// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/task/bind_post_task.h" #include "base/bind.h" #include "base/callback.h" #include "base/sequence_checker_impl.h" #include "base/task/sequenced_task_runner.h" #include "base/test/task_environment.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { namespace { void SetBool(bool* variable, bool value) { *variable = value; } void SetInt(int* variable, int value) { *variable = value; } void SetIntFromUniquePtr(int* variable, std::unique_ptr value) { *variable = *value; } int Multiply(int value) { return value * 5; } void ClearReference(OnceClosure callback) {} class SequenceRestrictionChecker { public: explicit SequenceRestrictionChecker(bool& set_on_destroy) : set_on_destroy_(set_on_destroy) {} ~SequenceRestrictionChecker() { EXPECT_TRUE(checker_.CalledOnValidSequence()); set_on_destroy_ = true; } void Run() { EXPECT_TRUE(checker_.CalledOnValidSequence()); } private: SequenceCheckerImpl checker_; bool& set_on_destroy_; }; } // namespace class BindPostTaskTest : public testing::Test { protected: test::SingleThreadTaskEnvironment task_environment_; scoped_refptr task_runner_ = SequencedTaskRunnerHandle::Get(); }; TEST_F(BindPostTaskTest, OnceClosure) { bool val = false; OnceClosure cb = BindOnce(&SetBool, &val, true); OnceClosure post_cb = BindPostTask(task_runner_, std::move(cb)); std::move(post_cb).Run(); EXPECT_FALSE(val); RunLoop().RunUntilIdle(); EXPECT_TRUE(val); } TEST_F(BindPostTaskTest, OnceCallback) { OnceCallback cb = BindOnce(&SetBool); OnceCallback post_cb = BindPostTask(task_runner_, std::move(cb)); bool val = false; std::move(post_cb).Run(&val, true); EXPECT_FALSE(val); RunLoop().RunUntilIdle(); EXPECT_TRUE(val); } TEST_F(BindPostTaskTest, OnceWithBoundMoveOnlyArg) { int val = 0; OnceClosure cb = BindOnce(&SetIntFromUniquePtr, &val, std::make_unique(10)); OnceClosure post_cb = BindPostTask(task_runner_, std::move(cb)); std::move(post_cb).Run(); EXPECT_EQ(val, 0); RunLoop().RunUntilIdle(); EXPECT_EQ(val, 10); } TEST_F(BindPostTaskTest, OnceWithUnboundMoveOnlyArg) { int val = 0; OnceCallback)> cb = BindOnce(&SetIntFromUniquePtr, &val); OnceCallback)> post_cb = BindPostTask(task_runner_, std::move(cb)); std::move(post_cb).Run(std::make_unique(10)); EXPECT_EQ(val, 0); RunLoop().RunUntilIdle(); EXPECT_EQ(val, 10); } TEST_F(BindPostTaskTest, OnceWithIgnoreResult) { OnceCallback post_cb = BindPostTask(task_runner_, BindOnce(IgnoreResult(&Multiply))); std::move(post_cb).Run(1); RunLoop().RunUntilIdle(); } TEST_F(BindPostTaskTest, OnceThen) { int value = 0; // Multiply() returns an int and SetInt() takes an int as a parameter. OnceClosure then_cb = BindOnce(&Multiply, 5) .Then(BindPostTask(task_runner_, BindOnce(&SetInt, &value))); std::move(then_cb).Run(); EXPECT_EQ(value, 0); RunLoop().RunUntilIdle(); EXPECT_EQ(value, 25); } // Ensure that the input callback is run/destroyed on the correct thread even if // the callback returned from BindPostTask() is run on a different thread. TEST_F(BindPostTaskTest, OnceRunDestroyedOnBound) { Thread target_thread("testing"); ASSERT_TRUE(target_thread.Start()); // SequenceRestrictionChecker checks it's creation, Run() and deletion all // happen on the main thread. bool destroyed = false; auto checker = std::make_unique(destroyed); // `checker` is owned by `cb` which is wrapped in `post_cb`. `post_cb` is run // on a different thread which triggers a PostTask() back to the test main // thread to invoke `cb` which runs SequenceRestrictionChecker::Run(). After // `cb` has been invoked `checker` is destroyed along with the BindState. OnceClosure cb = BindOnce(&SequenceRestrictionChecker::Run, std::move(checker)); OnceClosure post_cb = BindPostTask(task_runner_, std::move(cb)); target_thread.task_runner()->PostTask(FROM_HERE, std::move(post_cb)); target_thread.FlushForTesting(); EXPECT_FALSE(destroyed); RunLoop().RunUntilIdle(); EXPECT_TRUE(destroyed); } // Ensure that the input callback is destroyed on the correct thread even if the // callback returned from BindPostTask() is destroyed without being run on a // different thread. TEST_F(BindPostTaskTest, OnceNotRunDestroyedOnBound) { Thread target_thread("testing"); ASSERT_TRUE(target_thread.Start()); // SequenceRestrictionChecker checks it's creation and deletion all happen on // the test main thread. bool destroyed = false; auto checker = std::make_unique(destroyed); // `checker` is owned by `cb` which is wrapped in `post_cb`. `post_cb` is // deleted on a different thread which triggers a PostTask() back to the test // main thread to destroy `cb` and `checker`. OnceClosure cb = BindOnce(&SequenceRestrictionChecker::Run, std::move(checker)); OnceClosure post_cb = BindPostTask(task_runner_, std::move(cb)); target_thread.task_runner()->PostTask( FROM_HERE, BindOnce(&ClearReference, std::move(post_cb))); target_thread.FlushForTesting(); EXPECT_FALSE(destroyed); RunLoop().RunUntilIdle(); EXPECT_TRUE(destroyed); } TEST_F(BindPostTaskTest, RepeatingClosure) { bool val = false; RepeatingClosure cb = BindRepeating(&SetBool, &val, true); RepeatingClosure post_cb = BindPostTask(task_runner_, std::move(cb)); post_cb.Run(); EXPECT_FALSE(val); RunLoop().RunUntilIdle(); EXPECT_TRUE(val); val = false; post_cb.Run(); EXPECT_FALSE(val); RunLoop().RunUntilIdle(); EXPECT_TRUE(val); } TEST_F(BindPostTaskTest, RepeatingCallback) { RepeatingCallback cb = BindRepeating(&SetBool); RepeatingCallback post_cb = BindPostTask(task_runner_, std::move(cb)); bool val = false; post_cb.Run(&val, true); EXPECT_FALSE(val); RunLoop().RunUntilIdle(); EXPECT_TRUE(val); post_cb.Run(&val, false); EXPECT_TRUE(val); RunLoop().RunUntilIdle(); EXPECT_FALSE(val); } TEST_F(BindPostTaskTest, RepeatingWithUnboundMoveOnlyArg) { int val = 0; RepeatingCallback)> cb = BindRepeating(&SetIntFromUniquePtr, &val); RepeatingCallback)> post_cb = BindPostTask(task_runner_, std::move(cb)); post_cb.Run(std::make_unique(10)); EXPECT_EQ(val, 0); RunLoop().RunUntilIdle(); EXPECT_EQ(val, 10); post_cb.Run(std::make_unique(20)); EXPECT_EQ(val, 10); RunLoop().RunUntilIdle(); EXPECT_EQ(val, 20); } TEST_F(BindPostTaskTest, RepeatingWithIgnoreResult) { RepeatingCallback post_cb = BindPostTask(task_runner_, BindRepeating(IgnoreResult(&Multiply))); std::move(post_cb).Run(1); RunLoop().RunUntilIdle(); } TEST_F(BindPostTaskTest, RepeatingThen) { int value = 0; // Multiply() returns an int and SetInt() takes an int as a parameter. RepeatingCallback then_cb = BindRepeating(&Multiply).Then( BindPostTask(task_runner_, BindRepeating(&SetInt, &value))); then_cb.Run(5); EXPECT_EQ(value, 0); RunLoop().RunUntilIdle(); EXPECT_EQ(value, 25); then_cb.Run(10); EXPECT_EQ(value, 25); RunLoop().RunUntilIdle(); EXPECT_EQ(value, 50); } // Ensure that the input callback is run/destroyed on the correct thread even if // the callback returned from BindPostTask() is run on a different thread. TEST_F(BindPostTaskTest, RepeatingRunDestroyedOnBound) { Thread target_thread("testing"); ASSERT_TRUE(target_thread.Start()); // SequenceRestrictionChecker checks it's creation, Run() and deletion all // happen on the main thread. bool destroyed = false; auto checker = std::make_unique(destroyed); // `checker` is owned by `cb` which is wrapped in `post_cb`. `post_cb` is run // on a different thread which triggers a PostTask() back to the test main // thread to invoke `cb` which runs SequenceRestrictionChecker::Run(). After // `cb` has been invoked `checker` is destroyed along with the BindState. RepeatingClosure cb = BindRepeating(&SequenceRestrictionChecker::Run, std::move(checker)); RepeatingClosure post_cb = BindPostTask(task_runner_, std::move(cb)); target_thread.task_runner()->PostTask(FROM_HERE, std::move(post_cb)); target_thread.FlushForTesting(); EXPECT_FALSE(destroyed); RunLoop().RunUntilIdle(); EXPECT_TRUE(destroyed); } // Ensure that the input callback is destroyed on the correct thread even if the // callback returned from BindPostTask() is destroyed without being run on a // different thread. TEST_F(BindPostTaskTest, RepeatingNotRunDestroyedOnBound) { Thread target_thread("testing"); ASSERT_TRUE(target_thread.Start()); // SequenceRestrictionChecker checks it's creation and deletion all happen on // the test main thread. bool destroyed = false; auto checker = std::make_unique(destroyed); // `checker` is owned by `cb` which is wrapped in `post_cb`. `post_cb` is // deleted on a different thread which triggers a PostTask() back to the test // main thread to destroy `cb` and `checker`. RepeatingClosure cb = BindRepeating(&SequenceRestrictionChecker::Run, std::move(checker)); RepeatingClosure post_cb = BindPostTask(task_runner_, std::move(cb)); target_thread.task_runner()->PostTask( FROM_HERE, BindRepeating(&ClearReference, std::move(post_cb))); target_thread.FlushForTesting(); EXPECT_FALSE(destroyed); RunLoop().RunUntilIdle(); EXPECT_TRUE(destroyed); } } // namespace base