1#!/usr/bin/env python
2# Copyright 2014 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""
7Unit tests for the contents of battery_utils.py
8"""
9
10# pylint: disable=protected-access,unused-argument
11
12import logging
13import unittest
14
15from devil import devil_env
16from devil.android import battery_utils
17from devil.android import device_errors
18from devil.android import device_utils
19from devil.android import device_utils_test
20from devil.utils import mock_calls
21
22with devil_env.SysPath(devil_env.PYMOCK_PATH):
23  import mock  # pylint: disable=import-error
24
25_DUMPSYS_OUTPUT = [
26    '9,0,i,uid,1000,test_package1',
27    '9,0,i,uid,1001,test_package2',
28    '9,1000,l,pwi,uid,1',
29    '9,1001,l,pwi,uid,2',
30    '9,0,l,pws,1728,2000,190,207',
31]
32
33
34class BatteryUtilsTest(mock_calls.TestCase):
35
36  _NEXUS_5 = {
37    'name': 'Nexus 5',
38    'witness_file': '/sys/kernel/debug/bq24192/INPUT_SRC_CONT',
39    'enable_command': (
40        'echo 0x4A > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
41        'echo 1 > /sys/class/power_supply/usb/online'),
42    'disable_command': (
43        'echo 0xCA > /sys/kernel/debug/bq24192/INPUT_SRC_CONT && '
44        'chmod 644 /sys/class/power_supply/usb/online && '
45        'echo 0 > /sys/class/power_supply/usb/online'),
46    'charge_counter': None,
47    'voltage': None,
48    'current': None,
49  }
50
51  _NEXUS_6 = {
52    'name': 'Nexus 6',
53    'witness_file': None,
54    'enable_command': None,
55    'disable_command': None,
56    'charge_counter': (
57        '/sys/class/power_supply/max170xx_battery/charge_counter_ext'),
58    'voltage': '/sys/class/power_supply/max170xx_battery/voltage_now',
59    'current': '/sys/class/power_supply/max170xx_battery/current_now',
60  }
61
62  _NEXUS_10 = {
63    'name': 'Nexus 10',
64    'witness_file': None,
65    'enable_command': None,
66    'disable_command': None,
67    'charge_counter': (
68        '/sys/class/power_supply/ds2784-fuelgauge/charge_counter_ext'),
69    'voltage': '/sys/class/power_supply/ds2784-fuelgauge/voltage_now',
70    'current': '/sys/class/power_supply/ds2784-fuelgauge/current_now',
71  }
72
73  def ShellError(self, output=None, status=1):
74    def action(cmd, *args, **kwargs):
75      raise device_errors.AdbShellCommandFailedError(
76          cmd, output, status, str(self.device))
77    if output is None:
78      output = 'Permission denied\n'
79    return action
80
81  def setUp(self):
82    self.adb = device_utils_test._AdbWrapperMock('0123456789abcdef')
83    self.device = device_utils.DeviceUtils(
84        self.adb, default_timeout=10, default_retries=0)
85    self.watchMethodCalls(self.call.adb, ignore=['GetDeviceSerial'])
86    self.battery = battery_utils.BatteryUtils(
87        self.device, default_timeout=10, default_retries=0)
88
89
90class BatteryUtilsInitTest(unittest.TestCase):
91
92  def testInitWithDeviceUtil(self):
93    serial = '0fedcba987654321'
94    d = device_utils.DeviceUtils(serial)
95    b = battery_utils.BatteryUtils(d)
96    self.assertEqual(d, b._device)
97
98  def testInitWithMissing_fails(self):
99    with self.assertRaises(TypeError):
100      battery_utils.BatteryUtils(None)
101    with self.assertRaises(TypeError):
102      battery_utils.BatteryUtils('')
103
104
105class BatteryUtilsSetChargingTest(BatteryUtilsTest):
106
107  @mock.patch('time.sleep', mock.Mock())
108  def testHardwareSetCharging_enabled(self):
109    self.battery._cache['profile'] = self._NEXUS_5
110    with self.assertCalls(
111        (self.call.device.RunShellCommand(
112            mock.ANY, shell=True, check_return=True, as_root=True,
113            large_output=True), []),
114        (self.call.battery.GetCharging(), False),
115        (self.call.battery.GetCharging(), True)):
116      self.battery._HardwareSetCharging(True)
117
118  def testHardwareSetCharging_alreadyEnabled(self):
119    self.battery._cache['profile'] = self._NEXUS_5
120    with self.assertCalls(
121        (self.call.device.RunShellCommand(
122            mock.ANY, shell=True, check_return=True, as_root=True,
123            large_output=True), []),
124        (self.call.battery.GetCharging(), True)):
125      self.battery._HardwareSetCharging(True)
126
127  @mock.patch('time.sleep', mock.Mock())
128  def testHardwareSetCharging_disabled(self):
129    self.battery._cache['profile'] = self._NEXUS_5
130    with self.assertCalls(
131        (self.call.device.RunShellCommand(
132            mock.ANY, shell=True, check_return=True, as_root=True,
133            large_output=True), []),
134        (self.call.battery.GetCharging(), True),
135        (self.call.battery.GetCharging(), False)):
136      self.battery._HardwareSetCharging(False)
137
138
139class BatteryUtilsSetBatteryMeasurementTest(BatteryUtilsTest):
140
141  @mock.patch('time.sleep', mock.Mock())
142  def testBatteryMeasurementWifi(self):
143    with self.patch_call(self.call.device.build_version_sdk,
144                         return_value=22):
145      with self.assertCalls(
146          (self.call.battery._ClearPowerData(), True),
147          (self.call.device.RunShellCommand(
148              ['dumpsys', 'battery', 'set', 'ac', '0'], check_return=True), []),
149          (self.call.device.RunShellCommand(
150              ['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True),
151           []),
152          (self.call.battery.GetCharging(), False),
153          (self.call.device.RunShellCommand(
154              ['dumpsys', 'battery', 'reset'], check_return=True), []),
155          (self.call.battery.GetCharging(), False),
156          (self.call.device.RunShellCommand(
157              ['dumpsys', 'battery'], check_return=True), ['UPDATES STOPPED']),
158          (self.call.battery.GetCharging(), False),
159          (self.call.device.RunShellCommand(
160              ['dumpsys', 'battery'], check_return=True), [])):
161        with self.battery.BatteryMeasurement():
162          pass
163
164  @mock.patch('time.sleep', mock.Mock())
165  def testBatteryMeasurementUsb(self):
166    with self.patch_call(self.call.device.build_version_sdk,
167                         return_value=22):
168      with self.assertCalls(
169          (self.call.battery._ClearPowerData(), True),
170          (self.call.device.RunShellCommand(
171              ['dumpsys', 'battery', 'set', 'ac', '0'], check_return=True), []),
172          (self.call.device.RunShellCommand(
173              ['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True),
174           []),
175          (self.call.battery.GetCharging(), False),
176          (self.call.device.RunShellCommand(
177              ['dumpsys', 'battery', 'reset'], check_return=True), []),
178          (self.call.battery.GetCharging(), False),
179          (self.call.device.RunShellCommand(
180              ['dumpsys', 'battery'], check_return=True), ['UPDATES STOPPED']),
181          (self.call.battery.GetCharging(), True)):
182        with self.battery.BatteryMeasurement():
183          pass
184
185
186class BatteryUtilsGetPowerData(BatteryUtilsTest):
187
188  def testGetPowerData(self):
189    with self.assertCalls(
190        (self.call.device.RunShellCommand(
191            ['dumpsys', 'batterystats', '-c'],
192            check_return=True, large_output=True),
193         _DUMPSYS_OUTPUT)):
194      data = self.battery.GetPowerData()
195      check = {
196        'system_total': 2000.0,
197        'per_package': {
198          'test_package1': {'uid': '1000', 'data': [1.0]},
199          'test_package2': {'uid': '1001', 'data': [2.0]}
200        }
201      }
202      self.assertEqual(data, check)
203
204  def testGetPowerData_packageCollisionSame(self):
205    self.battery._cache['uids'] = {'test_package1': '1000'}
206    with self.assertCall(
207        self.call.device.RunShellCommand(
208            ['dumpsys', 'batterystats', '-c'],
209            check_return=True, large_output=True),
210        _DUMPSYS_OUTPUT):
211      data = self.battery.GetPowerData()
212      check = {
213        'system_total': 2000.0,
214        'per_package': {
215          'test_package1': {'uid': '1000', 'data': [1.0]},
216          'test_package2': {'uid': '1001', 'data': [2.0]}
217        }
218      }
219      self.assertEqual(data, check)
220
221  def testGetPowerData_packageCollisionDifferent(self):
222    self.battery._cache['uids'] = {'test_package1': '1'}
223    with self.assertCall(
224        self.call.device.RunShellCommand(
225            ['dumpsys', 'batterystats', '-c'],
226            check_return=True, large_output=True),
227        _DUMPSYS_OUTPUT):
228      with self.assertRaises(device_errors.CommandFailedError):
229        self.battery.GetPowerData()
230
231  def testGetPowerData_cacheCleared(self):
232    with self.assertCalls(
233        (self.call.device.RunShellCommand(
234            ['dumpsys', 'batterystats', '-c'],
235            check_return=True, large_output=True),
236         _DUMPSYS_OUTPUT)):
237      self.battery._cache.clear()
238      data = self.battery.GetPowerData()
239      check = {
240        'system_total': 2000.0,
241        'per_package': {
242          'test_package1': {'uid': '1000', 'data': [1.0]},
243          'test_package2': {'uid': '1001', 'data': [2.0]}
244        }
245      }
246      self.assertEqual(data, check)
247
248
249class BatteryUtilsChargeDevice(BatteryUtilsTest):
250
251  @mock.patch('time.sleep', mock.Mock())
252  def testChargeDeviceToLevel_pass(self):
253    with self.assertCalls(
254        (self.call.battery.SetCharging(True)),
255        (self.call.battery.GetBatteryInfo(), {'level': '50'}),
256        (self.call.battery.GetBatteryInfo(), {'level': '100'})):
257      self.battery.ChargeDeviceToLevel(95)
258
259  @mock.patch('time.sleep', mock.Mock())
260  def testChargeDeviceToLevel_failureSame(self):
261    with self.assertCalls(
262        (self.call.battery.SetCharging(True)),
263        (self.call.battery.GetBatteryInfo(), {'level': '50'}),
264        (self.call.battery.GetBatteryInfo(), {'level': '50'}),
265
266        (self.call.battery.GetBatteryInfo(), {'level': '50'})):
267      with self.assertRaises(device_errors.DeviceChargingError):
268        old_max = battery_utils._MAX_CHARGE_ERROR
269        try:
270          battery_utils._MAX_CHARGE_ERROR = 2
271          self.battery.ChargeDeviceToLevel(95)
272        finally:
273          battery_utils._MAX_CHARGE_ERROR = old_max
274
275  @mock.patch('time.sleep', mock.Mock())
276  def testChargeDeviceToLevel_failureDischarge(self):
277    with self.assertCalls(
278        (self.call.battery.SetCharging(True)),
279        (self.call.battery.GetBatteryInfo(), {'level': '50'}),
280        (self.call.battery.GetBatteryInfo(), {'level': '49'}),
281        (self.call.battery.GetBatteryInfo(), {'level': '48'})):
282      with self.assertRaises(device_errors.DeviceChargingError):
283        old_max = battery_utils._MAX_CHARGE_ERROR
284        try:
285          battery_utils._MAX_CHARGE_ERROR = 2
286          self.battery.ChargeDeviceToLevel(95)
287        finally:
288          battery_utils._MAX_CHARGE_ERROR = old_max
289
290
291class BatteryUtilsDischargeDevice(BatteryUtilsTest):
292
293  @mock.patch('time.sleep', mock.Mock())
294  def testDischargeDevice_exact(self):
295    with self.assertCalls(
296        (self.call.battery.GetBatteryInfo(), {'level': '100'}),
297        (self.call.battery._HardwareSetCharging(False)),
298        (self.call.battery._HardwareSetCharging(True)),
299        (self.call.battery.GetBatteryInfo(), {'level': '99'})):
300      self.battery._DischargeDevice(1)
301
302  @mock.patch('time.sleep', mock.Mock())
303  def testDischargeDevice_over(self):
304    with self.assertCalls(
305        (self.call.battery.GetBatteryInfo(), {'level': '100'}),
306        (self.call.battery._HardwareSetCharging(False)),
307        (self.call.battery._HardwareSetCharging(True)),
308        (self.call.battery.GetBatteryInfo(), {'level': '50'})):
309      self.battery._DischargeDevice(1)
310
311  @mock.patch('time.sleep', mock.Mock())
312  def testDischargeDevice_takeslong(self):
313    with self.assertCalls(
314        (self.call.battery.GetBatteryInfo(), {'level': '100'}),
315        (self.call.battery._HardwareSetCharging(False)),
316        (self.call.battery._HardwareSetCharging(True)),
317        (self.call.battery.GetBatteryInfo(), {'level': '100'}),
318        (self.call.battery._HardwareSetCharging(False)),
319        (self.call.battery._HardwareSetCharging(True)),
320        (self.call.battery.GetBatteryInfo(), {'level': '99'}),
321        (self.call.battery._HardwareSetCharging(False)),
322        (self.call.battery._HardwareSetCharging(True)),
323        (self.call.battery.GetBatteryInfo(), {'level': '98'}),
324        (self.call.battery._HardwareSetCharging(False)),
325        (self.call.battery._HardwareSetCharging(True)),
326        (self.call.battery.GetBatteryInfo(), {'level': '97'})):
327      self.battery._DischargeDevice(3)
328
329  @mock.patch('time.sleep', mock.Mock())
330  def testDischargeDevice_dischargeTooClose(self):
331    with self.assertCalls(
332        (self.call.battery.GetBatteryInfo(), {'level': '100'})):
333      self.battery._DischargeDevice(99)
334
335  @mock.patch('time.sleep', mock.Mock())
336  def testDischargeDevice_percentageOutOfBounds(self):
337    with self.assertCalls(
338        (self.call.battery.GetBatteryInfo(), {'level': '100'})):
339      with self.assertRaises(ValueError):
340        self.battery._DischargeDevice(100)
341    with self.assertCalls(
342        (self.call.battery.GetBatteryInfo(), {'level': '100'})):
343      with self.assertRaises(ValueError):
344        self.battery._DischargeDevice(0)
345
346
347class BatteryUtilsGetBatteryInfoTest(BatteryUtilsTest):
348
349  def testGetBatteryInfo_normal(self):
350    with self.assertCalls(
351        (self.call.device.RunShellCommand(
352            ['dumpsys', 'battery'], check_return=True),
353        [
354          'Current Battery Service state:',
355          '  AC powered: false',
356          '  USB powered: true',
357          '  level: 100',
358          '  temperature: 321',
359        ])):
360      self.assertEquals(
361          {
362            'AC powered': 'false',
363            'USB powered': 'true',
364            'level': '100',
365            'temperature': '321',
366          },
367          self.battery.GetBatteryInfo())
368
369  def testGetBatteryInfo_nothing(self):
370    with self.assertCalls(
371        (self.call.device.RunShellCommand(
372            ['dumpsys', 'battery'], check_return=True), [])):
373      self.assertEquals({}, self.battery.GetBatteryInfo())
374
375
376class BatteryUtilsGetChargingTest(BatteryUtilsTest):
377
378  def testGetCharging_usb(self):
379    with self.assertCall(
380        self.call.battery.GetBatteryInfo(), {'USB powered': 'true'}):
381      self.assertTrue(self.battery.GetCharging())
382
383  def testGetCharging_usbFalse(self):
384    with self.assertCall(
385        self.call.battery.GetBatteryInfo(), {'USB powered': 'false'}):
386      self.assertFalse(self.battery.GetCharging())
387
388  def testGetCharging_ac(self):
389    with self.assertCall(
390        self.call.battery.GetBatteryInfo(), {'AC powered': 'true'}):
391      self.assertTrue(self.battery.GetCharging())
392
393  def testGetCharging_wireless(self):
394    with self.assertCall(
395        self.call.battery.GetBatteryInfo(), {'Wireless powered': 'true'}):
396      self.assertTrue(self.battery.GetCharging())
397
398  def testGetCharging_unknown(self):
399    with self.assertCall(
400        self.call.battery.GetBatteryInfo(), {'level': '42'}):
401      self.assertFalse(self.battery.GetCharging())
402
403
404class BatteryUtilsGetNetworkDataTest(BatteryUtilsTest):
405
406  def testGetNetworkData_noDataUsage(self):
407    with self.assertCalls(
408        (self.call.device.RunShellCommand(
409            ['dumpsys', 'batterystats', '-c'],
410            check_return=True, large_output=True),
411         _DUMPSYS_OUTPUT),
412        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_snd'),
413            self.ShellError()),
414        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_rcv'),
415            self.ShellError())):
416      self.assertEquals(self.battery.GetNetworkData('test_package1'), (0, 0))
417
418  def testGetNetworkData_badPackage(self):
419    with self.assertCall(
420        self.call.device.RunShellCommand(
421            ['dumpsys', 'batterystats', '-c'],
422            check_return=True, large_output=True),
423        _DUMPSYS_OUTPUT):
424      self.assertEqual(self.battery.GetNetworkData('asdf'), None)
425
426  def testGetNetworkData_packageNotCached(self):
427    with self.assertCalls(
428        (self.call.device.RunShellCommand(
429            ['dumpsys', 'batterystats', '-c'],
430            check_return=True, large_output=True),
431         _DUMPSYS_OUTPUT),
432        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_snd'), 1),
433        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_rcv'), 2)):
434      self.assertEqual(self.battery.GetNetworkData('test_package1'), (1, 2))
435
436  def testGetNetworkData_packageCached(self):
437    self.battery._cache['uids'] = {'test_package1': '1000'}
438    with self.assertCalls(
439        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_snd'), 1),
440        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_rcv'), 2)):
441      self.assertEqual(self.battery.GetNetworkData('test_package1'), (1, 2))
442
443  def testGetNetworkData_clearedCache(self):
444    with self.assertCalls(
445        (self.call.device.RunShellCommand(
446            ['dumpsys', 'batterystats', '-c'],
447            check_return=True, large_output=True),
448         _DUMPSYS_OUTPUT),
449        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_snd'), 1),
450        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_rcv'), 2)):
451      self.battery._cache.clear()
452      self.assertEqual(self.battery.GetNetworkData('test_package1'), (1, 2))
453
454
455class BatteryUtilsLetBatteryCoolToTemperatureTest(BatteryUtilsTest):
456
457  @mock.patch('time.sleep', mock.Mock())
458  def testLetBatteryCoolToTemperature_startUnder(self):
459    self.battery._cache['profile'] = self._NEXUS_6
460    with self.assertCalls(
461        (self.call.battery.EnableBatteryUpdates(), []),
462        (self.call.battery.GetBatteryInfo(), {'temperature': '500'})):
463      self.battery.LetBatteryCoolToTemperature(600)
464
465  @mock.patch('time.sleep', mock.Mock())
466  def testLetBatteryCoolToTemperature_startOver(self):
467    self.battery._cache['profile'] = self._NEXUS_6
468    with self.assertCalls(
469        (self.call.battery.EnableBatteryUpdates(), []),
470        (self.call.battery.GetBatteryInfo(), {'temperature': '500'}),
471        (self.call.battery.GetBatteryInfo(), {'temperature': '400'})):
472      self.battery.LetBatteryCoolToTemperature(400)
473
474  @mock.patch('time.sleep', mock.Mock())
475  def testLetBatteryCoolToTemperature_nexus5Hot(self):
476    self.battery._cache['profile'] = self._NEXUS_5
477    with self.assertCalls(
478        (self.call.battery.EnableBatteryUpdates(), []),
479        (self.call.battery.GetBatteryInfo(), {'temperature': '500'}),
480        (self.call.battery._DischargeDevice(1), []),
481        (self.call.battery.GetBatteryInfo(), {'temperature': '400'})):
482      self.battery.LetBatteryCoolToTemperature(400)
483
484  @mock.patch('time.sleep', mock.Mock())
485  def testLetBatteryCoolToTemperature_nexus5Cool(self):
486    self.battery._cache['profile'] = self._NEXUS_5
487    with self.assertCalls(
488        (self.call.battery.EnableBatteryUpdates(), []),
489        (self.call.battery.GetBatteryInfo(), {'temperature': '400'})):
490      self.battery.LetBatteryCoolToTemperature(400)
491
492
493class BatteryUtilsSupportsFuelGaugeTest(BatteryUtilsTest):
494
495  def testSupportsFuelGauge_false(self):
496    self.battery._cache['profile'] = self._NEXUS_5
497    self.assertFalse(self.battery.SupportsFuelGauge())
498
499  def testSupportsFuelGauge_trueMax(self):
500    self.battery._cache['profile'] = self._NEXUS_6
501    # TODO(rnephew): Change this to assertTrue when we have support for
502    # disabling hardware charging on nexus 6.
503    self.assertFalse(self.battery.SupportsFuelGauge())
504
505  def testSupportsFuelGauge_trueDS(self):
506    self.battery._cache['profile'] = self._NEXUS_10
507    # TODO(rnephew): Change this to assertTrue when we have support for
508    # disabling hardware charging on nexus 10.
509    self.assertFalse(self.battery.SupportsFuelGauge())
510
511
512class BatteryUtilsGetFuelGaugeChargeCounterTest(BatteryUtilsTest):
513
514  def testGetFuelGaugeChargeCounter_noFuelGauge(self):
515    self.battery._cache['profile'] = self._NEXUS_5
516    with self.assertRaises(device_errors.CommandFailedError):
517      self.battery.GetFuelGaugeChargeCounter()
518
519  def testGetFuelGaugeChargeCounter_fuelGaugePresent(self):
520    self.battery._cache['profile'] = self._NEXUS_6
521    with self.assertCalls(
522        (self.call.battery.SupportsFuelGauge(), True),
523        (self.call.device.ReadFile(mock.ANY), '123')):
524      self.assertEqual(self.battery.GetFuelGaugeChargeCounter(), 123)
525
526
527class BatteryUtilsSetCharging(BatteryUtilsTest):
528
529  @mock.patch('time.sleep', mock.Mock())
530  def testSetCharging_softwareSetTrue(self):
531    self.battery._cache['profile'] = self._NEXUS_6
532    with self.assertCalls(
533        (self.call.battery.GetCharging(), False),
534        (self.call.device.RunShellCommand(
535            ['dumpsys', 'battery', 'reset'], check_return=True), []),
536        (self.call.battery.GetCharging(), False),
537        (self.call.device.RunShellCommand(
538            ['dumpsys', 'battery'], check_return=True), ['UPDATES STOPPED']),
539        (self.call.battery.GetCharging(), True)):
540      self.battery.SetCharging(True)
541
542  @mock.patch('time.sleep', mock.Mock())
543  def testSetCharging_softwareSetFalse(self):
544    self.battery._cache['profile'] = self._NEXUS_6
545    with self.assertCalls(
546        (self.call.battery.GetCharging(), True),
547        (self.call.battery._ClearPowerData(), True),
548        (self.call.device.RunShellCommand(
549            ['dumpsys', 'battery', 'set', 'ac', '0'], check_return=True), []),
550        (self.call.device.RunShellCommand(
551            ['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True), []),
552        (self.call.battery.GetCharging(), False)):
553      self.battery.SetCharging(False)
554
555  @mock.patch('time.sleep', mock.Mock())
556  def testSetCharging_hardwareSetTrue(self):
557    self.battery._cache['profile'] = self._NEXUS_5
558    with self.assertCalls(
559        (self.call.battery.GetCharging(), False),
560        (self.call.battery._HardwareSetCharging(True))):
561      self.battery.SetCharging(True)
562
563  @mock.patch('time.sleep', mock.Mock())
564  def testSetCharging_hardwareSetFalse(self):
565    self.battery._cache['profile'] = self._NEXUS_5
566    with self.assertCalls(
567        (self.call.battery.GetCharging(), True),
568        (self.call.battery._ClearPowerData(), True),
569        (self.call.battery._HardwareSetCharging(False))):
570      self.battery.SetCharging(False)
571
572  def testSetCharging_expectedStateAlreadyTrue(self):
573    with self.assertCalls((self.call.battery.GetCharging(), True)):
574      self.battery.SetCharging(True)
575
576  def testSetCharging_expectedStateAlreadyFalse(self):
577    with self.assertCalls((self.call.battery.GetCharging(), False)):
578      self.battery.SetCharging(False)
579
580
581class BatteryUtilsPowerMeasurement(BatteryUtilsTest):
582
583  def testPowerMeasurement_hardware(self):
584    self.battery._cache['profile'] = self._NEXUS_5
585    with self.assertCalls(
586        (self.call.battery.GetCharging(), True),
587        (self.call.battery._ClearPowerData(), True),
588        (self.call.battery._HardwareSetCharging(False)),
589        (self.call.battery.GetCharging(), False),
590        (self.call.battery._HardwareSetCharging(True))):
591      with self.battery.PowerMeasurement():
592        pass
593
594  @mock.patch('time.sleep', mock.Mock())
595  def testPowerMeasurement_software(self):
596    self.battery._cache['profile'] = self._NEXUS_6
597    with self.assertCalls(
598        (self.call.battery.GetCharging(), True),
599        (self.call.battery._ClearPowerData(), True),
600        (self.call.device.RunShellCommand(
601            ['dumpsys', 'battery', 'set', 'ac', '0'], check_return=True), []),
602        (self.call.device.RunShellCommand(
603            ['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True), []),
604        (self.call.battery.GetCharging(), False),
605        (self.call.battery.GetCharging(), False),
606        (self.call.device.RunShellCommand(
607            ['dumpsys', 'battery', 'reset'], check_return=True), []),
608        (self.call.battery.GetCharging(), False),
609        (self.call.device.RunShellCommand(
610            ['dumpsys', 'battery'], check_return=True), ['UPDATES STOPPED']),
611        (self.call.battery.GetCharging(), True)):
612      with self.battery.PowerMeasurement():
613        pass
614
615
616class BatteryUtilsDiscoverDeviceProfile(BatteryUtilsTest):
617
618  def testDiscoverDeviceProfile_known(self):
619    with self.patch_call(self.call.device.product_model,
620                         return_value='Nexus 4'):
621      self.battery._DiscoverDeviceProfile()
622      self.assertListEqual(self.battery._cache['profile']['name'], ["Nexus 4"])
623
624  def testDiscoverDeviceProfile_unknown(self):
625    with self.patch_call(self.call.device.product_model,
626                         return_value='Other'):
627      self.battery._DiscoverDeviceProfile()
628      self.assertListEqual(self.battery._cache['profile']['name'], [])
629
630
631class BatteryUtilsClearPowerData(BatteryUtilsTest):
632
633  def testClearPowerData_preL(self):
634    with self.patch_call(self.call.device.build_version_sdk,
635                         return_value=20):
636      self.assertFalse(self.battery._ClearPowerData())
637
638  def testClearPowerData_clearedL(self):
639    with self.patch_call(self.call.device.build_version_sdk,
640                         return_value=22):
641      with self.assertCalls(
642          (self.call.device.RunShellCommand(
643              ['dumpsys', 'battery', 'set', 'usb', '1'], check_return=True),
644           []),
645          (self.call.device.RunShellCommand(
646              ['dumpsys', 'battery', 'set', 'ac', '1'], check_return=True), []),
647          (self.call.device.RunShellCommand(
648              ['dumpsys', 'batterystats', '--reset'], check_return=True), []),
649          (self.call.device.RunShellCommand(
650              ['dumpsys', 'batterystats', '--charged', '-c'],
651              check_return=True, large_output=True), []),
652          (self.call.device.RunShellCommand(
653              ['dumpsys', 'battery', 'reset'], check_return=True), [])):
654        self.assertTrue(self.battery._ClearPowerData())
655
656  @mock.patch('time.sleep', mock.Mock())
657  def testClearPowerData_notClearedL(self):
658    with self.patch_call(self.call.device.build_version_sdk,
659                         return_value=22):
660      with self.assertCalls(
661          (self.call.device.RunShellCommand(
662              ['dumpsys', 'battery', 'set', 'usb', '1'], check_return=True),
663           []),
664          (self.call.device.RunShellCommand(
665              ['dumpsys', 'battery', 'set', 'ac', '1'], check_return=True), []),
666          (self.call.device.RunShellCommand(
667              ['dumpsys', 'batterystats', '--reset'], check_return=True), []),
668          (self.call.device.RunShellCommand(
669              ['dumpsys', 'batterystats', '--charged', '-c'],
670              check_return=True, large_output=True),
671              ['9,1000,l,pwi,uid,0.0327']),
672          (self.call.device.RunShellCommand(
673              ['dumpsys', 'batterystats', '--reset'], check_return=True), []),
674          (self.call.device.RunShellCommand(
675              ['dumpsys', 'batterystats', '--charged', '-c'],
676              check_return=True, large_output=True),
677              ['9,1000,l,pwi,uid,0.0327']),
678          (self.call.device.RunShellCommand(
679              ['dumpsys', 'batterystats', '--reset'], check_return=True), []),
680          (self.call.device.RunShellCommand(
681              ['dumpsys', 'batterystats', '--charged', '-c'],
682              check_return=True, large_output=True),
683              ['9,1000,l,pwi,uid,0.0327']),
684          (self.call.device.RunShellCommand(
685              ['dumpsys', 'batterystats', '--reset'], check_return=True), []),
686          (self.call.device.RunShellCommand(
687              ['dumpsys', 'batterystats', '--charged', '-c'],
688              check_return=True, large_output=True),
689              ['9,1000,l,pwi,uid,0.0']),
690          (self.call.device.RunShellCommand(
691              ['dumpsys', 'battery', 'reset'], check_return=True), [])):
692        self.battery._ClearPowerData()
693
694
695if __name__ == '__main__':
696  logging.getLogger().setLevel(logging.DEBUG)
697  unittest.main(verbosity=2)
698