# Lint as: python2, python3 # Copyright 2021 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. """Helper class for power autotests that force DUT to discharge with EC.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function import logging import time from autotest_lib.client.common_lib import error from autotest_lib.client.cros import ec from autotest_lib.client.cros.power import power_utils from six.moves import range _FORCE_DISCHARGE_SETTINGS = ['false', 'true', 'optional'] def _parse(force_discharge): """ Parse and return force discharge setting. @param force_discharge: string of whether to tell ec to discharge battery even when the charger is plugged in. 'false' means no forcing discharge; 'true' means forcing discharge and raising an error when it fails; 'optional' means forcing discharge when possible but not raising an error when it fails, which is more friendly to devices without a battery. @return: string representing valid force discharge setting. @raise error.TestError: for invalid force discharge setting. """ setting = str(force_discharge).lower() if setting not in _FORCE_DISCHARGE_SETTINGS: raise error.TestError( 'Force discharge setting \'%s\' need to be one of %s.' % (str(force_discharge), _FORCE_DISCHARGE_SETTINGS)) return setting def _wait_for_battery_discharge(status): """ Polling every 100ms for 2 seconds until battery is discharging. This normally would take about 350ms. @param status: DUT power status object. @return: boolean indicating force discharge success. """ for _ in range(20): status.refresh() if status.battery_discharging(): return True time.sleep(0.1) return False def process(force_discharge, status): """ Perform force discharge steps. @param force_discharge: string of whether to tell ec to discharge battery even when the charger is plugged in. 'false' means no forcing discharge; 'true' means forcing discharge and raising an error when it fails; 'optional' means forcing discharge when possible but not raising an error when it fails, which is more friendly to devices without a battery. @param status: DUT power status object. @return: bool to indicate whether force discharge steps are successful. Note that DUT cannot force discharge if DUT is not connected to AC. @raise error.TestError: for invalid force discharge setting. @raise error.TestNAError: when force_discharge is 'true' and the DUT is incapable of forcing discharge. @raise error.TestError: when force_discharge is 'true' and the DUT command to force discharge fails. """ force_discharge = _parse(force_discharge) if force_discharge == 'true': if not status.battery: raise error.TestNAError('DUT does not have battery. ' 'Could not force discharge.') if not ec.has_cros_ec(): raise error.TestNAError('DUT does not have CrOS EC. ' 'Could not force discharge.') if not power_utils.charge_control_by_ectool(False): raise error.TestError('Could not run battery force discharge.') if not _wait_for_battery_discharge(status): logging.warning('Battery does not report discharging state.') return True elif force_discharge == 'optional': if not status.battery: logging.warning('DUT does not have battery. ' 'Do not force discharge.') return False if not ec.has_cros_ec(): logging.warning('DUT does not have CrOS EC. ' 'Do not force discharge.') return False if not power_utils.charge_control_by_ectool(False): logging.warning('Could not run battery force discharge. ' 'Do not force discharge.') return False if not _wait_for_battery_discharge(status): logging.warning('Battery does not report discharging state.') return True elif force_discharge == 'false': return False def restore(force_discharge_success): """ Set DUT back to charging. @param force_discharge_success: if DUT previously forced discharge successfully, set DUT back to charging. """ if force_discharge_success: if not power_utils.charge_control_by_ectool(True): logging.warning('Can not restore from force discharge.')