blob: 1e69d9ab2a11413c92c9264162598e1bc3caea1a [file] [log] [blame]
# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Test that waits the battery to be charged to specific level.
Description
-----------
The test waits until the battery is charged to the given level, and pass.
The ``target_charge_pct`` sets the target charge level in percentage.
``target_charge_pct`` can be set to some special values:
* ``"goofy"``: Use ``min_charge_pct`` from Goofy's charge_manager plugin as
target charge level.
* ``"cutoff"``: Use ``CUTOFF_BATTERY_MIN_PERCENTAGE`` from cutoff.json as
target charge level.
If ``target_charge_pct_is_delta`` is True, ``target_charge_pct`` would be
interpreted as difference to current charge level.
If battery doesn't reach the target level in ``timeout_secs`` seconds, the test
would fail.
Test Procedure
--------------
This is an automated test without user interaction.
1. A screen would be shown with current battery level, target battery level,
and time remaining.
2. Test would pass when battery reach target level, or fail if test run longer
than ``timeout_secs``.
Dependency
----------
Device API `cros.factory.device.power`.
Examples
--------
To charge the device to ``min_charge_pct`` in Goofy charge_manager (default
behavior), add this in test list::
{
"pytest_name": "blocking_charge",
"exclusive_resources": ["POWER"]
}
To charge the device to minimum battery level needed for cutoff, add this in
test list::
{
"pytest_name": "blocking_charge",
"exclusive_resources": ["POWER"],
"args": {
"target_charge_pct": "cutoff"
}
}
To charge the device to 75 percent, add this in test list::
{
"pytest_name": "blocking_charge",
"exclusive_resources": ["POWER"],
"args": {
"target_charge_pct": 75
}
}
To charge the device 10 percent more, and only allow 5 minutes time for
charging, add this in test list::
{
"pytest_name": "blocking_charge",
"exclusive_resources": ["POWER"],
"args": {
"target_charge_pct_is_delta": true,
"timeout_secs": 300,
"target_charge_pct": 20
}
}
"""
import logging
import os
import factory_common # pylint: disable=unused-import
from cros.factory.device import device_utils
from cros.factory.test.env import paths
from cros.factory.test import event_log # TODO(chuntsen): Deprecate event log.
from cros.factory.test.i18n import _
from cros.factory.test import test_case
from cros.factory.test.utils import goofy_plugin_utils
from cros.factory.testlog import testlog
from cros.factory.utils.arg_utils import Arg
from cros.factory.utils import config_utils
from cros.factory.utils import type_utils
_DEFAULT_TARGET_CHARGE = 78
def FormatTime(seconds):
return '%d:%02d:%02d' % (seconds / 3600, (seconds / 60) % 60, seconds % 60)
def MakeChargeTextLabel(start, current, target, elapsed, remaining):
return _(
'Charging to {target}% (Start: {start}%. Current: {current}%.)<br>'
'Time elapsed: {elapsed} Time remaining: {remaining}',
target=target,
start=start,
current=current,
elapsed=FormatTime(elapsed),
remaining=FormatTime(remaining))
def MakeSpriteHTMLTag(src, height, width):
return ('<div id="batteryIcon" style="background-image: url(%s);'
'width: %dpx; height: %dpx; margin: auto;"></div>') % (src, width,
height)
def _GetCutoffBatteryMinPercentage():
config = config_utils.LoadConfig(
config_name='cutoff',
default_config_dirs=os.path.join(paths.FACTORY_DIR, 'sh', 'cutoff'))
return config.get('CUTOFF_BATTERY_MIN_PERCENTAGE', _DEFAULT_TARGET_CHARGE)
def _GetGoofyBatteryMinPercentage():
config = goofy_plugin_utils.GetPluginArguments('charge_manager') or {}
return config.get('min_charge_pct', _DEFAULT_TARGET_CHARGE)
class ChargerTest(test_case.TestCase):
ARGS = [
Arg('target_charge_pct', (int, type_utils.Enum(['goofy', 'cutoff'])),
'Target charge level.', default='goofy'),
Arg('target_charge_pct_is_delta', bool,
'Specify target_charge_pct is a delta of current charge',
default=False),
Arg('timeout_secs', int, 'Maximum allowed time to charge battery',
default=3600),
]
def setUp(self):
self._power = device_utils.CreateDUTInterface().power
# Group checker for Testlog.
self._group_checker = testlog.GroupParam('charge', ['charge', 'elapsed'])
def runTest(self):
self.assertTrue(self._power.CheckBatteryPresent(), 'Cannot find battery.')
self.assertTrue(self._power.CheckACPresent(), 'Cannot find AC power.')
start_charge = self._power.GetChargePct()
self.assertTrue(start_charge, 'Error getting battery state.')
target_charge = self.args.target_charge_pct
if self.args.target_charge_pct_is_delta:
self.assertIsInstance(target_charge, int,
'target_charge must be int when '
'target_charge_pct_is_delta is True.')
target_charge = min(target_charge + start_charge, 100)
elif target_charge == 'cutoff':
target_charge = _GetCutoffBatteryMinPercentage()
elif target_charge == 'goofy':
target_charge = _GetGoofyBatteryMinPercentage()
logging.info('Target charge is %d%%', target_charge)
testlog.LogParam('start_charge', start_charge)
testlog.LogParam('target_charge', target_charge)
if start_charge >= target_charge:
return
self._power.SetChargeState(self._power.ChargeState.CHARGE)
self.ui.SetState(MakeSpriteHTMLTag('charging_sprite.png', 256, 256))
logging.info('Charging starting at %d%%', start_charge)
for elapsed in xrange(self.args.timeout_secs):
charge = self._power.GetChargePct()
if charge >= target_charge:
event_log.Log('charged', charge=charge, target=target_charge,
elapsed=elapsed)
with self._group_checker:
testlog.CheckNumericParam('charge', charge, min=target_charge)
testlog.LogParam('elapsed', elapsed)
return
self.ui.RunJS(
'document.getElementById("batteryIcon").style.backgroundPosition'
' = "-%dpx 0px"' % ((elapsed % 4) * 256))
self.ui.SetInstruction(MakeChargeTextLabel(
start_charge,
charge,
target_charge,
elapsed,
self.args.timeout_secs - elapsed))
if elapsed % 300 == 0:
logging.info('Battery level is %d%% after %d minutes',
charge,
elapsed / 60)
self.Sleep(1)
event_log.Log('failed_to_charge', charge=charge, target=target_charge,
timeout_sec=self.args.timeout_secs)
self.FailTask('Cannot charge battery to %d%% in %d seconds.' %
(target_charge, self.args.timeout_secs))