|  | // Copyright 2016 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/threading/post_task_and_reply_impl.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/auto_reset.h" | 
|  | #include "base/bind.h" | 
|  | #include "base/bind_helpers.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/memory/ref_counted.h" | 
|  | #include "base/test/test_mock_time_task_runner.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | using ::testing::_; | 
|  |  | 
|  | namespace base { | 
|  | namespace internal { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class PostTaskAndReplyTaskRunner : public internal::PostTaskAndReplyImpl { | 
|  | public: | 
|  | explicit PostTaskAndReplyTaskRunner(TaskRunner* destination) | 
|  | : destination_(destination) {} | 
|  |  | 
|  | private: | 
|  | bool PostTask(const Location& from_here, OnceClosure task) override { | 
|  | return destination_->PostTask(from_here, std::move(task)); | 
|  | } | 
|  |  | 
|  | // Non-owning. | 
|  | TaskRunner* const destination_; | 
|  | }; | 
|  |  | 
|  | class ObjectToDelete : public RefCounted<ObjectToDelete> { | 
|  | public: | 
|  | // |delete_flag| is set to true when this object is deleted | 
|  | ObjectToDelete(bool* delete_flag) : delete_flag_(delete_flag) { | 
|  | EXPECT_FALSE(*delete_flag_); | 
|  | } | 
|  |  | 
|  | private: | 
|  | friend class RefCounted<ObjectToDelete>; | 
|  | ~ObjectToDelete() { *delete_flag_ = true; } | 
|  |  | 
|  | bool* const delete_flag_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(ObjectToDelete); | 
|  | }; | 
|  |  | 
|  | class MockObject { | 
|  | public: | 
|  | MockObject() = default; | 
|  |  | 
|  | MOCK_METHOD1(Task, void(scoped_refptr<ObjectToDelete>)); | 
|  | MOCK_METHOD1(Reply, void(scoped_refptr<ObjectToDelete>)); | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(MockObject); | 
|  | }; | 
|  |  | 
|  | class MockRunsTasksInCurrentSequenceTaskRunner : public TestMockTimeTaskRunner { | 
|  | public: | 
|  | MockRunsTasksInCurrentSequenceTaskRunner( | 
|  | TestMockTimeTaskRunner::Type type = | 
|  | TestMockTimeTaskRunner::Type::kStandalone) | 
|  | : TestMockTimeTaskRunner(type) {} | 
|  |  | 
|  | void RunUntilIdleWithRunsTasksInCurrentSequence() { | 
|  | AutoReset<bool> reset(&runs_tasks_in_current_sequence_, true); | 
|  | RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | void ClearPendingTasksWithRunsTasksInCurrentSequence() { | 
|  | AutoReset<bool> reset(&runs_tasks_in_current_sequence_, true); | 
|  | ClearPendingTasks(); | 
|  | } | 
|  |  | 
|  | // TestMockTimeTaskRunner: | 
|  | bool RunsTasksInCurrentSequence() const override { | 
|  | return runs_tasks_in_current_sequence_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | ~MockRunsTasksInCurrentSequenceTaskRunner() override = default; | 
|  |  | 
|  | bool runs_tasks_in_current_sequence_ = false; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(MockRunsTasksInCurrentSequenceTaskRunner); | 
|  | }; | 
|  |  | 
|  | class PostTaskAndReplyImplTest : public testing::Test { | 
|  | protected: | 
|  | PostTaskAndReplyImplTest() = default; | 
|  |  | 
|  | void PostTaskAndReplyToMockObject() { | 
|  | // Expect the post to succeed. | 
|  | EXPECT_TRUE( | 
|  | PostTaskAndReplyTaskRunner(post_runner_.get()) | 
|  | .PostTaskAndReply( | 
|  | FROM_HERE, | 
|  | BindOnce(&MockObject::Task, Unretained(&mock_object_), | 
|  | MakeRefCounted<ObjectToDelete>(&delete_task_flag_)), | 
|  | BindOnce(&MockObject::Reply, Unretained(&mock_object_), | 
|  | MakeRefCounted<ObjectToDelete>(&delete_reply_flag_)))); | 
|  |  | 
|  | // Expect the first task to be posted to |post_runner_|. | 
|  | EXPECT_TRUE(post_runner_->HasPendingTask()); | 
|  | EXPECT_FALSE(reply_runner_->HasPendingTask()); | 
|  | EXPECT_FALSE(delete_task_flag_); | 
|  | EXPECT_FALSE(delete_reply_flag_); | 
|  | } | 
|  |  | 
|  | scoped_refptr<MockRunsTasksInCurrentSequenceTaskRunner> post_runner_ = | 
|  | MakeRefCounted<MockRunsTasksInCurrentSequenceTaskRunner>(); | 
|  | scoped_refptr<MockRunsTasksInCurrentSequenceTaskRunner> reply_runner_ = | 
|  | MakeRefCounted<MockRunsTasksInCurrentSequenceTaskRunner>( | 
|  | TestMockTimeTaskRunner::Type::kBoundToThread); | 
|  | testing::StrictMock<MockObject> mock_object_; | 
|  | bool delete_task_flag_ = false; | 
|  | bool delete_reply_flag_ = false; | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(PostTaskAndReplyImplTest); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST_F(PostTaskAndReplyImplTest, PostTaskAndReply) { | 
|  | PostTaskAndReplyToMockObject(); | 
|  |  | 
|  | EXPECT_CALL(mock_object_, Task(_)); | 
|  | post_runner_->RunUntilIdleWithRunsTasksInCurrentSequence(); | 
|  | testing::Mock::VerifyAndClear(&mock_object_); | 
|  | // The task should have been deleted right after being run. | 
|  | EXPECT_TRUE(delete_task_flag_); | 
|  | EXPECT_FALSE(delete_reply_flag_); | 
|  |  | 
|  | // Expect the reply to be posted to |reply_runner_|. | 
|  | EXPECT_FALSE(post_runner_->HasPendingTask()); | 
|  | EXPECT_TRUE(reply_runner_->HasPendingTask()); | 
|  |  | 
|  | EXPECT_CALL(mock_object_, Reply(_)); | 
|  | reply_runner_->RunUntilIdleWithRunsTasksInCurrentSequence(); | 
|  | testing::Mock::VerifyAndClear(&mock_object_); | 
|  | EXPECT_TRUE(delete_task_flag_); | 
|  | // The reply should have been deleted right after being run. | 
|  | EXPECT_TRUE(delete_reply_flag_); | 
|  |  | 
|  | // Expect no pending task in |post_runner_| and |reply_runner_|. | 
|  | EXPECT_FALSE(post_runner_->HasPendingTask()); | 
|  | EXPECT_FALSE(reply_runner_->HasPendingTask()); | 
|  | } | 
|  |  | 
|  | TEST_F(PostTaskAndReplyImplTest, TaskDoesNotRun) { | 
|  | PostTaskAndReplyToMockObject(); | 
|  |  | 
|  | // Clear the |post_runner_|. Both callbacks should be scheduled for deletion | 
|  | // on the |reply_runner_|. | 
|  | post_runner_->ClearPendingTasksWithRunsTasksInCurrentSequence(); | 
|  | EXPECT_FALSE(post_runner_->HasPendingTask()); | 
|  | EXPECT_TRUE(reply_runner_->HasPendingTask()); | 
|  | EXPECT_FALSE(delete_task_flag_); | 
|  | EXPECT_FALSE(delete_reply_flag_); | 
|  |  | 
|  | // Run the |reply_runner_|. Both callbacks should be deleted. | 
|  | reply_runner_->RunUntilIdleWithRunsTasksInCurrentSequence(); | 
|  | EXPECT_TRUE(delete_task_flag_); | 
|  | EXPECT_TRUE(delete_reply_flag_); | 
|  | } | 
|  |  | 
|  | TEST_F(PostTaskAndReplyImplTest, ReplyDoesNotRun) { | 
|  | PostTaskAndReplyToMockObject(); | 
|  |  | 
|  | EXPECT_CALL(mock_object_, Task(_)); | 
|  | post_runner_->RunUntilIdleWithRunsTasksInCurrentSequence(); | 
|  | testing::Mock::VerifyAndClear(&mock_object_); | 
|  | // The task should have been deleted right after being run. | 
|  | EXPECT_TRUE(delete_task_flag_); | 
|  | EXPECT_FALSE(delete_reply_flag_); | 
|  |  | 
|  | // Expect the reply to be posted to |reply_runner_|. | 
|  | EXPECT_FALSE(post_runner_->HasPendingTask()); | 
|  | EXPECT_TRUE(reply_runner_->HasPendingTask()); | 
|  |  | 
|  | // Clear the |reply_runner_| queue without running tasks. The reply callback | 
|  | // should be deleted. | 
|  | reply_runner_->ClearPendingTasksWithRunsTasksInCurrentSequence(); | 
|  | EXPECT_TRUE(delete_task_flag_); | 
|  | EXPECT_TRUE(delete_reply_flag_); | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace base |