普通文本  |  164行  |  6.68 KB

# 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.

import logging
import time

from autotest_lib.client.common_lib import error
from autotest_lib.server import autotest
from autotest_lib.server.cros.faft.firmware_test import FirmwareTest


class firmware_Cr50DeepSleepStress(FirmwareTest):
    """Verify cr50 deep sleep after running power_SuspendStress.

    Cr50 should enter deep sleep every suspend. Verify that by checking the
    idle deep sleep count.

    @param suspend_count: The number of times to reboot or suspend the device.
    @param reset_type: a str with the cycle type: 'mem' or 'reboot'
    """
    version = 1

    SLEEP_DELAY = 20
    MIN_RESUME = 15
    MIN_SUSPEND = 15
    MEM = 'mem'

    def initialize(self, host, cmdline_args, suspend_count, reset_type):
        """Make sure the test is running with access to the cr50 console"""
        super(firmware_Cr50DeepSleepStress, self).initialize(host, cmdline_args)
        if not hasattr(self, 'cr50'):
            raise error.TestNAError('Test can only be run on devices with '
                                    'access to the Cr50 console')
        # Reset the device
        self.servo.get_power_state_controller().reset()

        # Save the original version, so we can make sure cr50 doesn't rollback.
        self.original_cr50_version = self.cr50.get_active_version_info()


    def check_cr50_state(self, expected_count, expected_version):
        """Check the reboot count and version match the expected values"""
        count = self.cr50.get_deep_sleep_count()
        version = self.cr50.get_active_version_info()

        logging.info('running %s', version)
        logging.info('After %s reboots, Cr50 resumed from deep sleep %s times',
                expected_count, count)

        mismatch = []
        if count != expected_count:
            mismatch.append('count mismatch: expected %s got %s' %
                    (expected_count, count))
        if version != expected_version:
            mismatch.append('version mismatch: expected %s got %s' %
                    (expected_version, version))

        if mismatch:
            raise error.TestFail('Deep Sleep Failure: "%s"' %
                    '" and "'.join(mismatch))


    def run_reboots(self, suspend_count):
        """Reboot the device the requested number of times

        @param suspend_count: the number of times to reboot the device.
        """
        if self.cr50.using_ccd():
            raise error.TestNAError('Reboot deep sleep tests can only be run '
                    'with a servo flex')
        # This test may be running on servo v4 with servo micro. That servo v4
        # may have a type A cable or type C cable for data communication with
        # the DUT. If it is using a type C cable, that cable will look like a
        # debug accessory. Run ccd_disable, to stop debug accessory mode and
        # prevent CCD from interfering with deep sleep. Running ccd_disable on
        # a type A servo v4 or any other servo version isn't harmful.
        self.cr50.ccd_disable()

        for i in range(suspend_count):
            # Power off the device
            self.servo.get_power_state_controller().power_off()
            time.sleep(self.MIN_SUSPEND)

            # Power on the device
            self.servo.power_short_press()
            time.sleep(self.MIN_RESUME)

            # Make sure it booted into normal mode
            self.check_state((self.checkers.crossystem_checker,
                              {'mainfw_type': 'normal'}))


    def run_suspend_resume(self, host, suspend_count):
        """Suspend the device the requested number of times

        @param host: the host object representing the DUT.
        @param suspend_count: the number of times to suspend the device.
        """
        client_at = autotest.Autotest(host)

        # Disable CCD so Cr50 can enter deep sleep
        self.cr50.ccd_disable()
        # Duration is set to 0, because it is required but unused when
        # iterations is given.
        client_at.run_test('power_SuspendStress', tag='idle',
                           duration=0,
                           min_suspend=self.MIN_SUSPEND,
                           min_resume=self.MIN_RESUME,
                           check_connection=False,
                           iterations=suspend_count,
                           suspend_state=self.MEM)
        # Reenable CCD
        self.cr50.ccd_enable()


    def get_expected_ds_count(self, host, reset_type, suspend_count):
        """Returns the expected deep sleep count"""
        is_arm = host.run('arch').stdout.strip() in ['aarch64', 'armv7l']
        # x86 devices should suspend once per reset. ARM will only suspend
        # if the device enters s5.
        return 0 if (reset_type != 'reboot' and is_arm) else suspend_count


    def run_once(self, host, suspend_count, reset_type):
        """Verify deep sleep after suspending for the given number of cycles

        The test either suspends to s3 or reboots the device depending on
        reset_type. There are two valid reset types: mem and reboot. The test
        will make sure that the device is off or in s3 long enough to ensure
        Cr50 should be able to enter deep sleep. At the end of the test, it
        checks that Cr50 entered deep sleep the same number of times it
        suspended.

        @param host: the host object representing the DUT.
        @param suspend_count: The number of cycles to suspend or reboot the
                device.
        @param reset_type: a str with the cycle type: 'mem' or 'reboot'
        """
        if self.MIN_SUSPEND + self.MIN_RESUME < self.SLEEP_DELAY:
            logging.info('Minimum suspend-resume cycle is %ds. This is '
                         'shorter than the Cr50 idle timeout. Cr50 may not '
                         'enter deep sleep every cycle',
                         self.MIN_SUSPEND + self.MIN_RESUME)
        if not suspend_count:
            raise error.TestFail('Need to provide non-zero suspend_count')

        # Clear the deep sleep count
        logging.info('Clear Cr50 deep sleep count')
        self.cr50.clear_deep_sleep_count()

        if reset_type == 'reboot':
            self.run_reboots(suspend_count)
        elif reset_type == 'mem':
            self.run_suspend_resume(host, suspend_count)
        else:
            raise error.TestNAError('Invalid reset_type. Use "mem" or "reboot"')

        # Cr50 should enter deep sleep once per suspend cycle if deep sleep is
        # supported
        expected_ds_count = self.get_expected_ds_count(host, reset_type,
                suspend_count)
        self.check_cr50_state(expected_ds_count, self.original_cr50_version)