// 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/mock_render_process_host.h" #include "extensions/browser/app_window/app_window_client.h" #include "extensions/browser/app_window/app_window_registry.h" #include "extensions/browser/app_window/native_app_window.h" #include "extensions/browser/app_window/test_app_window_contents.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( browser->tab_strip_model() ->GetActiveWebContents() ->GetRenderViewHost() ->GetProcess()); } scoped_ptr MakeProcessHandle(int process_id) { scoped_ptr proc_handle(new base::ProcessHandle( #if defined(OS_WIN) reinterpret_cast(process_id)) #else process_id) #endif ); return proc_handle.Pass(); } scoped_ptr collector; scoped_ptr profile_manager_; }; 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 browser2( chrome::CreateBrowserWithTestWindowForParams(&native_params)); scoped_ptr 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 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 other_profile(CreateProfile()); Browser::CreateParams native_params(other_profile.get(), chrome::HOST_DESKTOP_TYPE_NATIVE); scoped_ptr 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 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()), extension.get()); content::WebContents* web_contents( content::WebContents::Create(content::WebContents::CreateParams( current_profile, content::SiteInstance::CreateForURL(current_profile, url)))); window->SetAppWindowContentsForTesting( scoped_ptr( new extensions::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()); window->OnNativeClose(); collector->UpdatePowerConsumptionForTesting(); EXPECT_EQ(0u, collector->metrics_map_for_testing()->size()); }