| // Copyright 2020 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/task/bind_post_task.h" | 
 |  | 
 | #include "base/functional/bind.h" | 
 | #include "base/functional/callback.h" | 
 | #include "base/memory/raw_ref.h" | 
 | #include "base/sequence_checker_impl.h" | 
 | #include "base/task/sequenced_task_runner.h" | 
 | #include "base/test/task_environment.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<int> 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_; | 
 |   const raw_ref<bool> set_on_destroy_; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | class BindPostTaskTest : public testing::Test { | 
 |  protected: | 
 |   test::SingleThreadTaskEnvironment task_environment_; | 
 |   scoped_refptr<SequencedTaskRunner> task_runner_ = | 
 |       SequencedTaskRunner::GetCurrentDefault(); | 
 | }; | 
 |  | 
 | 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<void(bool*, bool)> cb = BindOnce(&SetBool); | 
 |   OnceCallback<void(bool*, bool)> 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<int>(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<void(std::unique_ptr<int>)> cb = | 
 |       BindOnce(&SetIntFromUniquePtr, &val); | 
 |   OnceCallback<void(std::unique_ptr<int>)> post_cb = | 
 |       BindPostTask(task_runner_, std::move(cb)); | 
 |  | 
 |   std::move(post_cb).Run(std::make_unique<int>(10)); | 
 |   EXPECT_EQ(val, 0); | 
 |  | 
 |   RunLoop().RunUntilIdle(); | 
 |   EXPECT_EQ(val, 10); | 
 | } | 
 |  | 
 | TEST_F(BindPostTaskTest, OnceWithIgnoreResult) { | 
 |   OnceCallback<void(int)> 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<SequenceRestrictionChecker>(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<SequenceRestrictionChecker>(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<void(bool*, bool)> cb = BindRepeating(&SetBool); | 
 |   RepeatingCallback<void(bool*, bool)> 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<void(std::unique_ptr<int>)> cb = | 
 |       BindRepeating(&SetIntFromUniquePtr, &val); | 
 |   RepeatingCallback<void(std::unique_ptr<int>)> post_cb = | 
 |       BindPostTask(task_runner_, std::move(cb)); | 
 |  | 
 |   post_cb.Run(std::make_unique<int>(10)); | 
 |   EXPECT_EQ(val, 0); | 
 |  | 
 |   RunLoop().RunUntilIdle(); | 
 |   EXPECT_EQ(val, 10); | 
 |  | 
 |   post_cb.Run(std::make_unique<int>(20)); | 
 |   EXPECT_EQ(val, 10); | 
 |  | 
 |   RunLoop().RunUntilIdle(); | 
 |   EXPECT_EQ(val, 20); | 
 | } | 
 |  | 
 | TEST_F(BindPostTaskTest, RepeatingWithIgnoreResult) { | 
 |   RepeatingCallback<void(int)> 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<void(int)> 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<SequenceRestrictionChecker>(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<SequenceRestrictionChecker>(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 |