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.
Description
-----------
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.
Dependency
----------
- Need a power source.
For plugged with required power range:
- Need a USB PD power source with required power range.
Examples
--------
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'
_AC_TYPE_USB_PD = 'USB_PD'
class ACPowerTest(test_case.TestCase):
"""A test to instruct the operator to plug/unplug AC power.
Args:
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
warning.
"""
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]',
default=None),
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.',
default=None),
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.',
default=2),
]
def setUp(self):
self._power = device_utils.CreateDUTInterface().power
if not self.args.online:
instruction = _('Unplug the charger.')
elif self.args.power_type:
instruction = _('Plug in the charger ({type})', type=self.args.power_type)
else:
instruction = _('Plug in the charger')
self.ui.SetInstruction(instruction)
self.ui.SetState(
'<div id="%s"></div><div id="%s"></div><div id="%s"></div>' %
(_PROBE_TIMES_ID, _AC_STATUS_ID, _AC_POWER_ID))
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:
testlog.UpdateParam(
name='usbpd_power',
description='Detected usbpd power.',
value_unit='mW')
# 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):
self.ui.SetHTML(
_('Detected power {watt} W, '
'required power range ({min_watt} W, {max_watt} W)',
watt=watt,
min_watt=min_watt,
max_watt=max_watt),
id=_AC_POWER_ID)
def UpdateACStatus(self, status):
self.ui.SetHTML(status, id=_AC_STATUS_ID)
def UpdateProbeTimes(self, num_probes):
self.ui.SetHTML(
_('Probed {times} / {total}', times=num_probes,
total=self.args.retries),
id=_PROBE_TIMES_ID)
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 != self.args.online:
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))
session.console.warning(
'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 info.id != port:
continue
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:
session.console.warning(
'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:
self.fixture.SetDeviceEngaged(bft_fixture.BFTFixture.Device.AC_ADAPTER,
self.args.online)
num_probes = 0
while True:
if self.args.retries is not None:
self.UpdateProbeTimes(num_probes)
if self.CheckCondition():
break
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.
self.Sleep(self.args.polling_period_secs)