|  | // Copyright 2018 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/post_task.h" | 
|  |  | 
|  | #include "base/bind_helpers.h" | 
|  | #include "base/metrics/statistics_recorder.h" | 
|  | #include "base/task/task_executor.h" | 
|  | #include "base/task/test_task_traits_extension.h" | 
|  | #include "base/test/gtest_util.h" | 
|  | #include "base/test/scoped_task_environment.h" | 
|  | #include "base/test/test_simple_task_runner.h" | 
|  | #include "build/build_config.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | using ::testing::_; | 
|  | using ::testing::Invoke; | 
|  | using ::testing::Return; | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class MockTaskExecutor : public TaskExecutor { | 
|  | public: | 
|  | MockTaskExecutor() { | 
|  | ON_CALL(*this, PostDelayedTaskWithTraitsMock(_, _, _, _)) | 
|  | .WillByDefault(Invoke([this](const Location& from_here, | 
|  | const TaskTraits& traits, | 
|  | OnceClosure& task, TimeDelta delay) { | 
|  | return runner_->PostDelayedTask(from_here, std::move(task), delay); | 
|  | })); | 
|  | ON_CALL(*this, CreateTaskRunnerWithTraits(_)) | 
|  | .WillByDefault(Return(runner_)); | 
|  | ON_CALL(*this, CreateSequencedTaskRunnerWithTraits(_)) | 
|  | .WillByDefault(Return(runner_)); | 
|  | ON_CALL(*this, CreateSingleThreadTaskRunnerWithTraits(_, _)) | 
|  | .WillByDefault(Return(runner_)); | 
|  | #if defined(OS_WIN) | 
|  | ON_CALL(*this, CreateCOMSTATaskRunnerWithTraits(_, _)) | 
|  | .WillByDefault(Return(runner_)); | 
|  | #endif  // defined(OS_WIN) | 
|  | } | 
|  |  | 
|  | // TaskExecutor: | 
|  | // Helper because gmock doesn't support move-only types. | 
|  | bool PostDelayedTaskWithTraits(const Location& from_here, | 
|  | const TaskTraits& traits, | 
|  | OnceClosure task, | 
|  | TimeDelta delay) override { | 
|  | return PostDelayedTaskWithTraitsMock(from_here, traits, task, delay); | 
|  | } | 
|  | MOCK_METHOD4(PostDelayedTaskWithTraitsMock, | 
|  | bool(const Location& from_here, | 
|  | const TaskTraits& traits, | 
|  | OnceClosure& task, | 
|  | TimeDelta delay)); | 
|  | MOCK_METHOD1(CreateTaskRunnerWithTraits, | 
|  | scoped_refptr<TaskRunner>(const TaskTraits& traits)); | 
|  | MOCK_METHOD1(CreateSequencedTaskRunnerWithTraits, | 
|  | scoped_refptr<SequencedTaskRunner>(const TaskTraits& traits)); | 
|  | MOCK_METHOD2(CreateSingleThreadTaskRunnerWithTraits, | 
|  | scoped_refptr<SingleThreadTaskRunner>( | 
|  | const TaskTraits& traits, | 
|  | SingleThreadTaskRunnerThreadMode thread_mode)); | 
|  | #if defined(OS_WIN) | 
|  | MOCK_METHOD2(CreateCOMSTATaskRunnerWithTraits, | 
|  | scoped_refptr<SingleThreadTaskRunner>( | 
|  | const TaskTraits& traits, | 
|  | SingleThreadTaskRunnerThreadMode thread_mode)); | 
|  | #endif  // defined(OS_WIN) | 
|  |  | 
|  | TestSimpleTaskRunner* runner() const { return runner_.get(); } | 
|  |  | 
|  | private: | 
|  | scoped_refptr<TestSimpleTaskRunner> runner_ = | 
|  | MakeRefCounted<TestSimpleTaskRunner>(); | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(MockTaskExecutor); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | class PostTaskTestWithExecutor : public ::testing::Test { | 
|  | public: | 
|  | PostTaskTestWithExecutor() | 
|  | : recorder_for_testing_(StatisticsRecorder::CreateTemporaryForTesting()), | 
|  | scoped_task_environment_() {} | 
|  | void SetUp() override { | 
|  | RegisterTaskExecutor(TestTaskTraitsExtension::kExtensionId, &executor_); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | UnregisterTaskExecutorForTesting(TestTaskTraitsExtension::kExtensionId); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | testing::StrictMock<MockTaskExecutor> executor_; | 
|  | std::unique_ptr<StatisticsRecorder> recorder_for_testing_; | 
|  | test::ScopedTaskEnvironment scoped_task_environment_; | 
|  | }; | 
|  |  | 
|  | TEST_F(PostTaskTestWithExecutor, PostTaskToTaskScheduler) { | 
|  | // Tasks without extension should not go to the TestTaskExecutor. | 
|  | EXPECT_TRUE(PostTask(FROM_HERE, DoNothing())); | 
|  | EXPECT_FALSE(executor_.runner()->HasPendingTask()); | 
|  |  | 
|  | EXPECT_TRUE(PostTaskWithTraits(FROM_HERE, {MayBlock()}, DoNothing())); | 
|  | EXPECT_FALSE(executor_.runner()->HasPendingTask()); | 
|  |  | 
|  | // Task runners without extension should not be the executor's. | 
|  | auto task_runner = CreateTaskRunnerWithTraits({}); | 
|  | EXPECT_NE(executor_.runner(), task_runner); | 
|  | auto sequenced_task_runner = CreateSequencedTaskRunnerWithTraits({}); | 
|  | EXPECT_NE(executor_.runner(), sequenced_task_runner); | 
|  | auto single_thread_task_runner = CreateSingleThreadTaskRunnerWithTraits({}); | 
|  | EXPECT_NE(executor_.runner(), single_thread_task_runner); | 
|  | #if defined(OS_WIN) | 
|  | auto comsta_task_runner = CreateCOMSTATaskRunnerWithTraits({}); | 
|  | EXPECT_NE(executor_.runner(), comsta_task_runner); | 
|  | #endif  // defined(OS_WIN) | 
|  | } | 
|  |  | 
|  | #if !defined(STARBOARD) | 
|  | // Cobalt does not support tasks with extension yet. | 
|  | TEST_F(PostTaskTestWithExecutor, PostTaskToTaskExecutor) { | 
|  | // Tasks with extension should go to the executor. | 
|  | { | 
|  | TaskTraits traits = {TestExtensionBoolTrait()}; | 
|  | TaskTraits traits_with_explicit_priority = | 
|  | TaskTraits::Override(traits, {TaskPriority::USER_VISIBLE}); | 
|  | EXPECT_CALL(executor_, PostDelayedTaskWithTraitsMock( | 
|  | _, traits_with_explicit_priority, _, _)) | 
|  | .Times(1); | 
|  | EXPECT_TRUE(PostTaskWithTraits(FROM_HERE, traits, DoNothing())); | 
|  | EXPECT_TRUE(executor_.runner()->HasPendingTask()); | 
|  | executor_.runner()->ClearPendingTasks(); | 
|  | } | 
|  |  | 
|  | { | 
|  | TaskTraits traits = {MayBlock(), TestExtensionBoolTrait()}; | 
|  | TaskTraits traits_with_explicit_priority = | 
|  | TaskTraits::Override(traits, {TaskPriority::USER_VISIBLE}); | 
|  | EXPECT_CALL(executor_, PostDelayedTaskWithTraitsMock( | 
|  | _, traits_with_explicit_priority, _, _)) | 
|  | .Times(1); | 
|  | EXPECT_TRUE(PostTaskWithTraits(FROM_HERE, traits, DoNothing())); | 
|  | EXPECT_TRUE(executor_.runner()->HasPendingTask()); | 
|  | executor_.runner()->ClearPendingTasks(); | 
|  | } | 
|  |  | 
|  | { | 
|  | TaskTraits traits = {TestExtensionEnumTrait::kB, TestExtensionBoolTrait()}; | 
|  | TaskTraits traits_with_explicit_priority = | 
|  | TaskTraits::Override(traits, {TaskPriority::USER_VISIBLE}); | 
|  | EXPECT_CALL(executor_, PostDelayedTaskWithTraitsMock( | 
|  | _, traits_with_explicit_priority, _, _)) | 
|  | .Times(1); | 
|  | EXPECT_TRUE(PostTaskWithTraits(FROM_HERE, traits, DoNothing())); | 
|  | EXPECT_TRUE(executor_.runner()->HasPendingTask()); | 
|  | executor_.runner()->ClearPendingTasks(); | 
|  | } | 
|  |  | 
|  | // Task runners with extension should be the executor's. | 
|  | { | 
|  | TaskTraits traits = {TestExtensionBoolTrait()}; | 
|  | TaskTraits traits_with_explicit_priority = | 
|  | TaskTraits::Override(traits, {TaskPriority::USER_VISIBLE}); | 
|  | EXPECT_CALL(executor_, | 
|  | CreateTaskRunnerWithTraits(traits_with_explicit_priority)) | 
|  | .Times(1); | 
|  | auto task_runner = CreateTaskRunnerWithTraits(traits); | 
|  | EXPECT_EQ(executor_.runner(), task_runner); | 
|  | EXPECT_CALL(executor_, CreateSequencedTaskRunnerWithTraits( | 
|  | traits_with_explicit_priority)) | 
|  | .Times(1); | 
|  | auto sequenced_task_runner = CreateSequencedTaskRunnerWithTraits(traits); | 
|  | EXPECT_EQ(executor_.runner(), sequenced_task_runner); | 
|  | EXPECT_CALL(executor_, CreateSingleThreadTaskRunnerWithTraits( | 
|  | traits_with_explicit_priority, _)) | 
|  | .Times(1); | 
|  | auto single_thread_task_runner = | 
|  | CreateSingleThreadTaskRunnerWithTraits(traits); | 
|  | EXPECT_EQ(executor_.runner(), single_thread_task_runner); | 
|  | #if defined(OS_WIN) | 
|  | EXPECT_CALL(executor_, CreateCOMSTATaskRunnerWithTraits( | 
|  | traits_with_explicit_priority, _)) | 
|  | .Times(1); | 
|  | auto comsta_task_runner = CreateCOMSTATaskRunnerWithTraits(traits); | 
|  | EXPECT_EQ(executor_.runner(), comsta_task_runner); | 
|  | #endif  // defined(OS_WIN) | 
|  | } | 
|  | } | 
|  | #endif  // !defined(STARBOARD) | 
|  |  | 
|  | TEST_F(PostTaskTestWithExecutor, RegisterExecutorTwice) { | 
|  | testing::FLAGS_gtest_death_test_style = "threadsafe"; | 
|  | EXPECT_DCHECK_DEATH( | 
|  | RegisterTaskExecutor(TestTaskTraitsExtension::kExtensionId, &executor_)); | 
|  | } | 
|  |  | 
|  | }  // namespace base |