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, check_return=True, as_root=True, large_output=True), []),
113        (self.call.battery.GetCharging(), False),
114        (self.call.battery.GetCharging(), True)):
115      self.battery._HardwareSetCharging(True)
116
117  def testHardwareSetCharging_alreadyEnabled(self):
118    self.battery._cache['profile'] = self._NEXUS_5
119    with self.assertCalls(
120        (self.call.device.RunShellCommand(
121            mock.ANY, check_return=True, as_root=True, large_output=True), []),
122        (self.call.battery.GetCharging(), True)):
123      self.battery._HardwareSetCharging(True)
124
125  @mock.patch('time.sleep', mock.Mock())
126  def testHardwareSetCharging_disabled(self):
127    self.battery._cache['profile'] = self._NEXUS_5
128    with self.assertCalls(
129        (self.call.device.RunShellCommand(
130            mock.ANY, check_return=True, as_root=True, large_output=True), []),
131        (self.call.battery.GetCharging(), True),
132        (self.call.battery.GetCharging(), False)):
133      self.battery._HardwareSetCharging(False)
134
135
136class BatteryUtilsSetBatteryMeasurementTest(BatteryUtilsTest):
137
138  @mock.patch('time.sleep', mock.Mock())
139  def testBatteryMeasurementWifi(self):
140    with self.patch_call(self.call.device.build_version_sdk,
141                         return_value=22):
142      with self.assertCalls(
143          (self.call.battery._ClearPowerData(), True),
144          (self.call.device.RunShellCommand(
145              ['dumpsys', 'battery', 'set', 'ac', '0'], check_return=True), []),
146          (self.call.device.RunShellCommand(
147              ['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True),
148           []),
149          (self.call.battery.GetCharging(), False),
150          (self.call.device.RunShellCommand(
151              ['dumpsys', 'battery', 'reset'], check_return=True), []),
152          (self.call.battery.GetCharging(), False),
153          (self.call.device.RunShellCommand(
154              ['dumpsys', 'battery'], check_return=True), ['UPDATES STOPPED']),
155          (self.call.battery.GetCharging(), False),
156          (self.call.device.RunShellCommand(
157              ['dumpsys', 'battery'], check_return=True), [])):
158        with self.battery.BatteryMeasurement():
159          pass
160
161  @mock.patch('time.sleep', mock.Mock())
162  def testBatteryMeasurementUsb(self):
163    with self.patch_call(self.call.device.build_version_sdk,
164                         return_value=22):
165      with self.assertCalls(
166          (self.call.battery._ClearPowerData(), True),
167          (self.call.device.RunShellCommand(
168              ['dumpsys', 'battery', 'set', 'ac', '0'], check_return=True), []),
169          (self.call.device.RunShellCommand(
170              ['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True),
171           []),
172          (self.call.battery.GetCharging(), False),
173          (self.call.device.RunShellCommand(
174              ['dumpsys', 'battery', 'reset'], check_return=True), []),
175          (self.call.battery.GetCharging(), False),
176          (self.call.device.RunShellCommand(
177              ['dumpsys', 'battery'], check_return=True), ['UPDATES STOPPED']),
178          (self.call.battery.GetCharging(), True)):
179        with self.battery.BatteryMeasurement():
180          pass
181
182
183class BatteryUtilsGetPowerData(BatteryUtilsTest):
184
185  def testGetPowerData(self):
186    with self.assertCalls(
187        (self.call.device.RunShellCommand(
188            ['dumpsys', 'batterystats', '-c'],
189            check_return=True, large_output=True),
190         _DUMPSYS_OUTPUT)):
191      data = self.battery.GetPowerData()
192      check = {
193        'system_total': 2000.0,
194        'per_package': {
195          'test_package1': {'uid': '1000', 'data': [1.0]},
196          'test_package2': {'uid': '1001', 'data': [2.0]}
197        }
198      }
199      self.assertEqual(data, check)
200
201  def testGetPowerData_packageCollisionSame(self):
202    self.battery._cache['uids'] = {'test_package1': '1000'}
203    with self.assertCall(
204        self.call.device.RunShellCommand(
205            ['dumpsys', 'batterystats', '-c'],
206            check_return=True, large_output=True),
207        _DUMPSYS_OUTPUT):
208      data = self.battery.GetPowerData()
209      check = {
210        'system_total': 2000.0,
211        'per_package': {
212          'test_package1': {'uid': '1000', 'data': [1.0]},
213          'test_package2': {'uid': '1001', 'data': [2.0]}
214        }
215      }
216      self.assertEqual(data, check)
217
218  def testGetPowerData_packageCollisionDifferent(self):
219    self.battery._cache['uids'] = {'test_package1': '1'}
220    with self.assertCall(
221        self.call.device.RunShellCommand(
222            ['dumpsys', 'batterystats', '-c'],
223            check_return=True, large_output=True),
224        _DUMPSYS_OUTPUT):
225      with self.assertRaises(device_errors.CommandFailedError):
226        self.battery.GetPowerData()
227
228  def testGetPowerData_cacheCleared(self):
229    with self.assertCalls(
230        (self.call.device.RunShellCommand(
231            ['dumpsys', 'batterystats', '-c'],
232            check_return=True, large_output=True),
233         _DUMPSYS_OUTPUT)):
234      self.battery._cache.clear()
235      data = self.battery.GetPowerData()
236      check = {
237        'system_total': 2000.0,
238        'per_package': {
239          'test_package1': {'uid': '1000', 'data': [1.0]},
240          'test_package2': {'uid': '1001', 'data': [2.0]}
241        }
242      }
243      self.assertEqual(data, check)
244
245
246class BatteryUtilsChargeDevice(BatteryUtilsTest):
247
248  @mock.patch('time.sleep', mock.Mock())
249  def testChargeDeviceToLevel_pass(self):
250    with self.assertCalls(
251        (self.call.battery.SetCharging(True)),
252        (self.call.battery.GetBatteryInfo(), {'level': '50'}),
253        (self.call.battery.GetBatteryInfo(), {'level': '100'})):
254      self.battery.ChargeDeviceToLevel(95)
255
256  @mock.patch('time.sleep', mock.Mock())
257  def testChargeDeviceToLevel_failureSame(self):
258    with self.assertCalls(
259        (self.call.battery.SetCharging(True)),
260        (self.call.battery.GetBatteryInfo(), {'level': '50'}),
261        (self.call.battery.GetBatteryInfo(), {'level': '50'}),
262
263        (self.call.battery.GetBatteryInfo(), {'level': '50'})):
264      with self.assertRaises(device_errors.DeviceChargingError):
265        old_max = battery_utils._MAX_CHARGE_ERROR
266        try:
267          battery_utils._MAX_CHARGE_ERROR = 2
268          self.battery.ChargeDeviceToLevel(95)
269        finally:
270          battery_utils._MAX_CHARGE_ERROR = old_max
271
272  @mock.patch('time.sleep', mock.Mock())
273  def testChargeDeviceToLevel_failureDischarge(self):
274    with self.assertCalls(
275        (self.call.battery.SetCharging(True)),
276        (self.call.battery.GetBatteryInfo(), {'level': '50'}),
277        (self.call.battery.GetBatteryInfo(), {'level': '49'}),
278        (self.call.battery.GetBatteryInfo(), {'level': '48'})):
279      with self.assertRaises(device_errors.DeviceChargingError):
280        old_max = battery_utils._MAX_CHARGE_ERROR
281        try:
282          battery_utils._MAX_CHARGE_ERROR = 2
283          self.battery.ChargeDeviceToLevel(95)
284        finally:
285          battery_utils._MAX_CHARGE_ERROR = old_max
286
287
288class BatteryUtilsDischargeDevice(BatteryUtilsTest):
289
290  @mock.patch('time.sleep', mock.Mock())
291  def testDischargeDevice_exact(self):
292    with self.assertCalls(
293        (self.call.battery.GetBatteryInfo(), {'level': '100'}),
294        (self.call.battery._HardwareSetCharging(False)),
295        (self.call.battery._HardwareSetCharging(True)),
296        (self.call.battery.GetBatteryInfo(), {'level': '99'})):
297      self.battery._DischargeDevice(1)
298
299  @mock.patch('time.sleep', mock.Mock())
300  def testDischargeDevice_over(self):
301    with self.assertCalls(
302        (self.call.battery.GetBatteryInfo(), {'level': '100'}),
303        (self.call.battery._HardwareSetCharging(False)),
304        (self.call.battery._HardwareSetCharging(True)),
305        (self.call.battery.GetBatteryInfo(), {'level': '50'})):
306      self.battery._DischargeDevice(1)
307
308  @mock.patch('time.sleep', mock.Mock())
309  def testDischargeDevice_takeslong(self):
310    with self.assertCalls(
311        (self.call.battery.GetBatteryInfo(), {'level': '100'}),
312        (self.call.battery._HardwareSetCharging(False)),
313        (self.call.battery._HardwareSetCharging(True)),
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': '99'}),
318        (self.call.battery._HardwareSetCharging(False)),
319        (self.call.battery._HardwareSetCharging(True)),
320        (self.call.battery.GetBatteryInfo(), {'level': '98'}),
321        (self.call.battery._HardwareSetCharging(False)),
322        (self.call.battery._HardwareSetCharging(True)),
323        (self.call.battery.GetBatteryInfo(), {'level': '97'})):
324      self.battery._DischargeDevice(3)
325
326  @mock.patch('time.sleep', mock.Mock())
327  def testDischargeDevice_dischargeTooClose(self):
328    with self.assertCalls(
329        (self.call.battery.GetBatteryInfo(), {'level': '100'})):
330      self.battery._DischargeDevice(99)
331
332  @mock.patch('time.sleep', mock.Mock())
333  def testDischargeDevice_percentageOutOfBounds(self):
334    with self.assertCalls(
335        (self.call.battery.GetBatteryInfo(), {'level': '100'})):
336      with self.assertRaises(ValueError):
337        self.battery._DischargeDevice(100)
338    with self.assertCalls(
339        (self.call.battery.GetBatteryInfo(), {'level': '100'})):
340      with self.assertRaises(ValueError):
341        self.battery._DischargeDevice(0)
342
343
344class BatteryUtilsGetBatteryInfoTest(BatteryUtilsTest):
345
346  def testGetBatteryInfo_normal(self):
347    with self.assertCalls(
348        (self.call.device.RunShellCommand(
349            ['dumpsys', 'battery'], check_return=True),
350        [
351          'Current Battery Service state:',
352          '  AC powered: false',
353          '  USB powered: true',
354          '  level: 100',
355          '  temperature: 321',
356        ])):
357      self.assertEquals(
358          {
359            'AC powered': 'false',
360            'USB powered': 'true',
361            'level': '100',
362            'temperature': '321',
363          },
364          self.battery.GetBatteryInfo())
365
366  def testGetBatteryInfo_nothing(self):
367    with self.assertCalls(
368        (self.call.device.RunShellCommand(
369            ['dumpsys', 'battery'], check_return=True), [])):
370      self.assertEquals({}, self.battery.GetBatteryInfo())
371
372
373class BatteryUtilsGetChargingTest(BatteryUtilsTest):
374
375  def testGetCharging_usb(self):
376    with self.assertCall(
377        self.call.battery.GetBatteryInfo(), {'USB powered': 'true'}):
378      self.assertTrue(self.battery.GetCharging())
379
380  def testGetCharging_usbFalse(self):
381    with self.assertCall(
382        self.call.battery.GetBatteryInfo(), {'USB powered': 'false'}):
383      self.assertFalse(self.battery.GetCharging())
384
385  def testGetCharging_ac(self):
386    with self.assertCall(
387        self.call.battery.GetBatteryInfo(), {'AC powered': 'true'}):
388      self.assertTrue(self.battery.GetCharging())
389
390  def testGetCharging_wireless(self):
391    with self.assertCall(
392        self.call.battery.GetBatteryInfo(), {'Wireless powered': 'true'}):
393      self.assertTrue(self.battery.GetCharging())
394
395  def testGetCharging_unknown(self):
396    with self.assertCall(
397        self.call.battery.GetBatteryInfo(), {'level': '42'}):
398      self.assertFalse(self.battery.GetCharging())
399
400
401class BatteryUtilsGetNetworkDataTest(BatteryUtilsTest):
402
403  def testGetNetworkData_noDataUsage(self):
404    with self.assertCalls(
405        (self.call.device.RunShellCommand(
406            ['dumpsys', 'batterystats', '-c'],
407            check_return=True, large_output=True),
408         _DUMPSYS_OUTPUT),
409        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_snd'),
410            self.ShellError()),
411        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_rcv'),
412            self.ShellError())):
413      self.assertEquals(self.battery.GetNetworkData('test_package1'), (0, 0))
414
415  def testGetNetworkData_badPackage(self):
416    with self.assertCall(
417        self.call.device.RunShellCommand(
418            ['dumpsys', 'batterystats', '-c'],
419            check_return=True, large_output=True),
420        _DUMPSYS_OUTPUT):
421      self.assertEqual(self.battery.GetNetworkData('asdf'), None)
422
423  def testGetNetworkData_packageNotCached(self):
424    with self.assertCalls(
425        (self.call.device.RunShellCommand(
426            ['dumpsys', 'batterystats', '-c'],
427            check_return=True, large_output=True),
428         _DUMPSYS_OUTPUT),
429        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_snd'), 1),
430        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_rcv'), 2)):
431      self.assertEqual(self.battery.GetNetworkData('test_package1'), (1, 2))
432
433  def testGetNetworkData_packageCached(self):
434    self.battery._cache['uids'] = {'test_package1': '1000'}
435    with self.assertCalls(
436        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_snd'), 1),
437        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_rcv'), 2)):
438      self.assertEqual(self.battery.GetNetworkData('test_package1'), (1, 2))
439
440  def testGetNetworkData_clearedCache(self):
441    with self.assertCalls(
442        (self.call.device.RunShellCommand(
443            ['dumpsys', 'batterystats', '-c'],
444            check_return=True, large_output=True),
445         _DUMPSYS_OUTPUT),
446        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_snd'), 1),
447        (self.call.device.ReadFile('/proc/uid_stat/1000/tcp_rcv'), 2)):
448      self.battery._cache.clear()
449      self.assertEqual(self.battery.GetNetworkData('test_package1'), (1, 2))
450
451
452class BatteryUtilsLetBatteryCoolToTemperatureTest(BatteryUtilsTest):
453
454  @mock.patch('time.sleep', mock.Mock())
455  def testLetBatteryCoolToTemperature_startUnder(self):
456    self.battery._cache['profile'] = self._NEXUS_6
457    with self.assertCalls(
458        (self.call.battery.EnableBatteryUpdates(), []),
459        (self.call.battery.GetBatteryInfo(), {'temperature': '500'})):
460      self.battery.LetBatteryCoolToTemperature(600)
461
462  @mock.patch('time.sleep', mock.Mock())
463  def testLetBatteryCoolToTemperature_startOver(self):
464    self.battery._cache['profile'] = self._NEXUS_6
465    with self.assertCalls(
466        (self.call.battery.EnableBatteryUpdates(), []),
467        (self.call.battery.GetBatteryInfo(), {'temperature': '500'}),
468        (self.call.battery.GetBatteryInfo(), {'temperature': '400'})):
469      self.battery.LetBatteryCoolToTemperature(400)
470
471  @mock.patch('time.sleep', mock.Mock())
472  def testLetBatteryCoolToTemperature_nexus5Hot(self):
473    self.battery._cache['profile'] = self._NEXUS_5
474    with self.assertCalls(
475        (self.call.battery.EnableBatteryUpdates(), []),
476        (self.call.battery.GetBatteryInfo(), {'temperature': '500'}),
477        (self.call.battery._DischargeDevice(1), []),
478        (self.call.battery.GetBatteryInfo(), {'temperature': '400'})):
479      self.battery.LetBatteryCoolToTemperature(400)
480
481  @mock.patch('time.sleep', mock.Mock())
482  def testLetBatteryCoolToTemperature_nexus5Cool(self):
483    self.battery._cache['profile'] = self._NEXUS_5
484    with self.assertCalls(
485        (self.call.battery.EnableBatteryUpdates(), []),
486        (self.call.battery.GetBatteryInfo(), {'temperature': '400'})):
487      self.battery.LetBatteryCoolToTemperature(400)
488
489
490class BatteryUtilsSupportsFuelGaugeTest(BatteryUtilsTest):
491
492  def testSupportsFuelGauge_false(self):
493    self.battery._cache['profile'] = self._NEXUS_5
494    self.assertFalse(self.battery.SupportsFuelGauge())
495
496  def testSupportsFuelGauge_trueMax(self):
497    self.battery._cache['profile'] = self._NEXUS_6
498    # TODO(rnephew): Change this to assertTrue when we have support for
499    # disabling hardware charging on nexus 6.
500    self.assertFalse(self.battery.SupportsFuelGauge())
501
502  def testSupportsFuelGauge_trueDS(self):
503    self.battery._cache['profile'] = self._NEXUS_10
504    # TODO(rnephew): Change this to assertTrue when we have support for
505    # disabling hardware charging on nexus 10.
506    self.assertFalse(self.battery.SupportsFuelGauge())
507
508
509class BatteryUtilsGetFuelGaugeChargeCounterTest(BatteryUtilsTest):
510
511  def testGetFuelGaugeChargeCounter_noFuelGauge(self):
512    self.battery._cache['profile'] = self._NEXUS_5
513    with self.assertRaises(device_errors.CommandFailedError):
514      self.battery.GetFuelGaugeChargeCounter()
515
516  def testGetFuelGaugeChargeCounter_fuelGaugePresent(self):
517    self.battery._cache['profile'] = self._NEXUS_6
518    with self.assertCalls(
519        (self.call.battery.SupportsFuelGauge(), True),
520        (self.call.device.ReadFile(mock.ANY), '123')):
521      self.assertEqual(self.battery.GetFuelGaugeChargeCounter(), 123)
522
523
524class BatteryUtilsSetCharging(BatteryUtilsTest):
525
526  @mock.patch('time.sleep', mock.Mock())
527  def testSetCharging_softwareSetTrue(self):
528    self.battery._cache['profile'] = self._NEXUS_6
529    with self.assertCalls(
530        (self.call.battery.GetCharging(), False),
531        (self.call.device.RunShellCommand(
532            ['dumpsys', 'battery', 'reset'], check_return=True), []),
533        (self.call.battery.GetCharging(), False),
534        (self.call.device.RunShellCommand(
535            ['dumpsys', 'battery'], check_return=True), ['UPDATES STOPPED']),
536        (self.call.battery.GetCharging(), True)):
537      self.battery.SetCharging(True)
538
539  @mock.patch('time.sleep', mock.Mock())
540  def testSetCharging_softwareSetFalse(self):
541    self.battery._cache['profile'] = self._NEXUS_6
542    with self.assertCalls(
543        (self.call.battery.GetCharging(), True),
544        (self.call.battery._ClearPowerData(), True),
545        (self.call.device.RunShellCommand(
546            ['dumpsys', 'battery', 'set', 'ac', '0'], check_return=True), []),
547        (self.call.device.RunShellCommand(
548            ['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True), []),
549        (self.call.battery.GetCharging(), False)):
550      self.battery.SetCharging(False)
551
552  @mock.patch('time.sleep', mock.Mock())
553  def testSetCharging_hardwareSetTrue(self):
554    self.battery._cache['profile'] = self._NEXUS_5
555    with self.assertCalls(
556        (self.call.battery.GetCharging(), False),
557        (self.call.battery._HardwareSetCharging(True))):
558      self.battery.SetCharging(True)
559
560  @mock.patch('time.sleep', mock.Mock())
561  def testSetCharging_hardwareSetFalse(self):
562    self.battery._cache['profile'] = self._NEXUS_5
563    with self.assertCalls(
564        (self.call.battery.GetCharging(), True),
565        (self.call.battery._ClearPowerData(), True),
566        (self.call.battery._HardwareSetCharging(False))):
567      self.battery.SetCharging(False)
568
569  def testSetCharging_expectedStateAlreadyTrue(self):
570    with self.assertCalls((self.call.battery.GetCharging(), True)):
571      self.battery.SetCharging(True)
572
573  def testSetCharging_expectedStateAlreadyFalse(self):
574    with self.assertCalls((self.call.battery.GetCharging(), False)):
575      self.battery.SetCharging(False)
576
577
578class BatteryUtilsPowerMeasurement(BatteryUtilsTest):
579
580  def testPowerMeasurement_hardware(self):
581    self.battery._cache['profile'] = self._NEXUS_5
582    with self.assertCalls(
583        (self.call.battery.GetCharging(), True),
584        (self.call.battery._ClearPowerData(), True),
585        (self.call.battery._HardwareSetCharging(False)),
586        (self.call.battery.GetCharging(), False),
587        (self.call.battery._HardwareSetCharging(True))):
588      with self.battery.PowerMeasurement():
589        pass
590
591  @mock.patch('time.sleep', mock.Mock())
592  def testPowerMeasurement_software(self):
593    self.battery._cache['profile'] = self._NEXUS_6
594    with self.assertCalls(
595        (self.call.battery.GetCharging(), True),
596        (self.call.battery._ClearPowerData(), True),
597        (self.call.device.RunShellCommand(
598            ['dumpsys', 'battery', 'set', 'ac', '0'], check_return=True), []),
599        (self.call.device.RunShellCommand(
600            ['dumpsys', 'battery', 'set', 'usb', '0'], check_return=True), []),
601        (self.call.battery.GetCharging(), False),
602        (self.call.battery.GetCharging(), False),
603        (self.call.device.RunShellCommand(
604            ['dumpsys', 'battery', 'reset'], check_return=True), []),
605        (self.call.battery.GetCharging(), False),
606        (self.call.device.RunShellCommand(
607            ['dumpsys', 'battery'], check_return=True), ['UPDATES STOPPED']),
608        (self.call.battery.GetCharging(), True)):
609      with self.battery.PowerMeasurement():
610        pass
611
612
613class BatteryUtilsDiscoverDeviceProfile(BatteryUtilsTest):
614
615  def testDiscoverDeviceProfile_known(self):
616    with self.patch_call(self.call.device.product_model,
617                         return_value='Nexus 4'):
618      self.battery._DiscoverDeviceProfile()
619      self.assertEqual(self.battery._cache['profile']['name'], "Nexus 4")
620
621  def testDiscoverDeviceProfile_unknown(self):
622    with self.patch_call(self.call.device.product_model,
623                         return_value='Other'):
624      self.battery._DiscoverDeviceProfile()
625      self.assertEqual(self.battery._cache['profile']['name'], None)
626
627
628class BatteryUtilsClearPowerData(BatteryUtilsTest):
629
630  def testClearPowerData_preL(self):
631    with self.patch_call(self.call.device.build_version_sdk,
632                         return_value=20):
633      self.assertFalse(self.battery._ClearPowerData())
634
635  def testClearPowerData_clearedL(self):
636    with self.patch_call(self.call.device.build_version_sdk,
637                         return_value=22):
638      with self.assertCalls(
639          (self.call.device.RunShellCommand(
640              ['dumpsys', 'battery', 'set', 'usb', '1'], check_return=True),
641           []),
642          (self.call.device.RunShellCommand(
643              ['dumpsys', 'battery', 'set', 'ac', '1'], check_return=True), []),
644          (self.call.device.RunShellCommand(
645              ['dumpsys', 'batterystats', '--reset'], check_return=True), []),
646          (self.call.device.RunShellCommand(
647              ['dumpsys', 'batterystats', '--charged', '-c'],
648              check_return=True, large_output=True), []),
649          (self.call.device.RunShellCommand(
650              ['dumpsys', 'battery', 'reset'], check_return=True), [])):
651        self.assertTrue(self.battery._ClearPowerData())
652
653  @mock.patch('time.sleep', mock.Mock())
654  def testClearPowerData_notClearedL(self):
655    with self.patch_call(self.call.device.build_version_sdk,
656                         return_value=22):
657      with self.assertCalls(
658          (self.call.device.RunShellCommand(
659              ['dumpsys', 'battery', 'set', 'usb', '1'], check_return=True),
660           []),
661          (self.call.device.RunShellCommand(
662              ['dumpsys', 'battery', 'set', 'ac', '1'], check_return=True), []),
663          (self.call.device.RunShellCommand(
664              ['dumpsys', 'batterystats', '--reset'], check_return=True), []),
665          (self.call.device.RunShellCommand(
666              ['dumpsys', 'batterystats', '--charged', '-c'],
667              check_return=True, large_output=True),
668              ['9,1000,l,pwi,uid,0.0327']),
669          (self.call.device.RunShellCommand(
670              ['dumpsys', 'batterystats', '--reset'], check_return=True), []),
671          (self.call.device.RunShellCommand(
672              ['dumpsys', 'batterystats', '--charged', '-c'],
673              check_return=True, large_output=True),
674              ['9,1000,l,pwi,uid,0.0327']),
675          (self.call.device.RunShellCommand(
676              ['dumpsys', 'batterystats', '--reset'], check_return=True), []),
677          (self.call.device.RunShellCommand(
678              ['dumpsys', 'batterystats', '--charged', '-c'],
679              check_return=True, large_output=True),
680              ['9,1000,l,pwi,uid,0.0327']),
681          (self.call.device.RunShellCommand(
682              ['dumpsys', 'batterystats', '--reset'], check_return=True), []),
683          (self.call.device.RunShellCommand(
684              ['dumpsys', 'batterystats', '--charged', '-c'],
685              check_return=True, large_output=True),
686              ['9,1000,l,pwi,uid,0.0']),
687          (self.call.device.RunShellCommand(
688              ['dumpsys', 'battery', 'reset'], check_return=True), [])):
689        self.battery._ClearPowerData()
690
691
692if __name__ == '__main__':
693  logging.getLogger().setLevel(logging.DEBUG)
694  unittest.main(verbosity=2)
695