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 |