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/system/chromeos/power/tray_power.h" 6 7 #include "ash/ash_switches.h" 8 #include "ash/test/ash_test_base.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "chromeos/dbus/power_manager/power_supply_properties.pb.h" 11 #include "ui/message_center/fake_message_center.h" 12 13 using message_center::Notification; 14 using power_manager::PowerSupplyProperties; 15 16 namespace { 17 18 class MockMessageCenter : public message_center::FakeMessageCenter { 19 public: 20 MockMessageCenter() : add_count_(0), remove_count_(0) {} 21 virtual ~MockMessageCenter() {} 22 23 int add_count() const { return add_count_; } 24 int remove_count() const { return remove_count_; } 25 26 // message_center::FakeMessageCenter overrides: 27 virtual void AddNotification(scoped_ptr<Notification> notification) OVERRIDE { 28 add_count_++; 29 } 30 virtual void RemoveNotification(const std::string& id, bool by_user) 31 OVERRIDE { 32 remove_count_++; 33 } 34 35 private: 36 int add_count_; 37 int remove_count_; 38 39 DISALLOW_COPY_AND_ASSIGN(MockMessageCenter); 40 }; 41 42 } // namespace 43 44 namespace ash { 45 46 class TrayPowerTest : public test::AshTestBase { 47 public: 48 TrayPowerTest() {} 49 virtual ~TrayPowerTest() {} 50 51 MockMessageCenter* message_center() { return message_center_.get(); } 52 TrayPower* tray_power() { return tray_power_.get(); } 53 54 // test::AshTestBase::SetUp() overrides: 55 virtual void SetUp() OVERRIDE { 56 test::AshTestBase::SetUp(); 57 message_center_.reset(new MockMessageCenter()); 58 tray_power_.reset(new TrayPower(NULL, message_center_.get())); 59 } 60 61 virtual void TearDown() OVERRIDE { 62 tray_power_.reset(); 63 message_center_.reset(); 64 test::AshTestBase::TearDown(); 65 } 66 67 TrayPower::NotificationState notification_state() const { 68 return tray_power_->notification_state_; 69 } 70 71 bool MaybeShowUsbChargerNotification(const PowerSupplyProperties& proto) { 72 PowerStatus::Get()->SetProtoForTesting(proto); 73 return tray_power_->MaybeShowUsbChargerNotification(); 74 } 75 76 bool UpdateNotificationState(const PowerSupplyProperties& proto) { 77 PowerStatus::Get()->SetProtoForTesting(proto); 78 return tray_power_->UpdateNotificationState(); 79 } 80 81 void SetUsbChargerConnected(bool connected) { 82 tray_power_->usb_charger_was_connected_ = connected; 83 } 84 85 // Returns a discharging PowerSupplyProperties more appropriate for testing. 86 static PowerSupplyProperties DefaultPowerSupplyProperties() { 87 PowerSupplyProperties proto; 88 proto.set_external_power( 89 power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED); 90 proto.set_battery_state( 91 power_manager::PowerSupplyProperties_BatteryState_DISCHARGING); 92 proto.set_battery_percent(50.0); 93 proto.set_battery_time_to_empty_sec(3 * 60 * 60); 94 proto.set_battery_time_to_full_sec(2 * 60 * 60); 95 proto.set_is_calculating_battery_time(false); 96 return proto; 97 } 98 99 private: 100 scoped_ptr<MockMessageCenter> message_center_; 101 scoped_ptr<TrayPower> tray_power_; 102 103 DISALLOW_COPY_AND_ASSIGN(TrayPowerTest); 104 }; 105 106 TEST_F(TrayPowerTest, MaybeShowUsbChargerNotification) { 107 PowerSupplyProperties discharging = DefaultPowerSupplyProperties(); 108 EXPECT_FALSE(MaybeShowUsbChargerNotification(discharging)); 109 EXPECT_EQ(0, message_center()->add_count()); 110 EXPECT_EQ(0, message_center()->remove_count()); 111 112 // Notification shows when connecting a USB charger. 113 PowerSupplyProperties usb_connected = DefaultPowerSupplyProperties(); 114 usb_connected.set_external_power( 115 power_manager::PowerSupplyProperties_ExternalPower_USB); 116 EXPECT_TRUE(MaybeShowUsbChargerNotification(usb_connected)); 117 EXPECT_EQ(1, message_center()->add_count()); 118 EXPECT_EQ(0, message_center()->remove_count()); 119 120 // Change in charge does not trigger the notification again. 121 PowerSupplyProperties more_charge = DefaultPowerSupplyProperties(); 122 more_charge.set_external_power( 123 power_manager::PowerSupplyProperties_ExternalPower_USB); 124 more_charge.set_battery_time_to_full_sec(60 * 60); 125 more_charge.set_battery_percent(75.0); 126 SetUsbChargerConnected(true); 127 EXPECT_FALSE(MaybeShowUsbChargerNotification(more_charge)); 128 EXPECT_EQ(1, message_center()->add_count()); 129 EXPECT_EQ(0, message_center()->remove_count()); 130 131 // Disconnecting a USB charger with the notification showing should close 132 // the notification. 133 EXPECT_TRUE(MaybeShowUsbChargerNotification(discharging)); 134 EXPECT_EQ(1, message_center()->add_count()); 135 EXPECT_EQ(1, message_center()->remove_count()); 136 } 137 138 TEST_F(TrayPowerTest, UpdateNotificationState) { 139 // No notifications when no battery present. 140 PowerSupplyProperties no_battery = DefaultPowerSupplyProperties(); 141 no_battery.set_external_power( 142 power_manager::PowerSupplyProperties_ExternalPower_AC); 143 no_battery.set_battery_state( 144 power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT); 145 EXPECT_FALSE(UpdateNotificationState(no_battery)); 146 EXPECT_EQ(TrayPower::NOTIFICATION_NONE, notification_state()); 147 148 // No notification when calculating remaining battery time. 149 PowerSupplyProperties calculating = DefaultPowerSupplyProperties(); 150 calculating.set_is_calculating_battery_time(true); 151 EXPECT_FALSE(UpdateNotificationState(calculating)); 152 EXPECT_EQ(TrayPower::NOTIFICATION_NONE, notification_state()); 153 154 // No notification when charging. 155 PowerSupplyProperties charging = DefaultPowerSupplyProperties(); 156 charging.set_external_power( 157 power_manager::PowerSupplyProperties_ExternalPower_AC); 158 charging.set_battery_state( 159 power_manager::PowerSupplyProperties_BatteryState_CHARGING); 160 EXPECT_FALSE(UpdateNotificationState(charging)); 161 EXPECT_EQ(TrayPower::NOTIFICATION_NONE, notification_state()); 162 163 // When the rounded minutes-to-empty are above the threshold, no notification 164 // should be shown. 165 PowerSupplyProperties low = DefaultPowerSupplyProperties(); 166 low.set_battery_time_to_empty_sec(TrayPower::kLowPowerMinutes * 60 + 30); 167 EXPECT_FALSE(UpdateNotificationState(low)); 168 EXPECT_EQ(TrayPower::NOTIFICATION_NONE, notification_state()); 169 170 // When the rounded value matches the threshold, the notification should 171 // appear. 172 low.set_battery_time_to_empty_sec(TrayPower::kLowPowerMinutes * 60 + 29); 173 EXPECT_TRUE(UpdateNotificationState(low)); 174 EXPECT_EQ(TrayPower::NOTIFICATION_LOW_POWER, notification_state()); 175 176 // It should persist at lower values. 177 low.set_battery_time_to_empty_sec(TrayPower::kLowPowerMinutes * 60 - 20); 178 EXPECT_FALSE(UpdateNotificationState(low)); 179 EXPECT_EQ(TrayPower::NOTIFICATION_LOW_POWER, notification_state()); 180 181 // The critical low battery notification should be shown when the rounded 182 // value is at the lower threshold. 183 PowerSupplyProperties critical = DefaultPowerSupplyProperties(); 184 critical.set_battery_time_to_empty_sec(TrayPower::kCriticalMinutes * 60 + 29); 185 EXPECT_TRUE(UpdateNotificationState(critical)); 186 EXPECT_EQ(TrayPower::NOTIFICATION_CRITICAL, notification_state()); 187 188 // The notification should be dismissed when the no-warning threshold is 189 // reached. 190 PowerSupplyProperties safe = DefaultPowerSupplyProperties(); 191 safe.set_battery_time_to_empty_sec(TrayPower::kNoWarningMinutes * 60 - 29); 192 EXPECT_FALSE(UpdateNotificationState(safe)); 193 EXPECT_EQ(TrayPower::NOTIFICATION_NONE, notification_state()); 194 195 // Test that rounded percentages are used when a USB charger is connected. 196 PowerSupplyProperties low_usb = DefaultPowerSupplyProperties(); 197 low_usb.set_external_power( 198 power_manager::PowerSupplyProperties_ExternalPower_USB); 199 low_usb.set_battery_percent(TrayPower::kLowPowerPercentage + 0.5); 200 EXPECT_FALSE(UpdateNotificationState(low_usb)); 201 EXPECT_EQ(TrayPower::NOTIFICATION_NONE, notification_state()); 202 203 low_usb.set_battery_percent(TrayPower::kLowPowerPercentage + 0.49); 204 EXPECT_TRUE(UpdateNotificationState(low_usb)); 205 EXPECT_EQ(TrayPower::NOTIFICATION_LOW_POWER, notification_state()); 206 207 PowerSupplyProperties critical_usb = DefaultPowerSupplyProperties(); 208 critical_usb.set_external_power( 209 power_manager::PowerSupplyProperties_ExternalPower_USB); 210 critical_usb.set_battery_percent(TrayPower::kCriticalPercentage + 0.2); 211 EXPECT_TRUE(UpdateNotificationState(critical_usb)); 212 EXPECT_EQ(TrayPower::NOTIFICATION_CRITICAL, notification_state()); 213 214 PowerSupplyProperties safe_usb = DefaultPowerSupplyProperties(); 215 safe_usb.set_external_power( 216 power_manager::PowerSupplyProperties_ExternalPower_USB); 217 safe_usb.set_battery_percent(TrayPower::kNoWarningPercentage - 0.1); 218 EXPECT_FALSE(UpdateNotificationState(safe_usb)); 219 EXPECT_EQ(TrayPower::NOTIFICATION_NONE, notification_state()); 220 221 // A notification shouldn't be shown when we're in the full state with an 222 // original Spring charger connected: http://crbug.com/338376 223 PowerSupplyProperties spring = DefaultPowerSupplyProperties(); 224 spring.set_external_power(power_manager:: 225 PowerSupplyProperties_ExternalPower_ORIGINAL_SPRING_CHARGER); 226 spring.set_battery_state( 227 power_manager::PowerSupplyProperties_BatteryState_FULL); 228 spring.set_battery_time_to_empty_sec(0); 229 spring.set_battery_time_to_full_sec(0); 230 EXPECT_FALSE(UpdateNotificationState(spring)); 231 EXPECT_EQ(TrayPower::NOTIFICATION_NONE, notification_state()); 232 } 233 234 } // namespace ash 235