| // 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 "chrome/browser/task_manager/task_manager_browsertest_util.h" |
| |
| #include "base/bind_helpers.h" |
| #include "base/command_line.h" |
| #include "base/location.h" |
| #include "base/run_loop.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/pattern.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/test_timeouts.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/timer/timer.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/task_manager/task_manager_tester.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "extensions/strings/grit/extensions_strings.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/models/table_model_observer.h" |
| |
| namespace task_manager { |
| namespace browsertest_util { |
| |
| namespace { |
| |
| // Helper class to run a message loop until a TaskManagerTester is in an |
| // expected state. If timeout occurs, an ASCII version of the task manager's |
| // contents, along with a summary of the expected state, are dumped to test |
| // output, to assist debugging. |
| class ResourceChangeObserver { |
| public: |
| ResourceChangeObserver(int required_count, |
| const base::string16& title_pattern, |
| ColumnSpecifier column_specifier, |
| size_t min_column_value) |
| : required_count_(required_count), |
| title_pattern_(title_pattern), |
| column_specifier_(column_specifier), |
| min_column_value_(min_column_value) { |
| task_manager_tester_ = TaskManagerTester::Create(base::Bind( |
| &ResourceChangeObserver::OnResourceChange, base::Unretained(this))); |
| } |
| |
| void RunUntilSatisfied() { |
| // See if the condition is satisfied without having to run the loop. This |
| // check has to be placed after the installation of the |
| // TaskManagerModelObserver, because resources may change before that. |
| if (IsSatisfied()) |
| return; |
| |
| timer_.Start(FROM_HERE, TestTimeouts::action_timeout(), this, |
| &ResourceChangeObserver::OnTimeout); |
| |
| run_loop_.Run(); |
| |
| // If we succeeded normally (no timeout), check our post condition again |
| // before returning control to the test. If it is no longer satisfied, the |
| // test is likely flaky: we were waiting for a state that was only achieved |
| // emphemerally), so treat this as a failure. |
| if (!IsSatisfied() && timer_.IsRunning()) { |
| FAIL() << "Wait condition satisfied only emphemerally. Likely test " |
| << "problem. Maybe wait instead for the state below?\n" |
| << DumpTaskManagerModel(); |
| } |
| |
| timer_.Stop(); |
| } |
| |
| private: |
| void OnResourceChange() { |
| if (!IsSatisfied()) |
| return; |
| |
| base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, |
| run_loop_.QuitClosure()); |
| } |
| |
| bool IsSatisfied() { return CountMatches() == required_count_; } |
| |
| int CountMatches() { |
| int match_count = 0; |
| for (int i = 0; i < task_manager_tester_->GetRowCount(); i++) { |
| if (!base::MatchPattern(task_manager_tester_->GetRowTitle(i), |
| title_pattern_)) |
| continue; |
| |
| if (GetColumnValue(i) < min_column_value_) |
| continue; |
| |
| match_count++; |
| } |
| return match_count; |
| } |
| |
| int64_t GetColumnValue(int index) { |
| return task_manager_tester_->GetColumnValue(column_specifier_, index); |
| } |
| |
| const char* GetColumnName() { |
| switch (column_specifier_) { |
| case ColumnSpecifier::COLUMN_NONE: |
| return "N/A"; |
| case ColumnSpecifier::PROCESS_ID: |
| return "Process ID"; |
| case ColumnSpecifier::MEMORY_FOOTPRINT: |
| return "Memory Footprint"; |
| case ColumnSpecifier::V8_MEMORY: |
| return "V8 Memory"; |
| case ColumnSpecifier::V8_MEMORY_USED: |
| return "V8 Memory Used"; |
| case ColumnSpecifier::SQLITE_MEMORY_USED: |
| return "SQLite Memory Used"; |
| case ColumnSpecifier::IDLE_WAKEUPS: |
| return "Idle wake ups"; |
| case ColumnSpecifier::NETWORK_USE: |
| return "Network"; |
| case ColumnSpecifier::TOTAL_NETWORK_USE: |
| return "Total Network"; |
| } |
| return "N/A"; |
| } |
| |
| void OnTimeout() { |
| base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, |
| run_loop_.QuitClosure()); |
| FAIL() << "Timed out.\n" << DumpTaskManagerModel(); |
| } |
| |
| testing::Message DumpTaskManagerModel() { |
| testing::Message task_manager_state_dump; |
| task_manager_state_dump << "Waiting for exactly " << required_count_ |
| << " matches of wildcard pattern \"" |
| << base::UTF16ToASCII(title_pattern_) << "\""; |
| if (min_column_value_ > 0) { |
| task_manager_state_dump << " && [" << GetColumnName() |
| << " >= " << min_column_value_ << "]"; |
| } |
| task_manager_state_dump << "\nCurrently there are " << CountMatches() |
| << " matches."; |
| task_manager_state_dump << "\nCurrent Task Manager Model is:"; |
| for (int i = 0; i < task_manager_tester_->GetRowCount(); i++) { |
| task_manager_state_dump |
| << "\n > " << std::setw(40) << std::left |
| << base::UTF16ToASCII(task_manager_tester_->GetRowTitle(i)); |
| if (min_column_value_ > 0) { |
| task_manager_state_dump << " [" << GetColumnName() |
| << " == " << GetColumnValue(i) << "]"; |
| } |
| } |
| return task_manager_state_dump; |
| } |
| |
| std::unique_ptr<TaskManagerTester> task_manager_tester_; |
| const int required_count_; |
| const base::string16 title_pattern_; |
| const ColumnSpecifier column_specifier_; |
| const int64_t min_column_value_; |
| base::RunLoop run_loop_; |
| base::OneShotTimer timer_; |
| }; |
| |
| } // namespace |
| |
| void WaitForTaskManagerRows(int required_count, |
| const base::string16& title_pattern) { |
| const int column_value_dont_care = 0; |
| ResourceChangeObserver observer(required_count, title_pattern, |
| ColumnSpecifier::COLUMN_NONE, |
| column_value_dont_care); |
| observer.RunUntilSatisfied(); |
| } |
| |
| void WaitForTaskManagerStatToExceed(const base::string16& title_pattern, |
| ColumnSpecifier column_getter, |
| size_t min_column_value) { |
| const int wait_for_one_match = 1; |
| ResourceChangeObserver observer(wait_for_one_match, title_pattern, |
| column_getter, min_column_value); |
| observer.RunUntilSatisfied(); |
| } |
| |
| base::string16 MatchTab(const char* title) { |
| return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_TAB_PREFIX, |
| base::ASCIIToUTF16(title)); |
| } |
| |
| base::string16 MatchAnyTab() { |
| return MatchTab("*"); |
| } |
| |
| base::string16 MatchAboutBlankTab() { |
| return MatchTab("about:blank"); |
| } |
| |
| base::string16 MatchExtension(const char* title) { |
| return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_EXTENSION_PREFIX, |
| base::ASCIIToUTF16(title)); |
| } |
| |
| base::string16 MatchAnyExtension() { |
| return MatchExtension("*"); |
| } |
| |
| base::string16 MatchApp(const char* title) { |
| return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_APP_PREFIX, |
| base::ASCIIToUTF16(title)); |
| } |
| |
| base::string16 MatchAnyApp() { |
| return MatchApp("*"); |
| } |
| |
| base::string16 MatchWebView(const char* title) { |
| return l10n_util::GetStringFUTF16( |
| IDS_EXTENSION_TASK_MANAGER_WEBVIEW_TAG_PREFIX, base::ASCIIToUTF16(title)); |
| } |
| |
| base::string16 MatchAnyWebView() { |
| return MatchWebView("*"); |
| } |
| |
| base::string16 MatchBackground(const char* title) { |
| return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_BACKGROUND_PREFIX, |
| base::ASCIIToUTF16(title)); |
| } |
| |
| base::string16 MatchAnyBackground() { |
| return MatchBackground("*"); |
| } |
| |
| base::string16 MatchPrint(const char* title) { |
| return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_PRINT_PREFIX, |
| base::ASCIIToUTF16(title)); |
| } |
| |
| base::string16 MatchAnyPrint() { |
| return MatchPrint("*"); |
| } |
| |
| base::string16 MatchSubframe(const char* title) { |
| return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_SUBFRAME_PREFIX, |
| base::ASCIIToUTF16(title)); |
| } |
| |
| base::string16 MatchAnySubframe() { |
| return MatchSubframe("*"); |
| } |
| |
| base::string16 MatchUtility(const base::string16& title) { |
| return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_UTILITY_PREFIX, title); |
| } |
| |
| base::string16 MatchAnyUtility() { |
| return MatchUtility(base::ASCIIToUTF16("*")); |
| } |
| |
| } // namespace browsertest_util |
| } // namespace task_manager |