普通文本  |  161行  |  6.86 KB

# Copyright (c) 2011 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.

import logging, math, time

from autotest_lib.client.bin import test
from autotest_lib.client.common_lib import error
from autotest_lib.client.cros import rtc
from autotest_lib.client.cros.power import power_dashboard
from autotest_lib.client.cros.power import power_status
from autotest_lib.client.cros.power import power_telemetry_utils
from autotest_lib.client.cros.power import power_suspend
from autotest_lib.client.cros.power import power_utils


class power_Standby(test.test):
    """Measure Standby power test."""
    version = 1
    _percent_min_charge = 10
    _min_sample_hours = 0.1

    def initialize(self, pdash_note=''):
        """Reset force discharge state."""
        self._force_discharge_enabled = False
        self._pdash_note = pdash_note
        self._checkpoint_logger = power_status.CheckpointLogger()

    def run_once(self, test_hours=None, sample_hours=None,
                 max_milliwatts_standby=500, ac_ok=False,
                 force_discharge=False, suspend_state='', bypass_check=False):
        """Put DUT to suspend state for |sample_hours| and measure power."""
        if not power_utils.has_battery():
            raise error.TestNAError('Skipping test because DUT has no battery.')

        if test_hours < sample_hours:
            raise error.TestFail('Test hours must be greater than sample '
                                 'hours.')

        # If we're measuring < 6min of standby then the S0 time is not
        # negligible. Note, reasonable rule of thumb is S0 idle is ~10-20 times
        # standby power.
        if sample_hours < self._min_sample_hours and not bypass_check:
            raise error.TestFail('Must standby more than %.2f hours.' % \
                                 sample_hours)

        power_stats = power_status.get_status()

        if not ac_ok and power_stats.on_ac():
            raise error.TestError('On AC, please unplug power supply.')

        if force_discharge:
            if not power_stats.on_ac():
                raise error.TestError('Not on AC, please plug in power supply '
                                      'to attempt force discharge.')
            if not power_utils.charge_control_by_ectool(False):
                raise error.TestError('Unable to force discharge.')

            self._force_discharge_enabled = True

        charge_start = power_stats.battery[0].charge_now
        voltage_start = power_stats.battery[0].voltage_now

        max_hours = ((charge_start * voltage_start) /
                     (max_milliwatts_standby / 1000.))
        if max_hours < test_hours:
            raise error.TestFail('Battery not charged adequately for test.')

        suspender = power_suspend.Suspender(self.resultsdir,
                                            suspend_state=suspend_state)

        elapsed_hours = 0

        results = {}
        loop = 0
        start_ts = time.time()

        while elapsed_hours < test_hours:
            charge_before = power_stats.battery[0].charge_now
            before_suspend_secs = rtc.get_seconds()
            suspender.suspend(duration=sample_hours * 3600)
            after_suspend_secs = rtc.get_seconds()

            power_stats.refresh()
            if power_stats.percent_current_charge() < self._percent_min_charge:
                logging.warning('Battery = %.2f%%.  Too low to continue.')
                break

            # check that the RTC slept the correct amount of time as there could
            # potentially be another wake source that would spoil the test.
            actual_hours = (after_suspend_secs - before_suspend_secs) / 3600.0
            percent_diff = math.fabs((actual_hours - sample_hours) / (
                    (actual_hours + sample_hours) / 2) * 100)
            if percent_diff > 2 and not bypass_check:
                err = 'Requested standby time and actual varied by %.2f%%.' \
                    % percent_diff
                raise error.TestFail(err)

            # Check resulting charge consumption
            charge_used = charge_before - power_stats.battery[0].charge_now
            elapsed_hours += actual_hours
            logging.debug(
                    'loop%d done: loop hours %.3f, elapsed hours %.3f '
                    'charge used: %.3f', loop, actual_hours, elapsed_hours,
                    charge_used)
            loop += 1

        end_ts = time.time()
        offset = (end_ts - start_ts - elapsed_hours * 3600) / 2.
        offset += suspender.get_suspend_delay()
        start_ts += offset
        end_ts -= offset
        power_telemetry_utils.start_measurement(start_ts)
        power_telemetry_utils.end_measurement(end_ts)
        self._checkpoint_logger.checkpoint(self.tagged_testname,
                                           start_ts, end_ts)
        charge_end = power_stats.battery[0].charge_now
        total_charge_used = charge_start - charge_end
        if total_charge_used <= 0 and not bypass_check:
            raise error.TestError('Charge used is suspect.')

        voltage_end = power_stats.battery[0].voltage_now
        standby_hours = power_stats.battery[0].charge_full_design / \
                        total_charge_used * elapsed_hours
        energy_used = (voltage_start + voltage_end) / 2 * \
                      total_charge_used

        results['ah_charge_start'] = charge_start
        results['ah_charge_now'] = charge_end
        results['ah_charge_used'] = total_charge_used
        results['force_discharge'] = self._force_discharge_enabled
        results['hours_standby_time'] = standby_hours
        results['hours_standby_time_tested'] = elapsed_hours
        results['v_voltage_start'] = voltage_start
        results['v_voltage_now'] = voltage_end
        results['w_energy_rate'] = energy_used / elapsed_hours
        results['wh_energy_used'] = energy_used

        self.write_perf_keyval(results)
        pdash = power_dashboard.SimplePowerLoggerDashboard(
                end_ts - start_ts, results['w_energy_rate'],
                self.tagged_testname, start_ts, self.resultsdir,
                note=self._pdash_note)
        pdash.upload()
        self._checkpoint_logger.save_checkpoint_data(self.resultsdir)

        self.output_perf_value(description='hours_standby_time',
                               value=results['hours_standby_time'],
                               units='hours', higher_is_better=True)
        self.output_perf_value(description='w_energy_rate',
                               value=results['w_energy_rate'], units='watts',
                               higher_is_better=False)

        # need to sleep for some time to allow network connection to return
        time.sleep(10)

    def cleanup(self):
        """Clean up force discharge."""
        if self._force_discharge_enabled:
            power_utils.charge_control_by_ectool(True)