| // 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 <stdint.h> |
| |
| #include "base/containers/contains.h" |
| #include "base/containers/cxx20_erase.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/scoped_mock_time_message_loop_task_runner.h" |
| #include "chrome/browser/task_manager/providers/fallback_task_provider.h" |
| #include "chrome/browser/task_manager/providers/task.h" |
| #include "chrome/browser/task_manager/task_manager_observer.h" |
| #include "content/public/common/process_type.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/test_utils.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace task_manager { |
| |
| class FakeTask : public Task { |
| public: |
| FakeTask(base::ProcessId process_id, Type type, const std::string& title) |
| : Task(base::ASCIIToUTF16(title), |
| nullptr, |
| base::kNullProcessHandle, |
| process_id), |
| type_(type) {} |
| |
| FakeTask(const FakeTask&) = delete; |
| FakeTask& operator=(const FakeTask&) = delete; |
| |
| Type GetType() const override { return type_; } |
| |
| int GetChildProcessUniqueID() const override { return 0; } |
| |
| const Task* GetParentTask() const override { return nullptr; } |
| |
| SessionID GetTabId() const override { return SessionID::InvalidValue(); } |
| |
| private: |
| Type type_; |
| }; |
| |
| class FakeTaskProvider : public TaskProvider { |
| public: |
| FakeTaskProvider() = default; |
| FakeTaskProvider(const FakeTaskProvider&) = delete; |
| FakeTaskProvider& operator=(const FakeTaskProvider&) = delete; |
| ~FakeTaskProvider() override = default; |
| |
| Task* GetTaskOfUrlRequest(int child_id, int route_id) override { |
| return nullptr; |
| } |
| |
| void TaskAdded(Task* task) { |
| NotifyObserverTaskAdded(task); |
| task_provider_tasks_.emplace_back(task); |
| } |
| |
| void TaskRemoved(Task* task) { |
| NotifyObserverTaskRemoved(task); |
| base::Erase(task_provider_tasks_, task); |
| } |
| |
| private: |
| void StartUpdating() override { |
| for (Task* task : task_provider_tasks_) { |
| NotifyObserverTaskAdded(task); |
| } |
| } |
| |
| void StopUpdating() override {} |
| |
| std::vector<Task*> task_provider_tasks_; |
| }; |
| |
| // Defines a test for the child process task provider and the child process |
| // tasks themselves. |
| class FallbackTaskProviderTest : public testing::Test, |
| public TaskProviderObserver { |
| public: |
| FallbackTaskProviderTest() { |
| std::vector<std::unique_ptr<TaskProvider>> primary_subproviders; |
| primary_subproviders.push_back(std::make_unique<FakeTaskProvider>()); |
| primary_subproviders.push_back(std::make_unique<FakeTaskProvider>()); |
| |
| task_provider_ = std::make_unique<FallbackTaskProvider>( |
| std::move(primary_subproviders), std::make_unique<FakeTaskProvider>()); |
| |
| task_provider_->allow_fallback_for_testing_ = true; |
| } |
| |
| FallbackTaskProviderTest(const FallbackTaskProviderTest&) = delete; |
| FallbackTaskProviderTest& operator=(const FallbackTaskProviderTest&) = delete; |
| ~FallbackTaskProviderTest() override = default; |
| |
| // task_manager::TaskProviderObserver: |
| void TaskAdded(Task* task) override { |
| EXPECT_FALSE(base::Contains(seen_tasks_, task)); |
| seen_tasks_.emplace_back(task); |
| } |
| |
| void TaskRemoved(Task* task) override { |
| EXPECT_TRUE(base::Contains(seen_tasks_, task)); |
| base::Erase(seen_tasks_, task); |
| } |
| |
| // This adds tasks to the first primary subprovider. |
| void FirstPrimaryTaskAdded(Task* task) { |
| DCHECK(task); |
| static_cast<FakeTaskProvider*>( |
| task_provider_->primary_sources_[0]->subprovider()) |
| ->TaskAdded(task); |
| } |
| |
| // This removes tasks from the first primary subprovider. |
| void FirstPrimaryTaskRemoved(Task* task) { |
| DCHECK(task); |
| static_cast<FakeTaskProvider*>( |
| task_provider_->primary_sources_[0]->subprovider()) |
| ->TaskRemoved(task); |
| } |
| |
| // This adds tasks to the second primary subprovider. |
| void SecondPrimaryTaskAdded(Task* task) { |
| DCHECK(task); |
| static_cast<FakeTaskProvider*>( |
| task_provider_->primary_sources_[1]->subprovider()) |
| ->TaskAdded(task); |
| } |
| |
| // This removes tasks from the second primary subprovider. |
| void SecondPrimaryTaskRemoved(Task* task) { |
| DCHECK(task); |
| static_cast<FakeTaskProvider*>( |
| task_provider_->primary_sources_[1]->subprovider()) |
| ->TaskRemoved(task); |
| } |
| |
| // This adds tasks to the secondary subprovider. |
| void SecondaryTaskAdded(Task* task) { |
| DCHECK(task); |
| static_cast<FakeTaskProvider*>( |
| task_provider_->secondary_source_->subprovider()) |
| ->TaskAdded(task); |
| } |
| |
| // This removes tasks from the secondary subprovider. |
| void SecondaryTaskRemoved(Task* task) { |
| DCHECK(task); |
| static_cast<FakeTaskProvider*>( |
| task_provider_->secondary_source_->subprovider()) |
| ->TaskRemoved(task); |
| } |
| |
| std::string DumpSeenTasks() { |
| std::string result; |
| for (Task* task : seen_tasks_) { |
| result += base::UTF16ToUTF8(task->title()); |
| result += "\n"; |
| } |
| return result; |
| } |
| |
| void StartUpdating() { task_provider_->SetObserver(this); } |
| |
| void StopUpdating() { |
| task_provider_->ClearObserver(); |
| seen_tasks_.clear(); |
| } |
| |
| // This is the vector of tasks the FallbackTaskProvider has told us about. |
| std::vector<Task*> seen_tasks() { return seen_tasks_; } |
| |
| private: |
| content::BrowserTaskEnvironment task_environment_; |
| std::unique_ptr<FallbackTaskProvider> task_provider_; |
| std::vector<Task*> seen_tasks_; |
| }; |
| |
| TEST_F(FallbackTaskProviderTest, BasicTest) { |
| // The delay for showing a secondary source is 750ms; delay 1000ms to ensure |
| // we see them. |
| base::TimeDelta delay = base::Milliseconds(1000); |
| base::ScopedMockTimeMessageLoopTaskRunner mock_main_runner; |
| StartUpdating(); |
| |
| // There are two primary task providers and one secondary task provider. The |
| // naming convention here is "P_x_y". P is either P or Q for the two primary |
| // providers, and S for the secondary provider. The x is the PID of the |
| // process, and the y is the task index hosted within the process. For |
| // example, the first secondary task with process PID of 1 will be named |
| // "S_1_1". Similarly, the third primary task from the first primary provider |
| // with process PID of 7 would be "P_7_3". |
| |
| FakeTask fake_secondary_task_1_1(1, Task::RENDERER, "S_1_1"); |
| SecondaryTaskAdded(&fake_secondary_task_1_1); |
| mock_main_runner->FastForwardBy(delay); |
| EXPECT_EQ("S_1_1\n", DumpSeenTasks()); |
| |
| FakeTask fake_secondary_task_1_2(1, Task::RENDERER, "S_1_2"); |
| SecondaryTaskAdded(&fake_secondary_task_1_2); |
| mock_main_runner->FastForwardBy(delay); |
| EXPECT_EQ( |
| "S_1_1\n" |
| "S_1_2\n", |
| DumpSeenTasks()); |
| |
| FakeTask fake_primary_task_1_1(1, Task::RENDERER, "P_1_1"); |
| FirstPrimaryTaskAdded(&fake_primary_task_1_1); |
| mock_main_runner->FastForwardBy(delay); |
| EXPECT_EQ("P_1_1\n", DumpSeenTasks()); |
| |
| FakeTask fake_secondary_task_1_3(1, Task::RENDERER, "S_1_3"); |
| SecondaryTaskAdded(&fake_secondary_task_1_3); |
| mock_main_runner->FastForwardBy(delay); |
| EXPECT_EQ("P_1_1\n", DumpSeenTasks()); |
| |
| FakeTask fake_secondary_task_2_1(2, Task::RENDERER, "S_2_1"); |
| SecondaryTaskAdded(&fake_secondary_task_2_1); |
| mock_main_runner->FastForwardBy(delay); |
| EXPECT_EQ( |
| "P_1_1\n" |
| "S_2_1\n", |
| DumpSeenTasks()); |
| |
| FakeTask fake_primary_task_3_1(3, Task::RENDERER, "Q_3_1"); |
| SecondPrimaryTaskAdded(&fake_primary_task_3_1); |
| mock_main_runner->FastForwardBy(delay); |
| EXPECT_EQ( |
| "P_1_1\n" |
| "S_2_1\n" |
| "Q_3_1\n", |
| DumpSeenTasks()); |
| |
| FirstPrimaryTaskRemoved(&fake_primary_task_1_1); |
| mock_main_runner->FastForwardBy(delay); |
| EXPECT_EQ( |
| "S_2_1\n" |
| "Q_3_1\n" |
| "S_1_1\n" |
| "S_1_2\n" |
| "S_1_3\n", |
| DumpSeenTasks()); |
| |
| StopUpdating(); |
| EXPECT_EQ("", DumpSeenTasks()); |
| |
| // After updating the primary tasks (Ps) will be added before the secondary |
| // tasks (Ss) so it is reordered. |
| StartUpdating(); |
| mock_main_runner->FastForwardBy(delay); |
| EXPECT_EQ( |
| "Q_3_1\n" |
| "S_1_1\n" |
| "S_1_2\n" |
| "S_1_3\n" |
| "S_2_1\n", |
| DumpSeenTasks()); |
| |
| FirstPrimaryTaskAdded(&fake_primary_task_1_1); |
| mock_main_runner->FastForwardBy(delay); |
| EXPECT_EQ( |
| "Q_3_1\n" |
| "S_2_1\n" |
| "P_1_1\n", |
| DumpSeenTasks()); |
| |
| FakeTask fake_primary_task_1_2(1, Task::RENDERER, "P_1_2"); |
| FirstPrimaryTaskAdded(&fake_primary_task_1_2); |
| EXPECT_EQ( |
| "Q_3_1\n" |
| "S_2_1\n" |
| "P_1_1\n" |
| "P_1_2\n", |
| DumpSeenTasks()); |
| |
| FirstPrimaryTaskRemoved(&fake_primary_task_1_1); |
| mock_main_runner->FastForwardBy(delay); |
| EXPECT_EQ( |
| "Q_3_1\n" |
| "S_2_1\n" |
| "P_1_2\n", |
| DumpSeenTasks()); |
| |
| SecondaryTaskRemoved(&fake_secondary_task_2_1); |
| mock_main_runner->FastForwardBy(delay); |
| EXPECT_EQ( |
| "Q_3_1\n" |
| "P_1_2\n", |
| DumpSeenTasks()); |
| |
| SecondaryTaskRemoved(&fake_secondary_task_1_1); |
| mock_main_runner->FastForwardBy(delay); |
| EXPECT_EQ( |
| "Q_3_1\n" |
| "P_1_2\n", |
| DumpSeenTasks()); |
| |
| FirstPrimaryTaskRemoved(&fake_primary_task_1_2); |
| mock_main_runner->FastForwardBy(delay); |
| EXPECT_EQ( |
| "Q_3_1\n" |
| "S_1_2\n" |
| "S_1_3\n", |
| DumpSeenTasks()); |
| |
| SecondPrimaryTaskRemoved(&fake_primary_task_3_1); |
| mock_main_runner->FastForwardBy(delay); |
| EXPECT_EQ( |
| "S_1_2\n" |
| "S_1_3\n", |
| DumpSeenTasks()); |
| } |
| |
| } // namespace task_manager |