blob: 5bcd785e693b51aa2b0b1f84f3e5feaddbe59e8e [file] [log] [blame]
// Copyright 2014 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/power/process_power_collector.h"
#include "chrome/browser/apps/scoped_keep_alive.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/apps/chrome_app_delegate.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/power/origin_power_map.h"
#include "components/power/origin_power_map_factory.h"
#include "content/public/browser/site_instance.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/mock_render_process_host.h"
#include "extensions/browser/app_window/app_window_client.h"
#include "extensions/browser/app_window/app_window_contents.h"
#include "extensions/browser/app_window/app_window_registry.h"
#include "extensions/browser/app_window/native_app_window.h"
#include "extensions/common/extension.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#if defined(OS_CHROMEOS)
#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
#endif
using power::OriginPowerMap;
using power::OriginPowerMapFactory;
class BrowserProcessPowerTest : public BrowserWithTestWindowTest {
public:
BrowserProcessPowerTest() {}
~BrowserProcessPowerTest() override {}
void SetUp() override {
BrowserWithTestWindowTest::SetUp();
collector.reset(new ProcessPowerCollector);
#if defined(OS_CHROMEOS)
power_manager::PowerSupplyProperties prop;
prop.set_external_power(power_manager::PowerSupplyProperties::AC);
prop.set_battery_state(power_manager::PowerSupplyProperties::DISCHARGING);
prop.set_battery_percent(20.00);
prop.set_battery_discharge_rate(1);
collector->PowerChanged(prop);
#endif
profile_manager_.reset(
new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
ASSERT_TRUE(profile_manager_->SetUp());
}
void TearDown() override {
collector.reset();
BrowserWithTestWindowTest::TearDown();
}
// Mocks out CPU usage for all processes as |value| percent.
double ReturnCpuAsConstant(double value, base::ProcessHandle handle) {
return value;
}
protected:
content::MockRenderProcessHost* GetProcess(Browser* browser) {
return static_cast<content::MockRenderProcessHost*>(
browser->tab_strip_model()
->GetActiveWebContents()
->GetRenderViewHost()
->GetProcess());
}
scoped_ptr<base::ProcessHandle> MakeProcessHandle(int process_id) {
scoped_ptr<base::ProcessHandle> proc_handle(new base::ProcessHandle(
#if defined(OS_WIN)
reinterpret_cast<HANDLE>(process_id))
#else
process_id)
#endif
);
return proc_handle.Pass();
}
scoped_ptr<ProcessPowerCollector> collector;
scoped_ptr<TestingProfileManager> profile_manager_;
};
class TestAppWindowContents : public extensions::AppWindowContents {
public:
explicit TestAppWindowContents(content::WebContents* web_contents)
: web_contents_(web_contents) {}
// apps:AppWindowContents
void Initialize(content::BrowserContext* context, const GURL& url) override {}
void LoadContents(int32 creator_process_id) override {}
void NativeWindowChanged(
extensions::NativeAppWindow* native_app_window) override {}
void NativeWindowClosed() override {}
void DispatchWindowShownForTests() const override {}
content::WebContents* GetWebContents() const override {
return web_contents_.get();
}
private:
scoped_ptr<content::WebContents> web_contents_;
};
TEST_F(BrowserProcessPowerTest, NoSite) {
collector->UpdatePowerConsumptionForTesting();
EXPECT_EQ(0u, collector->metrics_map_for_testing()->size());
}
TEST_F(BrowserProcessPowerTest, OneSite) {
GURL url("http://www.google.com");
AddTab(browser(), url);
collector->UpdatePowerConsumptionForTesting();
ProcessPowerCollector::ProcessMetricsMap* metrics_map =
collector->metrics_map_for_testing();
EXPECT_EQ(1u, metrics_map->size());
// Create fake process numbers.
GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass());
OriginPowerMap* origin_power_map =
OriginPowerMapFactory::GetForBrowserContext(profile());
EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url));
collector->set_cpu_usage_callback_for_testing(
base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant,
base::Unretained(this),
5));
EXPECT_DOUBLE_EQ(5, collector->UpdatePowerConsumptionForTesting());
EXPECT_EQ(100, origin_power_map->GetPowerForOrigin(url));
}
TEST_F(BrowserProcessPowerTest, MultipleSites) {
Browser::CreateParams native_params(profile(),
chrome::HOST_DESKTOP_TYPE_NATIVE);
GURL url1("http://www.google.com");
GURL url2("http://www.example.com");
GURL url3("https://www.google.com");
scoped_ptr<Browser> browser2(
chrome::CreateBrowserWithTestWindowForParams(&native_params));
scoped_ptr<Browser> browser3(
chrome::CreateBrowserWithTestWindowForParams(&native_params));
AddTab(browser(), url1);
AddTab(browser2.get(), url2);
AddTab(browser3.get(), url3);
// Create fake process numbers.
GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass());
GetProcess(browser2.get())->SetProcessHandle(MakeProcessHandle(2).Pass());
GetProcess(browser3.get())->SetProcessHandle(MakeProcessHandle(3).Pass());
collector->UpdatePowerConsumptionForTesting();
ProcessPowerCollector::ProcessMetricsMap* metrics_map =
collector->metrics_map_for_testing();
EXPECT_EQ(3u, metrics_map->size());
// Since all handlers are uninitialized, this should be 0.
EXPECT_DOUBLE_EQ(0, collector->UpdatePowerConsumptionForTesting());
OriginPowerMap* origin_power_map =
OriginPowerMapFactory::GetForBrowserContext(profile());
EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url1));
EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url2));
EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url3));
collector->set_cpu_usage_callback_for_testing(
base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant,
base::Unretained(this),
5));
EXPECT_DOUBLE_EQ(15, collector->UpdatePowerConsumptionForTesting());
EXPECT_EQ(33, origin_power_map->GetPowerForOrigin(url1));
EXPECT_EQ(33, origin_power_map->GetPowerForOrigin(url2));
EXPECT_EQ(33, origin_power_map->GetPowerForOrigin(url3));
// Close some tabs and verify that they are removed from the metrics map.
chrome::CloseTab(browser2.get());
chrome::CloseTab(browser3.get());
collector->UpdatePowerConsumptionForTesting();
EXPECT_EQ(1u, metrics_map->size());
}
TEST_F(BrowserProcessPowerTest, IncognitoDoesntRecordPowerUsage) {
Browser::CreateParams native_params(profile()->GetOffTheRecordProfile(),
chrome::HOST_DESKTOP_TYPE_NATIVE);
scoped_ptr<Browser> incognito_browser(
chrome::CreateBrowserWithTestWindowForParams(&native_params));
GURL url("http://www.google.com");
AddTab(browser(), url);
GURL hidden_url("http://foo.com");
AddTab(incognito_browser.get(), hidden_url);
// Create fake process numbers.
GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass());
GetProcess(incognito_browser.get())
->SetProcessHandle(MakeProcessHandle(2).Pass());
EXPECT_DOUBLE_EQ(0, collector->UpdatePowerConsumptionForTesting());
ProcessPowerCollector::ProcessMetricsMap* metrics_map =
collector->metrics_map_for_testing();
EXPECT_EQ(1u, metrics_map->size());
OriginPowerMap* origin_power_map =
OriginPowerMapFactory::GetForBrowserContext(profile());
EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url));
collector->set_cpu_usage_callback_for_testing(
base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant,
base::Unretained(this),
5));
EXPECT_DOUBLE_EQ(5, collector->UpdatePowerConsumptionForTesting());
// Verify that the incognito data was not stored.
EXPECT_EQ(100, origin_power_map->GetPowerForOrigin(url));
EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(hidden_url));
chrome::CloseTab(incognito_browser.get());
}
TEST_F(BrowserProcessPowerTest, MultipleProfilesRecordSeparately) {
scoped_ptr<Profile> other_profile(CreateProfile());
Browser::CreateParams native_params(other_profile.get(),
chrome::HOST_DESKTOP_TYPE_NATIVE);
scoped_ptr<Browser> other_user(
chrome::CreateBrowserWithTestWindowForParams(&native_params));
GURL url("http://www.google.com");
AddTab(browser(), url);
GURL hidden_url("http://foo.com");
AddTab(other_user.get(), hidden_url);
// Create fake process numbers.
GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass());
GetProcess(other_user.get())->SetProcessHandle(MakeProcessHandle(2).Pass());
EXPECT_DOUBLE_EQ(0, collector->UpdatePowerConsumptionForTesting());
EXPECT_EQ(2u, collector->metrics_map_for_testing()->size());
collector->set_cpu_usage_callback_for_testing(
base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant,
base::Unretained(this),
5));
EXPECT_DOUBLE_EQ(10, collector->UpdatePowerConsumptionForTesting());
// profile() should have an entry for |url| but not |hidden_url|.
OriginPowerMap* origin_power_map_first =
OriginPowerMapFactory::GetForBrowserContext(profile());
EXPECT_EQ(100, origin_power_map_first->GetPowerForOrigin(url));
EXPECT_EQ(0, origin_power_map_first->GetPowerForOrigin(hidden_url));
// |other_profile| should have an entry for |hidden_url| but not |url|.
OriginPowerMap* origin_power_map_second =
OriginPowerMapFactory::GetForBrowserContext(other_profile.get());
EXPECT_EQ(0, origin_power_map_second->GetPowerForOrigin(url));
EXPECT_EQ(100, origin_power_map_second->GetPowerForOrigin(hidden_url));
// Clean up
chrome::CloseTab(other_user.get());
}
TEST_F(BrowserProcessPowerTest, AppsRecordPowerUsage) {
// Install an app (an extension*).
#if defined(OS_WIN)
base::FilePath extension_path(FILE_PATH_LITERAL("c:\\foo"));
#elif defined(OS_POSIX)
base::FilePath extension_path(FILE_PATH_LITERAL("/foo"));
#endif
base::DictionaryValue manifest;
manifest.SetString("name", "Fake Name");
manifest.SetString("version", "1");
std::string error;
char kTestAppId[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
scoped_refptr<extensions::Extension> extension(
extensions::Extension::Create(extension_path,
extensions::Manifest::INTERNAL,
manifest,
extensions::Extension::NO_FLAGS,
kTestAppId,
&error));
EXPECT_TRUE(extension.get()) << error;
Profile* current_profile =
profile_manager_->CreateTestingProfile("Test user");
GURL url("chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
extensions::AppWindow* window = new extensions::AppWindow(
current_profile, new ChromeAppDelegate(scoped_ptr<ScopedKeepAlive>()),
extension.get());
content::WebContents* web_contents(
content::WebContents::Create(content::WebContents::CreateParams(
current_profile,
content::SiteInstance::CreateForURL(current_profile, url))));
window->SetAppWindowContentsForTesting(
scoped_ptr<extensions::AppWindowContents>(
new TestAppWindowContents(web_contents)));
extensions::AppWindowRegistry* app_registry =
extensions::AppWindowRegistry::Get(current_profile);
app_registry->AddAppWindow(window);
collector->set_cpu_usage_callback_for_testing(
base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant,
base::Unretained(this),
5));
collector->UpdatePowerConsumptionForTesting();
EXPECT_EQ(1u, collector->metrics_map_for_testing()->size());
// Clear the AppWindowContents before trying to close.
window->SetAppWindowContentsForTesting(
scoped_ptr<extensions::AppWindowContents>());
window->OnNativeClose();
collector->UpdatePowerConsumptionForTesting();
EXPECT_EQ(0u, collector->metrics_map_for_testing()->size());
}