blob: 2474694f5a3edadcaeb76680127bb2ec10746795 [file] [log] [blame]
# Copyright 2013 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.
"""A test to ensure the power type and status of device under test.
The test detects the power type of the underlying device. It is also used to
ask operator to unplug the AC from the device.
Test Procedure
To test if plugged AC power is detected properly:
1. Plug the designated power source to the device, and unplug all the other
power sources.
2. Starts the test.
To test AC power unplugged:
1. Starts the test.
2. Follow the instruction on the UI to unplug the power.
- Need a power source.
For plugged with required power range:
- Need a USB PD power source with required power range.
To test AC unplugged, add this to test list::
"pytest_name": "ac_power",
"args": {
"online": false
To test USBPD 45W plugged on usbpd port 0::
"pytest_name": "ac_power",
"args": {
"power_type": "USB_PD",
"usbpd_power_range": [0, 45, 45],
"online": true
import numbers
import factory_common # pylint: disable=unused-import
from cros.factory.device import device_utils
from cros.factory.test.fixture import bft_fixture
from cros.factory.test.i18n import _
from cros.factory.test import session
from cros.factory.test import test_case
from cros.factory.testlog import testlog
from cros.factory.utils.arg_utils import Arg
_PROBE_TIMES_ID = 'probed_times'
_AC_STATUS_ID = 'ac_status'
_AC_POWER_ID = 'ac_power'
class ACPowerTest(test_case.TestCase):
"""A test to instruct the operator to plug/unplug AC power.
power_type: The type of the power. None to skip power type check.
usbpd_power_range: The required usbpd power range (min, max). None to skip
power range check.
online: True if expecting AC power. Otherwise, False.
bft_fixture: If assigned, it commands the BFT fixture to
plug/unplug an AC adapter.
retries: Maximum number of retries allowed to pass the test.
polling_period_secs: Polling period in seconds.
silent_warning: Skips first N charger type mismatch before giving a
ARGS = [
Arg('power_type', str, 'Type of the power source', default=None),
Arg('usbpd_power_range', list,
'The required power usbpd power range [usbpd_port, min, max]',
Arg('online', bool, 'True if expecting AC power', default=True),
Arg('bft_fixture', dict, bft_fixture.TEST_ARG_HELP, default=None),
Arg('retries', int,
'Maximum number of retries allowed to pass the test. '
'0 means only probe once. Default None means probe forever.',
Arg('polling_period_secs', numbers.Real,
'Polling period in seconds.', default=1),
Arg('silent_warning', int,
'Skips first N charger type mismatch before giving a warning. '
'Because EC needs about 1.6 seconds to identify charger type after '
'it is plugged in, it skips first N mismatched probe.',
def setUp(self):
self._power = device_utils.CreateDUTInterface().power
if not
instruction = _('Unplug the charger.')
elif self.args.power_type:
instruction = _('Plug in the charger ({type})', type=self.args.power_type)
instruction = _('Plug in the charger')
'<div id="%s"></div><div id="%s"></div><div id="%s"></div>' %
self._power_state = {}
self._last_type = None
self._last_ac_present = None
self._skip_warning_remains = self.args.silent_warning
if self.args.usbpd_power_range is not None:
description='Detected usbpd power.',
# Prepare fixture auto test if needed.
self.fixture = None
if self.args.bft_fixture:
self.fixture = bft_fixture.CreateBFTFixture(**self.args.bft_fixture)
def UpdateACPower(self, watt, min_watt, max_watt):
_('Detected power {watt} W, '
'required power range ({min_watt} W, {max_watt} W)',
def UpdateACStatus(self, status):
self.ui.SetHTML(status, id=_AC_STATUS_ID)
def UpdateProbeTimes(self, num_probes):
_('Probed {times} / {total}', times=num_probes,
def CheckCondition(self):
ac_present = self._power.CheckACPresent()
current_type = self._power.GetACType()
# Reset silent warning countdown when AC present status change.
# Also reset _last_type as we want to give a warning for each
# mismatched charger attached.
if self._last_ac_present != ac_present:
self._last_ac_present = ac_present
self._skip_warning_remains = self.args.silent_warning
self._last_type = None
if ac_present !=
if not ac_present:
self.UpdateACStatus(_('No AC adapter'))
return False
if self.args.power_type and self.args.power_type != current_type:
if self._skip_warning_remains > 0:
self.UpdateACStatus(_('Identifying AC adapter...'))
self._skip_warning_remains -= 1
elif self._last_type != current_type:
self.UpdateACStatus(_('AC adapter type: {type}', type=current_type))
'Expecting %s but see %s', self.args.power_type, current_type)
self._last_type = current_type
return False
if self.args.usbpd_power_range and self.args.power_type == _AC_TYPE_USB_PD:
usbpd_power_infos = self._power.GetUSBPDPowerInfo()
port, power_min, power_max = self.args.usbpd_power_range
# USBPortInfo: (id, state, voltage (mV), current (mA))
for info in usbpd_power_infos:
if != port:
power_watt = info.voltage * info.current / 1000000
self.UpdateACPower(power_watt, power_min, power_max)
result = testlog.CheckNumericParam(
'usbpdpower', power_watt, min=power_min, max=power_max)
if not result:
'Expecting (%s, %s) watt usbpd power but see %s' %
(power_min, power_max, power_watt))
return result
return False
return True
def runTest(self):
if self.fixture:
num_probes = 0
while True:
if self.args.retries is not None:
if self.CheckCondition():
num_probes += 1
if self.args.retries is not None and num_probes > self.args.retries:
self.FailTask('Failed after probing %d times' % num_probes)
# Prevent busy polling.