New pyauto automation hook to get browser process information.

Also modified the pyauto tests in process_count.py to use the new
automation hook.  Verified that this works on my local ChromeOS device,
my local Linux machine, and the Win/Mac trybots.  Re-enabling the tests
on Win/Mac in the CONTINUOUS suite to see if they now pass continuously.

BUG=94650,95142,93822
TEST=None

Review URL: http://codereview.chromium.org/7976016

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@102406 0039d316-1c4b-4281-b951-d872f2087c98
parent 84c13c03
......@@ -71,6 +71,7 @@
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/navigation_controller.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/common/child_process_info.h"
#include "content/common/json_value_serializer.h"
#include "content/common/notification_service.h"
#include "googleurl/src/gurl.h"
......@@ -2751,3 +2752,82 @@ void DragTargetDropAckNotificationObserver::Observe(
}
delete this;
}
ProcessInfoObserver::ProcessInfoObserver(
AutomationProvider* automation,
IPC::Message* reply_message)
: automation_(automation->AsWeakPtr()),
reply_message_(reply_message) {}
void ProcessInfoObserver::OnDetailsAvailable() {
scoped_ptr<DictionaryValue> return_value(new DictionaryValue);
ListValue* browser_proc_list = new ListValue();
const std::vector<ProcessData>& all_processes = processes();
for (size_t index = 0; index < all_processes.size(); ++index) {
DictionaryValue* browser_data = new DictionaryValue();
browser_data->SetString("name", all_processes[index].name);
browser_data->SetString("process_name", all_processes[index].process_name);
ListValue* proc_list = new ListValue();
for (ProcessMemoryInformationList::const_iterator iterator =
all_processes[index].processes.begin();
iterator != all_processes[index].processes.end(); ++iterator) {
DictionaryValue* proc_data = new DictionaryValue();
proc_data->SetInteger("pid", iterator->pid);
// Working set (resident) memory usage, in KBytes.
DictionaryValue* working_set = new DictionaryValue();
working_set->SetInteger("priv", iterator->working_set.priv);
working_set->SetInteger("shareable", iterator->working_set.shareable);
working_set->SetInteger("shared", iterator->working_set.shared);
proc_data->Set("working_set_mem", working_set);
// Committed (resident + paged) memory usage, in KBytes.
DictionaryValue* committed = new DictionaryValue();
committed->SetInteger("priv", iterator->committed.priv);
committed->SetInteger("mapped", iterator->committed.mapped);
committed->SetInteger("image", iterator->committed.image);
proc_data->Set("committed_mem", committed);
proc_data->SetString("version", iterator->version);
proc_data->SetString("product_name", iterator->product_name);
proc_data->SetInteger("num_processes", iterator->num_processes);
proc_data->SetBoolean("is_diagnostics", iterator->is_diagnostics);
// Process type, if this is a child process of Chrome (e.g., 'plugin').
std::string process_type = "Unknown";
// The following condition avoids a DCHECK in debug builds when the
// process type passed to |GetTypeNameInEnglish| is unknown.
if (iterator->type != ChildProcessInfo::UNKNOWN_PROCESS)
process_type = ChildProcessInfo::GetTypeNameInEnglish(iterator->type);
proc_data->SetString("child_process_type", process_type);
// Renderer type, if this is a renderer process.
std::string renderer_type = "Unknown";
if (iterator->renderer_type != ChildProcessInfo::RENDERER_UNKNOWN) {
renderer_type = ChildProcessInfo::GetRendererTypeNameInEnglish(
iterator->renderer_type);
}
proc_data->SetString("renderer_type", renderer_type);
// Titles associated with this process.
ListValue* titles = new ListValue();
for (size_t title_index = 0; title_index < iterator->titles.size();
++title_index)
titles->Append(Value::CreateStringValue(iterator->titles[title_index]));
proc_data->Set("titles", titles);
proc_list->Append(proc_data);
}
browser_data->Set("processes", proc_list);
browser_proc_list->Append(browser_data);
}
return_value->Set("browsers", browser_proc_list);
if (automation_) {
AutomationJSONReply(automation_, reply_message_.release())
.SendSuccess(return_value.get());
}
}
......@@ -37,6 +37,7 @@
#include "chrome/browser/history/history_types.h"
#include "chrome/browser/importer/importer_data_types.h"
#include "chrome/browser/importer/importer_progress_observer.h"
#include "chrome/browser/memory_details.h"
#include "chrome/browser/password_manager/password_store_change.h"
#include "chrome/browser/password_manager/password_store_consumer.h"
#include "chrome/browser/policy/browser_policy_connector.h"
......@@ -1707,4 +1708,21 @@ class DragTargetDropAckNotificationObserver : public NotificationObserver {
DISALLOW_COPY_AND_ASSIGN(DragTargetDropAckNotificationObserver);
};
// Allows the automation provider to wait for process memory details to be
// available before sending this information to the client.
class ProcessInfoObserver : public MemoryDetails {
public:
ProcessInfoObserver(AutomationProvider* automation,
IPC::Message* reply_message);
virtual void OnDetailsAvailable() OVERRIDE;
private:
~ProcessInfoObserver() {}
base::WeakPtr<AutomationProvider> automation_;
scoped_ptr<IPC::Message> reply_message_;
DISALLOW_COPY_AND_ASSIGN(ProcessInfoObserver);
};
#endif // CHROME_BROWSER_AUTOMATION_AUTOMATION_PROVIDER_OBSERVERS_H_
......@@ -2256,6 +2256,8 @@ void TestingAutomationProvider::SendJSONRequest(int handle,
&TestingAutomationProvider::CreateNewAutomationProvider;
handler_map["GetBrowserInfo"] =
&TestingAutomationProvider::GetBrowserInfo;
handler_map["GetProcessInfo"] =
&TestingAutomationProvider::GetProcessInfo;
#if defined(OS_CHROMEOS)
handler_map["GetLoginInfo"] = &TestingAutomationProvider::GetLoginInfo;
handler_map["ShowCreateAccountUI"] =
......@@ -2874,6 +2876,17 @@ void TestingAutomationProvider::GetBrowserInfo(
AutomationJSONReply(this, reply_message).SendSuccess(return_value.get());
}
// Sample json input: { "command": "GetProcessInfo" }
// Refer to GetProcessInfo() in chrome/test/pyautolib/pyauto.py for
// sample json output.
void TestingAutomationProvider::GetProcessInfo(
DictionaryValue* args,
IPC::Message* reply_message) {
scoped_refptr<ProcessInfoObserver>
proc_observer(new ProcessInfoObserver(this, reply_message));
proc_observer->StartFetch();
}
// Sample json input: { "command": "GetNavigationInfo" }
// Refer to GetNavigationInfo() in chrome/test/pyautolib/pyauto.py for
// sample json output.
......
......@@ -389,6 +389,10 @@ class TestingAutomationProvider : public AutomationProvider,
void GetBrowserInfo(base::DictionaryValue* args,
IPC::Message* reply_message);
// Get info about browser-related processes that currently exist.
void GetProcessInfo(base::DictionaryValue* args,
IPC::Message* reply_message);
// Get info about the state of navigation in a given tab.
// This includes ssl info.
// Uses the JSON interface for input/output.
......
......@@ -184,13 +184,6 @@
'-passwords.PasswordTest.testClearFetchedCredForNewUserName',
# Crashes browser. crbug.com/89000
'-prefs.PrefsTest.testGeolocationPref',
# crbug.com/93822
'-process_count.ProcessCountTest.testProcessCountAppendSingleTab',
'-process_count.ProcessCountTest.testProcessCountCombination',
'-process_count.ProcessCountTest.testProcessCountExtensionProcess',
'-process_count.ProcessCountTest.testProcessCountFlashProcess',
'-process_count.ProcessCountTest.testProcessCountFreshProfile',
'-process_count.ProcessCountTest.testProcessCountNewWindow',
# crbug.com/95621
'-youtube.YoutubeTest.testPlayerStatus',
],
......@@ -230,8 +223,6 @@
'-passwords',
# crbug.com/97140
'-plugins.PluginsTest.testDisableEnableAllPlugins',
# crbug.com/95142
'-process_count.ProcessCountTest.testProcessCountExtensionProcess',
# WebDriver requires python 2.6, which mac pyauto does not use yet.
# crbug.com/49379
'-pyauto_webdriver',
......
......@@ -12,46 +12,75 @@ import pyauto
class ProcessCountTest(pyauto.PyUITest):
"""Tests to ensure the number of Chrome-related processes is as expected."""
def _VerifyProcessCount(self, expected_count):
FRESH_PROFILE_PROC_COUNT = {
'win': 2, # Processes: browser, tab.
'mac': 2, # Processes: browser, tab.
'linux': 4, # Processes: browser, tab, sandbox helper, zygote.
'chromeos': 5, # Processes: browser, tab, sandbox helper, zygote, GPU.
}
CHROME_PROCESS_NAME = {
'win': 'chrome.exe',
'mac': 'Chromium',
'linux': 'chrome',
'chromeos': 'chrome',
}
def setUp(self):
self.proc_count_fresh_profile = 0
self.chrome_proc_name = ''
if self.IsChromeOS():
self.proc_count_fresh_profile = self.FRESH_PROFILE_PROC_COUNT['chromeos']
self.chrome_proc_name = self.CHROME_PROCESS_NAME['chromeos']
elif self.IsWin():
self.proc_count_fresh_profile = self.FRESH_PROFILE_PROC_COUNT['win']
self.chrome_proc_name = self.CHROME_PROCESS_NAME['win']
elif self.IsMac():
self.proc_count_fresh_profile = self.FRESH_PROFILE_PROC_COUNT['mac']
self.chrome_proc_name = self.CHROME_PROCESS_NAME['mac']
elif self.IsLinux():
self.proc_count_fresh_profile = self.FRESH_PROFILE_PROC_COUNT['linux']
self.chrome_proc_name = self.CHROME_PROCESS_NAME['linux']
pyauto.PyUITest.setUp(self)
def _VerifyProcessCount(self, num_expected):
"""Verifies the number of Chrome-related processes is as expected.
Args:
expected_count: An integer representing the expected number of Chrome-
related processes that should currently exist.
num_expected: An integer representing the expected number of Chrome-
related processes that should currently exist.
"""
# Compute the actual number of Chrome-related processes that exist.
# Processes include: a single browser process; a single renderer process
# for each tab in each window; 0 or more child processes (associated with
# plugins or other workers); and 0 or more extension processes.
info = self.GetBrowserInfo()
actual_count = (
1 + # Browser process.
sum([len(tab_info['tabs']) for tab_info in info['windows']]) +
len(info['child_processes']) + len(info['extension_views']))
self.assertEqual(actual_count, expected_count,
msg='Number of processes should be %d, but was %d.' %
(expected_count, actual_count))
proc_info = self.GetProcessInfo()
browser_info = [x for x in proc_info['browsers']
if x['process_name'] == self.chrome_proc_name]
assert len(browser_info) == 1
num_actual = len(browser_info[0]['processes'])
self.assertEqual(num_actual, num_expected,
msg='Number of processes should be %d, but was %d.\n'
'Actual process info:\n%s' % (
num_expected, num_actual, self.pformat(proc_info)))
def testProcessCountFreshProfile(self):
"""Verifies the process count in a fresh profile."""
self._VerifyProcessCount(2)
self._VerifyProcessCount(self.proc_count_fresh_profile)
def testProcessCountAppendSingleTab(self):
"""Verifies the process count after appending a single tab."""
self.AppendTab(pyauto.GURL('about:blank'), 0)
self._VerifyProcessCount(3)
self._VerifyProcessCount(self.proc_count_fresh_profile + 1)
def testProcessCountNewWindow(self):
"""Verifies the process count after opening a new window."""
self.OpenNewBrowserWindow(True)
self._VerifyProcessCount(3)
self._VerifyProcessCount(self.proc_count_fresh_profile + 1)
def testProcessCountFlashProcess(self):
"""Verifies the process count when the flash process is running."""
flash_url = self.GetFileURLForDataPath('plugin', 'flash.swf')
self.NavigateToURL(flash_url)
self._VerifyProcessCount(3)
self._VerifyProcessCount(self.proc_count_fresh_profile + 1)
def testProcessCountExtensionProcess(self):
"""Verifies the process count when an extension is installed."""
......@@ -59,10 +88,15 @@ class ProcessCountTest(pyauto.PyUITest):
os.path.join(self.DataDir(), 'extensions', 'page_action.crx'))
self.assertTrue(self.InstallExtension(crx_file_path, False),
msg='Extension install failed.')
self._VerifyProcessCount(3)
self._VerifyProcessCount(self.proc_count_fresh_profile + 1)
def testProcessCountCombination(self):
"""Verifies process count with a combination of tabs/windows/extensions."""
"""Verifies process count with a combination of tabs/windows/extensions.
This test installs 1 extension, appends 2 tabs to the window, navigates 1
tab to a flash page, opens 1 new window, and appends 3 tabs to the new
window (8 new processes expected).
"""
if self.IsMac():
# On Mac 10.5, flash files loaded too quickly after firing browser ends
# up getting downloaded, which seems to indicate that the plugin hasn't
......@@ -85,7 +119,7 @@ class ProcessCountTest(pyauto.PyUITest):
for _ in xrange(3):
self.AppendTab(pyauto.GURL('about:blank'), 1)
self._VerifyProcessCount(10)
self._VerifyProcessCount(self.proc_count_fresh_profile + 8)
if __name__ == '__main__':
......
......@@ -33,6 +33,7 @@ import logging
import optparse
import os
import pickle
import pprint
import shutil
import signal
import socket
......@@ -135,6 +136,10 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase):
self.Initialize(pyautolib.FilePath(self.BrowserPath()))
unittest.TestCase.__init__(self, methodName)
# Give all pyauto tests easy access to pprint.PrettyPrinter functions.
self.pprint = pprint.pprint
self.pformat = pprint.pformat
# Set up remote proxies, if they were requested.
self.remotes = []
self.remote = None
......@@ -1381,6 +1386,65 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase):
}
return self._GetResultFromJSONRequest(cmd_dict, windex=-1)
def GetProcessInfo(self):
"""Returns information about browser-related processes that currently exist.
This will also return information about other currently-running browsers
besides just Chrome.
Returns:
A dictionary containing browser-related process information as identified
by class MemoryDetails in src/chrome/browser/memory_details.h. The
dictionary contains a single key 'browsers', mapped to a list of
dictionaries containing information about each browser process name.
Each of those dictionaries contains a key 'processes', mapped to a list
of dictionaries containing the specific information for each process
with the given process name.
The memory values given in |committed_mem| and |working_set_mem| are in
KBytes.
Sample:
{ 'browsers': [ { 'name': 'Chromium',
'process_name': 'chrome',
'processes': [ { 'child_process_type': 'Browser',
'committed_mem': { 'image': 0,
'mapped': 0,
'priv': 0},
'is_diagnostics': False,
'num_processes': 1,
'pid': 7770,
'product_name': '',
'renderer_type': 'Unknown',
'titles': [],
'version': '',
'working_set_mem': { 'priv': 43672,
'shareable': 0,
'shared': 59251}},
{ 'child_process_type': 'Tab',
'committed_mem': { 'image': 0,
'mapped': 0,
'priv': 0},
'is_diagnostics': False,
'num_processes': 1,
'pid': 7791,
'product_name': '',
'renderer_type': 'Tab',
'titles': ['about:blank'],
'version': '',
'working_set_mem': { 'priv': 16768,
'shareable': 0,
'shared': 26256}},
...<more processes>...]}]}
Raises:
pyauto_errors.JSONInterfaceError if the automation call returns an error.
"""
cmd_dict = { # Prepare command for the json interface.
'command': 'GetProcessInfo',
}
return self._GetResultFromJSONRequest(cmd_dict, windex=-1)
def GetNavigationInfo(self, tab_index=0, windex=0):
"""Get info about the navigation state of a given tab.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment