blob: d23e53df8893b1ea18216a6f64ae4be2714fc698 [file] [log] [blame]
// 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.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