|  | // Copyright 2017 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_scheduler/lazy_task_runner.h" | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/bind_helpers.h" | 
|  | #include "base/sequence_checker_impl.h" | 
|  | #include "base/task_scheduler/scoped_set_task_priority_for_current_thread.h" | 
|  | #include "base/test/scoped_task_environment.h" | 
|  | #include "base/threading/thread_checker_impl.h" | 
|  | #include "build_config.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | #include "base/win/com_init_util.h" | 
|  | #endif | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | LazySequencedTaskRunner g_sequenced_task_runner_user_visible = | 
|  | LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER({TaskPriority::USER_VISIBLE}); | 
|  | LazySequencedTaskRunner g_sequenced_task_runner_user_blocking = | 
|  | LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER({TaskPriority::USER_BLOCKING}); | 
|  |  | 
|  | LazySingleThreadTaskRunner g_single_thread_task_runner_user_visible = | 
|  | LAZY_SINGLE_THREAD_TASK_RUNNER_INITIALIZER( | 
|  | {TaskPriority::USER_VISIBLE}, | 
|  | SingleThreadTaskRunnerThreadMode::SHARED); | 
|  | LazySingleThreadTaskRunner g_single_thread_task_runner_user_blocking = | 
|  | LAZY_SINGLE_THREAD_TASK_RUNNER_INITIALIZER( | 
|  | {TaskPriority::USER_BLOCKING}, | 
|  | SingleThreadTaskRunnerThreadMode::SHARED); | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | LazyCOMSTATaskRunner g_com_sta_task_runner_user_visible = | 
|  | LAZY_COM_STA_TASK_RUNNER_INITIALIZER( | 
|  | {TaskPriority::USER_VISIBLE}, | 
|  | SingleThreadTaskRunnerThreadMode::SHARED); | 
|  | LazyCOMSTATaskRunner g_com_sta_task_runner_user_blocking = | 
|  | LAZY_COM_STA_TASK_RUNNER_INITIALIZER( | 
|  | {TaskPriority::USER_BLOCKING}, | 
|  | SingleThreadTaskRunnerThreadMode::SHARED); | 
|  | #endif  // defined(OS_WIN) | 
|  |  | 
|  | void InitCheckers(SequenceCheckerImpl* sequence_checker, | 
|  | ThreadCheckerImpl* thread_checker) { | 
|  | sequence_checker->DetachFromSequence(); | 
|  | EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); | 
|  | thread_checker->DetachFromThread(); | 
|  | EXPECT_TRUE(thread_checker->CalledOnValidThread()); | 
|  | } | 
|  |  | 
|  | void ExpectSequencedEnvironment(SequenceCheckerImpl* sequence_checker, | 
|  | ThreadCheckerImpl* thread_checker, | 
|  | TaskPriority expected_priority) { | 
|  | EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); | 
|  | EXPECT_FALSE(thread_checker->CalledOnValidThread()); | 
|  | EXPECT_EQ(expected_priority, internal::GetTaskPriorityForCurrentThread()); | 
|  | } | 
|  |  | 
|  | void ExpectSingleThreadEnvironment(SequenceCheckerImpl* sequence_checker, | 
|  | ThreadCheckerImpl* thread_checker, | 
|  | TaskPriority expected_priority | 
|  | #if defined(OS_WIN) | 
|  | , | 
|  | bool expect_com_sta = false | 
|  | #endif | 
|  | ) { | 
|  | EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); | 
|  | EXPECT_TRUE(thread_checker->CalledOnValidThread()); | 
|  | EXPECT_EQ(expected_priority, internal::GetTaskPriorityForCurrentThread()); | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | if (expect_com_sta) | 
|  | win::AssertComApartmentType(win::ComApartmentType::STA); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | class TaskSchedulerLazyTaskRunnerEnvironmentTest : public testing::Test { | 
|  | protected: | 
|  | TaskSchedulerLazyTaskRunnerEnvironmentTest() = default; | 
|  |  | 
|  | void TestTaskRunnerEnvironment(scoped_refptr<SequencedTaskRunner> task_runner, | 
|  | bool expect_single_thread, | 
|  | TaskPriority expected_priority | 
|  | #if defined(OS_WIN) | 
|  | , | 
|  | bool expect_com_sta = false | 
|  | #endif | 
|  | ) { | 
|  | SequenceCheckerImpl sequence_checker; | 
|  | ThreadCheckerImpl thread_checker; | 
|  | task_runner->PostTask(FROM_HERE, | 
|  | BindOnce(&InitCheckers, Unretained(&sequence_checker), | 
|  | Unretained(&thread_checker))); | 
|  | scoped_task_environment_.RunUntilIdle(); | 
|  |  | 
|  | OnceClosure task = | 
|  | expect_single_thread | 
|  | ? BindOnce(&ExpectSingleThreadEnvironment, | 
|  | Unretained(&sequence_checker), | 
|  | Unretained(&thread_checker), expected_priority | 
|  | #if defined(OS_WIN) | 
|  | , | 
|  | expect_com_sta | 
|  | #endif | 
|  | ) | 
|  | : BindOnce(&ExpectSequencedEnvironment, | 
|  | Unretained(&sequence_checker), | 
|  | Unretained(&thread_checker), expected_priority); | 
|  | task_runner->PostTask(FROM_HERE, std::move(task)); | 
|  | scoped_task_environment_.RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | test::ScopedTaskEnvironment scoped_task_environment_; | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(TaskSchedulerLazyTaskRunnerEnvironmentTest); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST_F(TaskSchedulerLazyTaskRunnerEnvironmentTest, | 
|  | LazySequencedTaskRunnerUserVisible) { | 
|  | TestTaskRunnerEnvironment(g_sequenced_task_runner_user_visible.Get(), false, | 
|  | TaskPriority::USER_VISIBLE); | 
|  | } | 
|  |  | 
|  | TEST_F(TaskSchedulerLazyTaskRunnerEnvironmentTest, | 
|  | LazySequencedTaskRunnerUserBlocking) { | 
|  | TestTaskRunnerEnvironment(g_sequenced_task_runner_user_blocking.Get(), false, | 
|  | TaskPriority::USER_BLOCKING); | 
|  | } | 
|  |  | 
|  | TEST_F(TaskSchedulerLazyTaskRunnerEnvironmentTest, | 
|  | LazySingleThreadTaskRunnerUserVisible) { | 
|  | TestTaskRunnerEnvironment(g_single_thread_task_runner_user_visible.Get(), | 
|  | true, TaskPriority::USER_VISIBLE); | 
|  | } | 
|  |  | 
|  | TEST_F(TaskSchedulerLazyTaskRunnerEnvironmentTest, | 
|  | LazySingleThreadTaskRunnerUserBlocking) { | 
|  | TestTaskRunnerEnvironment(g_single_thread_task_runner_user_blocking.Get(), | 
|  | true, TaskPriority::USER_BLOCKING); | 
|  | } | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | TEST_F(TaskSchedulerLazyTaskRunnerEnvironmentTest, | 
|  | LazyCOMSTATaskRunnerUserVisible) { | 
|  | TestTaskRunnerEnvironment(g_com_sta_task_runner_user_visible.Get(), true, | 
|  | TaskPriority::USER_VISIBLE, true); | 
|  | } | 
|  |  | 
|  | TEST_F(TaskSchedulerLazyTaskRunnerEnvironmentTest, | 
|  | LazyCOMSTATaskRunnerUserBlocking) { | 
|  | TestTaskRunnerEnvironment(g_com_sta_task_runner_user_blocking.Get(), true, | 
|  | TaskPriority::USER_BLOCKING, true); | 
|  | } | 
|  | #endif  // defined(OS_WIN) | 
|  |  | 
|  | TEST(TaskSchdulerLazyTaskRunnerTest, LazySequencedTaskRunnerReset) { | 
|  | for (int i = 0; i < 2; ++i) { | 
|  | test::ScopedTaskEnvironment scoped_task_environment; | 
|  | // If the TaskRunner isn't released when the test::ScopedTaskEnvironment | 
|  | // goes out of scope, the second invocation of the line below will access a | 
|  | // deleted TaskScheduler and crash. | 
|  | g_sequenced_task_runner_user_visible.Get()->PostTask(FROM_HERE, | 
|  | DoNothing()); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(TaskSchdulerLazyTaskRunnerTest, LazySingleThreadTaskRunnerReset) { | 
|  | for (int i = 0; i < 2; ++i) { | 
|  | test::ScopedTaskEnvironment scoped_task_environment; | 
|  | // If the TaskRunner isn't released when the test::ScopedTaskEnvironment | 
|  | // goes out of scope, the second invocation of the line below will access a | 
|  | // deleted TaskScheduler and crash. | 
|  | g_single_thread_task_runner_user_visible.Get()->PostTask(FROM_HERE, | 
|  | DoNothing()); | 
|  | } | 
|  | } | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | TEST(TaskSchdulerLazyTaskRunnerTest, LazyCOMSTATaskRunnerReset) { | 
|  | for (int i = 0; i < 2; ++i) { | 
|  | test::ScopedTaskEnvironment scoped_task_environment; | 
|  | // If the TaskRunner isn't released when the test::ScopedTaskEnvironment | 
|  | // goes out of scope, the second invocation of the line below will access a | 
|  | // deleted TaskScheduler and crash. | 
|  | g_com_sta_task_runner_user_visible.Get()->PostTask(FROM_HERE, DoNothing()); | 
|  | } | 
|  | } | 
|  | #endif  // defined(OS_WIN) | 
|  |  | 
|  | }  // namespace base |