| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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 "ash/common/system/chromeos/power/tray_power.h" | |
| 6 | |
| 7 #include <map> | |
| 8 #include <memory> | |
| 9 #include <string> | |
| 10 | |
| 11 #include "ash/test/ash_test_base.h" | |
| 12 #include "chromeos/dbus/power_manager/power_supply_properties.pb.h" | |
| 13 #include "ui/message_center/fake_message_center.h" | |
| 14 | |
| 15 using message_center::Notification; | |
| 16 using power_manager::PowerSupplyProperties; | |
| 17 | |
| 18 namespace { | |
| 19 | |
| 20 class MockMessageCenter : public message_center::FakeMessageCenter { | |
| 21 public: | |
| 22 MockMessageCenter() : add_count_(0), remove_count_(0), update_count_(0) {} | |
| 23 ~MockMessageCenter() override {} | |
| 24 | |
| 25 int add_count() const { return add_count_; } | |
| 26 int remove_count() const { return remove_count_; } | |
| 27 int update_count() const { return update_count_; } | |
| 28 | |
| 29 // message_center::FakeMessageCenter overrides: | |
| 30 void AddNotification(std::unique_ptr<Notification> notification) override { | |
| 31 add_count_++; | |
| 32 notifications_.insert( | |
| 33 std::make_pair(notification->id(), std::move(notification))); | |
| 34 } | |
| 35 void RemoveNotification(const std::string& id, bool by_user) override { | |
| 36 Notification* notification = FindVisibleNotificationById(id); | |
| 37 if (notification && notification->delegate()) | |
| 38 notification->delegate()->Close(by_user); | |
| 39 remove_count_++; | |
| 40 notifications_.erase(id); | |
| 41 } | |
| 42 void UpdateNotification( | |
| 43 const std::string& id, | |
| 44 std::unique_ptr<Notification> new_notification) override { | |
| 45 update_count_++; | |
| 46 Notification* notification = FindVisibleNotificationById(id); | |
| 47 if (notification) | |
| 48 notifications_.erase(id); | |
| 49 notifications_.insert( | |
| 50 std::make_pair(new_notification->id(), std::move(new_notification))); | |
| 51 } | |
| 52 | |
| 53 Notification* FindVisibleNotificationById(const std::string& id) override { | |
| 54 auto it = notifications_.find(id); | |
| 55 return it == notifications_.end() ? NULL : it->second.get(); | |
| 56 } | |
| 57 | |
| 58 private: | |
| 59 int add_count_; | |
| 60 int remove_count_; | |
| 61 int update_count_; | |
| 62 std::map<std::string, std::unique_ptr<Notification>> notifications_; | |
| 63 | |
| 64 DISALLOW_COPY_AND_ASSIGN(MockMessageCenter); | |
| 65 }; | |
| 66 | |
| 67 } // namespace | |
| 68 | |
| 69 namespace ash { | |
| 70 | |
| 71 class TrayPowerTest : public test::AshTestBase { | |
| 72 public: | |
| 73 TrayPowerTest() {} | |
| 74 ~TrayPowerTest() override {} | |
| 75 | |
| 76 MockMessageCenter* message_center() { return message_center_.get(); } | |
| 77 TrayPower* tray_power() { return tray_power_.get(); } | |
| 78 | |
| 79 // test::AshTestBase::SetUp() overrides: | |
| 80 void SetUp() override { | |
| 81 test::AshTestBase::SetUp(); | |
| 82 message_center_.reset(new MockMessageCenter()); | |
| 83 tray_power_.reset(new TrayPower(NULL, message_center_.get())); | |
| 84 } | |
| 85 | |
| 86 void TearDown() override { | |
| 87 tray_power_.reset(); | |
| 88 message_center_.reset(); | |
| 89 test::AshTestBase::TearDown(); | |
| 90 } | |
| 91 | |
| 92 TrayPower::NotificationState notification_state() const { | |
| 93 return tray_power_->notification_state_; | |
| 94 } | |
| 95 | |
| 96 bool MaybeShowUsbChargerNotification(const PowerSupplyProperties& proto) { | |
| 97 PowerStatus::Get()->SetProtoForTesting(proto); | |
| 98 return tray_power_->MaybeShowUsbChargerNotification(); | |
| 99 } | |
| 100 | |
| 101 void MaybeShowDualRoleNotification(const PowerSupplyProperties& proto) { | |
| 102 PowerStatus::Get()->SetProtoForTesting(proto); | |
| 103 tray_power_->MaybeShowDualRoleNotification(); | |
| 104 } | |
| 105 | |
| 106 void UpdateNotificationState(const PowerSupplyProperties& proto, | |
| 107 TrayPower::NotificationState expected_state, | |
| 108 bool expected_add, | |
| 109 bool expected_remove) { | |
| 110 int prev_add = message_center_->add_count(); | |
| 111 int prev_remove = message_center_->remove_count(); | |
| 112 PowerStatus::Get()->SetProtoForTesting(proto); | |
| 113 tray_power_->OnPowerStatusChanged(); | |
| 114 EXPECT_EQ(expected_state, notification_state()); | |
| 115 EXPECT_EQ(expected_add, message_center_->add_count() == prev_add + 1); | |
| 116 EXPECT_EQ(expected_remove, | |
| 117 message_center_->remove_count() == prev_remove + 1); | |
| 118 } | |
| 119 | |
| 120 void SetUsbChargerConnected(bool connected) { | |
| 121 tray_power_->usb_charger_was_connected_ = connected; | |
| 122 } | |
| 123 | |
| 124 // Returns a discharging PowerSupplyProperties more appropriate for testing. | |
| 125 static PowerSupplyProperties DefaultPowerSupplyProperties() { | |
| 126 PowerSupplyProperties proto; | |
| 127 proto.set_external_power( | |
| 128 power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED); | |
| 129 proto.set_battery_state( | |
| 130 power_manager::PowerSupplyProperties_BatteryState_DISCHARGING); | |
| 131 proto.set_battery_percent(50.0); | |
| 132 proto.set_battery_time_to_empty_sec(3 * 60 * 60); | |
| 133 proto.set_battery_time_to_full_sec(2 * 60 * 60); | |
| 134 proto.set_is_calculating_battery_time(false); | |
| 135 return proto; | |
| 136 } | |
| 137 | |
| 138 private: | |
| 139 std::unique_ptr<MockMessageCenter> message_center_; | |
| 140 std::unique_ptr<TrayPower> tray_power_; | |
| 141 | |
| 142 DISALLOW_COPY_AND_ASSIGN(TrayPowerTest); | |
| 143 }; | |
| 144 | |
| 145 TEST_F(TrayPowerTest, MaybeShowUsbChargerNotification) { | |
| 146 PowerSupplyProperties discharging = DefaultPowerSupplyProperties(); | |
| 147 EXPECT_FALSE(MaybeShowUsbChargerNotification(discharging)); | |
| 148 EXPECT_EQ(0, message_center()->add_count()); | |
| 149 EXPECT_EQ(0, message_center()->remove_count()); | |
| 150 | |
| 151 // Notification shows when connecting a USB charger. | |
| 152 PowerSupplyProperties usb_connected = DefaultPowerSupplyProperties(); | |
| 153 usb_connected.set_external_power( | |
| 154 power_manager::PowerSupplyProperties_ExternalPower_USB); | |
| 155 EXPECT_TRUE(MaybeShowUsbChargerNotification(usb_connected)); | |
| 156 EXPECT_EQ(1, message_center()->add_count()); | |
| 157 EXPECT_EQ(0, message_center()->remove_count()); | |
| 158 SetUsbChargerConnected(true); | |
| 159 | |
| 160 // Change in charge does not trigger the notification again. | |
| 161 PowerSupplyProperties more_charge = DefaultPowerSupplyProperties(); | |
| 162 more_charge.set_external_power( | |
| 163 power_manager::PowerSupplyProperties_ExternalPower_USB); | |
| 164 more_charge.set_battery_time_to_full_sec(60 * 60); | |
| 165 more_charge.set_battery_percent(75.0); | |
| 166 EXPECT_FALSE(MaybeShowUsbChargerNotification(more_charge)); | |
| 167 EXPECT_EQ(1, message_center()->add_count()); | |
| 168 EXPECT_EQ(0, message_center()->remove_count()); | |
| 169 | |
| 170 // Disconnecting a USB charger with the notification showing should close | |
| 171 // the notification. | |
| 172 EXPECT_TRUE(MaybeShowUsbChargerNotification(discharging)); | |
| 173 EXPECT_EQ(1, message_center()->add_count()); | |
| 174 EXPECT_EQ(1, message_center()->remove_count()); | |
| 175 SetUsbChargerConnected(false); | |
| 176 | |
| 177 // Notification shows when connecting a USB charger again. | |
| 178 EXPECT_TRUE(MaybeShowUsbChargerNotification(usb_connected)); | |
| 179 EXPECT_EQ(2, message_center()->add_count()); | |
| 180 EXPECT_EQ(1, message_center()->remove_count()); | |
| 181 SetUsbChargerConnected(true); | |
| 182 | |
| 183 // Notification hides when external power switches to AC. | |
| 184 PowerSupplyProperties ac_charger = DefaultPowerSupplyProperties(); | |
| 185 ac_charger.set_external_power( | |
| 186 power_manager::PowerSupplyProperties_ExternalPower_AC); | |
| 187 EXPECT_TRUE(MaybeShowUsbChargerNotification(ac_charger)); | |
| 188 EXPECT_EQ(2, message_center()->add_count()); | |
| 189 EXPECT_EQ(2, message_center()->remove_count()); | |
| 190 SetUsbChargerConnected(false); | |
| 191 | |
| 192 // Notification shows when external power switches back to USB. | |
| 193 EXPECT_TRUE(MaybeShowUsbChargerNotification(usb_connected)); | |
| 194 EXPECT_EQ(3, message_center()->add_count()); | |
| 195 EXPECT_EQ(2, message_center()->remove_count()); | |
| 196 SetUsbChargerConnected(true); | |
| 197 | |
| 198 // Notification does not re-appear after being manually dismissed if | |
| 199 // power supply flickers between AC and USB charger. | |
| 200 message_center()->RemoveNotification(TrayPower::kUsbNotificationId, true); | |
| 201 EXPECT_EQ(3, message_center()->remove_count()); | |
| 202 EXPECT_TRUE(MaybeShowUsbChargerNotification(ac_charger)); | |
| 203 SetUsbChargerConnected(false); | |
| 204 EXPECT_FALSE(MaybeShowUsbChargerNotification(usb_connected)); | |
| 205 EXPECT_EQ(3, message_center()->add_count()); | |
| 206 SetUsbChargerConnected(true); | |
| 207 | |
| 208 // Notification appears again after being manually dismissed if the charger | |
| 209 // is removed, and then a USB charger is attached. | |
| 210 MaybeShowUsbChargerNotification(discharging); | |
| 211 EXPECT_EQ(3, message_center()->add_count()); | |
| 212 SetUsbChargerConnected(false); | |
| 213 MaybeShowUsbChargerNotification(usb_connected); | |
| 214 EXPECT_EQ(4, message_center()->add_count()); | |
| 215 SetUsbChargerConnected(true); | |
| 216 } | |
| 217 | |
| 218 TEST_F(TrayPowerTest, MaybeShowDualRoleNotification) { | |
| 219 PowerSupplyProperties discharging = DefaultPowerSupplyProperties(); | |
| 220 discharging.set_supports_dual_role_devices(true); | |
| 221 MaybeShowDualRoleNotification(discharging); | |
| 222 EXPECT_EQ(0, message_center()->add_count()); | |
| 223 EXPECT_EQ(0, message_center()->update_count()); | |
| 224 EXPECT_EQ(0, message_center()->remove_count()); | |
| 225 | |
| 226 // Notification shows when connecting a dual-role device. | |
| 227 PowerSupplyProperties dual_role = DefaultPowerSupplyProperties(); | |
| 228 dual_role.set_supports_dual_role_devices(true); | |
| 229 power_manager::PowerSupplyProperties_PowerSource* source = | |
| 230 dual_role.add_available_external_power_source(); | |
| 231 source->set_id("dual-role1"); | |
| 232 source->set_active_by_default(false); | |
| 233 MaybeShowDualRoleNotification(dual_role); | |
| 234 EXPECT_EQ(1, message_center()->add_count()); | |
| 235 EXPECT_EQ(0, message_center()->update_count()); | |
| 236 EXPECT_EQ(0, message_center()->remove_count()); | |
| 237 | |
| 238 // Connecting another dual-role device updates the notification to be plural. | |
| 239 source = dual_role.add_available_external_power_source(); | |
| 240 source->set_id("dual-role2"); | |
| 241 source->set_active_by_default(false); | |
| 242 MaybeShowDualRoleNotification(dual_role); | |
| 243 EXPECT_EQ(1, message_center()->add_count()); | |
| 244 EXPECT_EQ(1, message_center()->update_count()); | |
| 245 EXPECT_EQ(0, message_center()->remove_count()); | |
| 246 | |
| 247 // Connecting a 3rd dual-role device doesn't affect the notification. | |
| 248 source = dual_role.add_available_external_power_source(); | |
| 249 source->set_id("dual-role3"); | |
| 250 source->set_active_by_default(false); | |
| 251 MaybeShowDualRoleNotification(dual_role); | |
| 252 EXPECT_EQ(1, message_center()->add_count()); | |
| 253 EXPECT_EQ(1, message_center()->update_count()); | |
| 254 EXPECT_EQ(0, message_center()->remove_count()); | |
| 255 | |
| 256 // Connecting a legacy USB device removes the notification. | |
| 257 PowerSupplyProperties legacy(dual_role); | |
| 258 power_manager::PowerSupplyProperties_PowerSource* legacy_source = | |
| 259 legacy.add_available_external_power_source(); | |
| 260 legacy_source->set_id("legacy"); | |
| 261 legacy_source->set_active_by_default(true); | |
| 262 legacy.set_external_power_source_id("legacy"); | |
| 263 legacy.set_external_power( | |
| 264 power_manager::PowerSupplyProperties_ExternalPower_USB); | |
| 265 MaybeShowDualRoleNotification(legacy); | |
| 266 EXPECT_EQ(1, message_center()->add_count()); | |
| 267 EXPECT_EQ(1, message_center()->update_count()); | |
| 268 EXPECT_EQ(1, message_center()->remove_count()); | |
| 269 | |
| 270 // Removing the legacy USB device adds the notification again. | |
| 271 MaybeShowDualRoleNotification(dual_role); | |
| 272 EXPECT_EQ(2, message_center()->add_count()); | |
| 273 EXPECT_EQ(1, message_center()->update_count()); | |
| 274 EXPECT_EQ(1, message_center()->remove_count()); | |
| 275 | |
| 276 // Charging from the device updates the notification. | |
| 277 dual_role.set_external_power_source_id("dual-role1"); | |
| 278 dual_role.set_external_power( | |
| 279 power_manager::PowerSupplyProperties_ExternalPower_USB); | |
| 280 MaybeShowDualRoleNotification(dual_role); | |
| 281 EXPECT_EQ(2, message_center()->add_count()); | |
| 282 EXPECT_EQ(2, message_center()->update_count()); | |
| 283 EXPECT_EQ(1, message_center()->remove_count()); | |
| 284 | |
| 285 // Adding a device as a sink doesn't change the notification, because the | |
| 286 // notification exposes the source. | |
| 287 source = dual_role.add_available_external_power_source(); | |
| 288 source->set_active_by_default(false); | |
| 289 MaybeShowDualRoleNotification(dual_role); | |
| 290 EXPECT_EQ(2, message_center()->add_count()); | |
| 291 EXPECT_EQ(2, message_center()->update_count()); | |
| 292 EXPECT_EQ(1, message_center()->remove_count()); | |
| 293 | |
| 294 // Changing the source to a sink changes the notification. | |
| 295 dual_role.set_external_power_source_id(""); | |
| 296 dual_role.set_external_power( | |
| 297 power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED); | |
| 298 MaybeShowDualRoleNotification(dual_role); | |
| 299 EXPECT_EQ(2, message_center()->add_count()); | |
| 300 EXPECT_EQ(3, message_center()->update_count()); | |
| 301 EXPECT_EQ(1, message_center()->remove_count()); | |
| 302 | |
| 303 // An unrelated change has no effect. | |
| 304 dual_role.set_battery_time_to_empty_sec(2 * 60 * 60); | |
| 305 MaybeShowDualRoleNotification(dual_role); | |
| 306 EXPECT_EQ(2, message_center()->add_count()); | |
| 307 EXPECT_EQ(3, message_center()->update_count()); | |
| 308 EXPECT_EQ(1, message_center()->remove_count()); | |
| 309 | |
| 310 // Removing devices hides the notification. | |
| 311 MaybeShowDualRoleNotification(discharging); | |
| 312 EXPECT_EQ(2, message_center()->add_count()); | |
| 313 EXPECT_EQ(3, message_center()->update_count()); | |
| 314 EXPECT_EQ(2, message_center()->remove_count()); | |
| 315 } | |
| 316 | |
| 317 TEST_F(TrayPowerTest, UpdateNotificationState) { | |
| 318 // No notifications when no battery present. | |
| 319 PowerSupplyProperties no_battery = DefaultPowerSupplyProperties(); | |
| 320 no_battery.set_external_power( | |
| 321 power_manager::PowerSupplyProperties_ExternalPower_AC); | |
| 322 no_battery.set_battery_state( | |
| 323 power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT); | |
| 324 { | |
| 325 SCOPED_TRACE("No notifications when no battery present"); | |
| 326 UpdateNotificationState(no_battery, TrayPower::NOTIFICATION_NONE, false, | |
| 327 false); | |
| 328 } | |
| 329 | |
| 330 // No notification when calculating remaining battery time. | |
| 331 PowerSupplyProperties calculating = DefaultPowerSupplyProperties(); | |
| 332 calculating.set_is_calculating_battery_time(true); | |
| 333 { | |
| 334 SCOPED_TRACE("No notification when calculating remaining battery time"); | |
| 335 UpdateNotificationState(calculating, TrayPower::NOTIFICATION_NONE, false, | |
| 336 false); | |
| 337 } | |
| 338 | |
| 339 // No notification when charging. | |
| 340 PowerSupplyProperties charging = DefaultPowerSupplyProperties(); | |
| 341 charging.set_external_power( | |
| 342 power_manager::PowerSupplyProperties_ExternalPower_AC); | |
| 343 charging.set_battery_state( | |
| 344 power_manager::PowerSupplyProperties_BatteryState_CHARGING); | |
| 345 { | |
| 346 SCOPED_TRACE("No notification when charging"); | |
| 347 UpdateNotificationState(charging, TrayPower::NOTIFICATION_NONE, false, | |
| 348 false); | |
| 349 } | |
| 350 | |
| 351 // When the rounded minutes-to-empty are above the threshold, no notification | |
| 352 // should be shown. | |
| 353 PowerSupplyProperties low = DefaultPowerSupplyProperties(); | |
| 354 low.set_battery_time_to_empty_sec(TrayPower::kLowPowerMinutes * 60 + 30); | |
| 355 { | |
| 356 SCOPED_TRACE("No notification when time to empty above threshold"); | |
| 357 UpdateNotificationState(low, TrayPower::NOTIFICATION_NONE, false, false); | |
| 358 } | |
| 359 | |
| 360 // When the rounded value matches the threshold, the notification should | |
| 361 // appear. | |
| 362 low.set_battery_time_to_empty_sec(TrayPower::kLowPowerMinutes * 60 + 29); | |
| 363 { | |
| 364 SCOPED_TRACE("Notification when time to empty matches threshold"); | |
| 365 UpdateNotificationState(low, TrayPower::NOTIFICATION_LOW_POWER, true, | |
| 366 false); | |
| 367 } | |
| 368 | |
| 369 // It should persist at lower values. | |
| 370 low.set_battery_time_to_empty_sec(TrayPower::kLowPowerMinutes * 60 - 20); | |
| 371 { | |
| 372 SCOPED_TRACE("Notification persists at lower values"); | |
| 373 UpdateNotificationState(low, TrayPower::NOTIFICATION_LOW_POWER, false, | |
| 374 false); | |
| 375 } | |
| 376 | |
| 377 // The critical low battery notification should be shown when the rounded | |
| 378 // value is at the lower threshold. | |
| 379 PowerSupplyProperties critical = DefaultPowerSupplyProperties(); | |
| 380 critical.set_battery_time_to_empty_sec(TrayPower::kCriticalMinutes * 60 + 29); | |
| 381 { | |
| 382 SCOPED_TRACE("Critical notification when time to empty is critical"); | |
| 383 UpdateNotificationState(critical, TrayPower::NOTIFICATION_CRITICAL, true, | |
| 384 true); | |
| 385 } | |
| 386 | |
| 387 // The notification should be dismissed when the no-warning threshold is | |
| 388 // reached. | |
| 389 PowerSupplyProperties safe = DefaultPowerSupplyProperties(); | |
| 390 safe.set_battery_time_to_empty_sec(TrayPower::kNoWarningMinutes * 60 - 29); | |
| 391 { | |
| 392 SCOPED_TRACE("Notification removed when battery not low"); | |
| 393 UpdateNotificationState(safe, TrayPower::NOTIFICATION_NONE, false, true); | |
| 394 } | |
| 395 | |
| 396 // Test that rounded percentages are used when a USB charger is connected. | |
| 397 PowerSupplyProperties low_usb = DefaultPowerSupplyProperties(); | |
| 398 low_usb.set_external_power( | |
| 399 power_manager::PowerSupplyProperties_ExternalPower_USB); | |
| 400 low_usb.set_battery_percent(TrayPower::kLowPowerPercentage + 0.5); | |
| 401 { | |
| 402 SCOPED_TRACE("No notification for rounded battery percent"); | |
| 403 UpdateNotificationState(low_usb, TrayPower::NOTIFICATION_NONE, true, false); | |
| 404 } | |
| 405 | |
| 406 low_usb.set_battery_percent(TrayPower::kLowPowerPercentage + 0.49); | |
| 407 { | |
| 408 SCOPED_TRACE("Notification for rounded low power percent"); | |
| 409 UpdateNotificationState(low_usb, TrayPower::NOTIFICATION_LOW_POWER, true, | |
| 410 false); | |
| 411 } | |
| 412 | |
| 413 PowerSupplyProperties critical_usb = DefaultPowerSupplyProperties(); | |
| 414 critical_usb.set_external_power( | |
| 415 power_manager::PowerSupplyProperties_ExternalPower_USB); | |
| 416 critical_usb.set_battery_percent(TrayPower::kCriticalPercentage + 0.2); | |
| 417 { | |
| 418 SCOPED_TRACE("Notification for rounded critical power percent"); | |
| 419 UpdateNotificationState(critical_usb, TrayPower::NOTIFICATION_CRITICAL, | |
| 420 true, true); | |
| 421 } | |
| 422 | |
| 423 PowerSupplyProperties safe_usb = DefaultPowerSupplyProperties(); | |
| 424 safe_usb.set_external_power( | |
| 425 power_manager::PowerSupplyProperties_ExternalPower_USB); | |
| 426 safe_usb.set_battery_percent(TrayPower::kNoWarningPercentage - 0.1); | |
| 427 { | |
| 428 SCOPED_TRACE("Notification removed for rounded percent above threshold"); | |
| 429 UpdateNotificationState(safe_usb, TrayPower::NOTIFICATION_NONE, false, | |
| 430 true); | |
| 431 } | |
| 432 } | |
| 433 | |
| 434 } // namespace ash | |
| OLD | NEW |