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