1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/power/process_power_collector.h" 6 7 #include "chrome/browser/apps/scoped_keep_alive.h" 8 #include "chrome/browser/profiles/profile_manager.h" 9 #include "chrome/browser/ui/apps/chrome_app_delegate.h" 10 #include "chrome/browser/ui/browser_commands.h" 11 #include "chrome/browser/ui/tabs/tab_strip_model.h" 12 #include "chrome/test/base/browser_with_test_window_test.h" 13 #include "chrome/test/base/testing_browser_process.h" 14 #include "chrome/test/base/testing_profile_manager.h" 15 #include "components/power/origin_power_map.h" 16 #include "components/power/origin_power_map_factory.h" 17 #include "content/public/browser/site_instance.h" 18 #include "content/public/test/browser_test_utils.h" 19 #include "content/public/test/mock_render_process_host.h" 20 #include "extensions/browser/app_window/app_window_client.h" 21 #include "extensions/browser/app_window/app_window_contents.h" 22 #include "extensions/browser/app_window/app_window_registry.h" 23 #include "extensions/browser/app_window/native_app_window.h" 24 #include "extensions/common/extension.h" 25 #include "testing/gtest/include/gtest/gtest.h" 26 #include "url/gurl.h" 27 28 #if defined(OS_CHROMEOS) 29 #include "chromeos/dbus/power_manager/power_supply_properties.pb.h" 30 #endif 31 32 using power::OriginPowerMap; 33 using power::OriginPowerMapFactory; 34 35 class BrowserProcessPowerTest : public BrowserWithTestWindowTest { 36 public: 37 BrowserProcessPowerTest() {} 38 virtual ~BrowserProcessPowerTest() {} 39 40 virtual void SetUp() OVERRIDE { 41 BrowserWithTestWindowTest::SetUp(); 42 collector.reset(new ProcessPowerCollector); 43 44 #if defined(OS_CHROMEOS) 45 power_manager::PowerSupplyProperties prop; 46 prop.set_external_power(power_manager::PowerSupplyProperties::AC); 47 prop.set_battery_state(power_manager::PowerSupplyProperties::DISCHARGING); 48 prop.set_battery_percent(20.00); 49 prop.set_battery_discharge_rate(1); 50 collector->PowerChanged(prop); 51 #endif 52 53 profile_manager_.reset( 54 new TestingProfileManager(TestingBrowserProcess::GetGlobal())); 55 ASSERT_TRUE(profile_manager_->SetUp()); 56 } 57 58 virtual void TearDown() OVERRIDE { 59 collector.reset(); 60 BrowserWithTestWindowTest::TearDown(); 61 } 62 63 // Mocks out CPU usage for all processes as |value| percent. 64 double ReturnCpuAsConstant(double value, base::ProcessHandle handle) { 65 return value; 66 } 67 68 protected: 69 content::MockRenderProcessHost* GetProcess(Browser* browser) { 70 return static_cast<content::MockRenderProcessHost*>( 71 browser->tab_strip_model() 72 ->GetActiveWebContents() 73 ->GetRenderViewHost() 74 ->GetProcess()); 75 } 76 77 scoped_ptr<base::ProcessHandle> MakeProcessHandle(int process_id) { 78 scoped_ptr<base::ProcessHandle> proc_handle(new base::ProcessHandle( 79 #if defined(OS_WIN) 80 reinterpret_cast<HANDLE>(process_id)) 81 #else 82 process_id) 83 #endif 84 ); 85 return proc_handle.Pass(); 86 } 87 88 scoped_ptr<ProcessPowerCollector> collector; 89 scoped_ptr<TestingProfileManager> profile_manager_; 90 }; 91 92 class TestAppWindowContents : public extensions::AppWindowContents { 93 public: 94 explicit TestAppWindowContents(content::WebContents* web_contents) 95 : web_contents_(web_contents) {} 96 97 // apps:AppWindowContents 98 virtual void Initialize(content::BrowserContext* context, 99 const GURL& url) OVERRIDE {} 100 virtual void LoadContents(int32 creator_process_id) OVERRIDE {} 101 virtual void NativeWindowChanged( 102 extensions::NativeAppWindow* native_app_window) OVERRIDE {} 103 virtual void NativeWindowClosed() OVERRIDE {} 104 virtual void DispatchWindowShownForTests() const OVERRIDE {} 105 virtual content::WebContents* GetWebContents() const OVERRIDE { 106 return web_contents_.get(); 107 } 108 109 private: 110 scoped_ptr<content::WebContents> web_contents_; 111 }; 112 113 TEST_F(BrowserProcessPowerTest, NoSite) { 114 collector->UpdatePowerConsumptionForTesting(); 115 EXPECT_EQ(0u, collector->metrics_map_for_testing()->size()); 116 } 117 118 TEST_F(BrowserProcessPowerTest, OneSite) { 119 GURL url("http://www.google.com"); 120 AddTab(browser(), url); 121 collector->UpdatePowerConsumptionForTesting(); 122 ProcessPowerCollector::ProcessMetricsMap* metrics_map = 123 collector->metrics_map_for_testing(); 124 EXPECT_EQ(1u, metrics_map->size()); 125 126 // Create fake process numbers. 127 GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass()); 128 129 OriginPowerMap* origin_power_map = 130 OriginPowerMapFactory::GetForBrowserContext(profile()); 131 EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url)); 132 133 collector->set_cpu_usage_callback_for_testing( 134 base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant, 135 base::Unretained(this), 136 5)); 137 EXPECT_DOUBLE_EQ(5, collector->UpdatePowerConsumptionForTesting()); 138 EXPECT_EQ(100, origin_power_map->GetPowerForOrigin(url)); 139 } 140 141 TEST_F(BrowserProcessPowerTest, MultipleSites) { 142 Browser::CreateParams native_params(profile(), 143 chrome::HOST_DESKTOP_TYPE_NATIVE); 144 GURL url1("http://www.google.com"); 145 GURL url2("http://www.example.com"); 146 GURL url3("https://www.google.com"); 147 scoped_ptr<Browser> browser2( 148 chrome::CreateBrowserWithTestWindowForParams(&native_params)); 149 scoped_ptr<Browser> browser3( 150 chrome::CreateBrowserWithTestWindowForParams(&native_params)); 151 AddTab(browser(), url1); 152 AddTab(browser2.get(), url2); 153 AddTab(browser3.get(), url3); 154 155 // Create fake process numbers. 156 GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass()); 157 GetProcess(browser2.get())->SetProcessHandle(MakeProcessHandle(2).Pass()); 158 GetProcess(browser3.get())->SetProcessHandle(MakeProcessHandle(3).Pass()); 159 160 collector->UpdatePowerConsumptionForTesting(); 161 ProcessPowerCollector::ProcessMetricsMap* metrics_map = 162 collector->metrics_map_for_testing(); 163 EXPECT_EQ(3u, metrics_map->size()); 164 165 // Since all handlers are uninitialized, this should be 0. 166 EXPECT_DOUBLE_EQ(0, collector->UpdatePowerConsumptionForTesting()); 167 OriginPowerMap* origin_power_map = 168 OriginPowerMapFactory::GetForBrowserContext(profile()); 169 EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url1)); 170 EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url2)); 171 EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url3)); 172 173 collector->set_cpu_usage_callback_for_testing( 174 base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant, 175 base::Unretained(this), 176 5)); 177 EXPECT_DOUBLE_EQ(15, collector->UpdatePowerConsumptionForTesting()); 178 EXPECT_EQ(33, origin_power_map->GetPowerForOrigin(url1)); 179 EXPECT_EQ(33, origin_power_map->GetPowerForOrigin(url2)); 180 EXPECT_EQ(33, origin_power_map->GetPowerForOrigin(url3)); 181 182 // Close some tabs and verify that they are removed from the metrics map. 183 chrome::CloseTab(browser2.get()); 184 chrome::CloseTab(browser3.get()); 185 186 collector->UpdatePowerConsumptionForTesting(); 187 EXPECT_EQ(1u, metrics_map->size()); 188 } 189 190 TEST_F(BrowserProcessPowerTest, IncognitoDoesntRecordPowerUsage) { 191 Browser::CreateParams native_params(profile()->GetOffTheRecordProfile(), 192 chrome::HOST_DESKTOP_TYPE_NATIVE); 193 scoped_ptr<Browser> incognito_browser( 194 chrome::CreateBrowserWithTestWindowForParams(&native_params)); 195 GURL url("http://www.google.com"); 196 AddTab(browser(), url); 197 198 GURL hidden_url("http://foo.com"); 199 AddTab(incognito_browser.get(), hidden_url); 200 201 // Create fake process numbers. 202 GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass()); 203 GetProcess(incognito_browser.get()) 204 ->SetProcessHandle(MakeProcessHandle(2).Pass()); 205 206 EXPECT_DOUBLE_EQ(0, collector->UpdatePowerConsumptionForTesting()); 207 ProcessPowerCollector::ProcessMetricsMap* metrics_map = 208 collector->metrics_map_for_testing(); 209 EXPECT_EQ(1u, metrics_map->size()); 210 211 OriginPowerMap* origin_power_map = 212 OriginPowerMapFactory::GetForBrowserContext(profile()); 213 EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(url)); 214 215 collector->set_cpu_usage_callback_for_testing( 216 base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant, 217 base::Unretained(this), 218 5)); 219 EXPECT_DOUBLE_EQ(5, collector->UpdatePowerConsumptionForTesting()); 220 221 // Verify that the incognito data was not stored. 222 EXPECT_EQ(100, origin_power_map->GetPowerForOrigin(url)); 223 EXPECT_EQ(0, origin_power_map->GetPowerForOrigin(hidden_url)); 224 225 chrome::CloseTab(incognito_browser.get()); 226 } 227 228 TEST_F(BrowserProcessPowerTest, MultipleProfilesRecordSeparately) { 229 scoped_ptr<Profile> other_profile(CreateProfile()); 230 Browser::CreateParams native_params(other_profile.get(), 231 chrome::HOST_DESKTOP_TYPE_NATIVE); 232 scoped_ptr<Browser> other_user( 233 chrome::CreateBrowserWithTestWindowForParams(&native_params)); 234 235 GURL url("http://www.google.com"); 236 AddTab(browser(), url); 237 238 GURL hidden_url("http://foo.com"); 239 AddTab(other_user.get(), hidden_url); 240 241 // Create fake process numbers. 242 GetProcess(browser())->SetProcessHandle(MakeProcessHandle(1).Pass()); 243 GetProcess(other_user.get())->SetProcessHandle(MakeProcessHandle(2).Pass()); 244 245 EXPECT_DOUBLE_EQ(0, collector->UpdatePowerConsumptionForTesting()); 246 EXPECT_EQ(2u, collector->metrics_map_for_testing()->size()); 247 248 collector->set_cpu_usage_callback_for_testing( 249 base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant, 250 base::Unretained(this), 251 5)); 252 EXPECT_DOUBLE_EQ(10, collector->UpdatePowerConsumptionForTesting()); 253 254 // profile() should have an entry for |url| but not |hidden_url|. 255 OriginPowerMap* origin_power_map_first = 256 OriginPowerMapFactory::GetForBrowserContext(profile()); 257 EXPECT_EQ(100, origin_power_map_first->GetPowerForOrigin(url)); 258 EXPECT_EQ(0, origin_power_map_first->GetPowerForOrigin(hidden_url)); 259 260 // |other_profile| should have an entry for |hidden_url| but not |url|. 261 OriginPowerMap* origin_power_map_second = 262 OriginPowerMapFactory::GetForBrowserContext(other_profile.get()); 263 EXPECT_EQ(0, origin_power_map_second->GetPowerForOrigin(url)); 264 EXPECT_EQ(100, origin_power_map_second->GetPowerForOrigin(hidden_url)); 265 266 // Clean up 267 chrome::CloseTab(other_user.get()); 268 } 269 270 TEST_F(BrowserProcessPowerTest, AppsRecordPowerUsage) { 271 // Install an app (an extension*). 272 #if defined(OS_WIN) 273 base::FilePath extension_path(FILE_PATH_LITERAL("c:\\foo")); 274 #elif defined(OS_POSIX) 275 base::FilePath extension_path(FILE_PATH_LITERAL("/foo")); 276 #endif 277 base::DictionaryValue manifest; 278 manifest.SetString("name", "Fake Name"); 279 manifest.SetString("version", "1"); 280 std::string error; 281 char kTestAppId[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 282 scoped_refptr<extensions::Extension> extension( 283 extensions::Extension::Create(extension_path, 284 extensions::Manifest::INTERNAL, 285 manifest, 286 extensions::Extension::NO_FLAGS, 287 kTestAppId, 288 &error)); 289 EXPECT_TRUE(extension.get()) << error; 290 291 Profile* current_profile = 292 profile_manager_->CreateTestingProfile("Test user"); 293 GURL url("chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); 294 extensions::AppWindow* window = new extensions::AppWindow( 295 current_profile, new ChromeAppDelegate(scoped_ptr<ScopedKeepAlive>()), 296 extension.get()); 297 content::WebContents* web_contents( 298 content::WebContents::Create(content::WebContents::CreateParams( 299 current_profile, 300 content::SiteInstance::CreateForURL(current_profile, url)))); 301 window->SetAppWindowContentsForTesting( 302 scoped_ptr<extensions::AppWindowContents>( 303 new TestAppWindowContents(web_contents))); 304 extensions::AppWindowRegistry* app_registry = 305 extensions::AppWindowRegistry::Get(current_profile); 306 app_registry->AddAppWindow(window); 307 308 collector->set_cpu_usage_callback_for_testing( 309 base::Bind(&BrowserProcessPowerTest::ReturnCpuAsConstant, 310 base::Unretained(this), 311 5)); 312 collector->UpdatePowerConsumptionForTesting(); 313 EXPECT_EQ(1u, collector->metrics_map_for_testing()->size()); 314 315 // Clear the AppWindowContents before trying to close. 316 window->SetAppWindowContentsForTesting( 317 scoped_ptr<extensions::AppWindowContents>()); 318 window->OnNativeClose(); 319 collector->UpdatePowerConsumptionForTesting(); 320 EXPECT_EQ(0u, collector->metrics_map_for_testing()->size()); 321 } 322