Home | History | Annotate | Download | only in platform_ServoPowerStateController
      1 # Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """Tests for the servo `power_state` control.
      6 
      7 ## Purpose
      8 This test exists to ensure that servo's `power_state` control provides a
      9 consistent, hardware-independent implementation across all models of
     10 Chrome hardware.
     11 
     12     ==== NOTE NOTE NOTE ====
     13     YOU ARE NOT ALLOWED TO CHANGE THIS TEST TO TREAT DIFFERENT HARDWARE
     14     DIFFERENTLY.
     15 
     16 In particular, importing from `faft.config` in order to customize this
     17 test for different hardware is never allowed.  This has been tried twice
     18 before; anyone making a third attempt is liable to be taken out and
     19 shot.  Or at least, be given a good talking to.
     20 
     21 This test is intended to enforce a contract between hardware-independent
     22 parts of Autotest (especially the repair code) and hardware-dependent
     23 implementations of servo in `hdctools`.  The contract imposes requirements
     24 and limitations on both sides.
     25 
     26 ## Requirements on the servo implementation in `hdctools`
     27 ### Setting `power_state:off`
     28 Setting `power_state:off` must turn the DUT "off" unconditionally.  The
     29 operation must have the same effect regardless of the state of the DUT
     30 prior to the operation.  The operation is not allowed to fail.
     31 
     32 The meaning of "off" does not require any specific component to be
     33 actually powered off, provided that the DUT does not respond on the
     34 network, and does not respond to USB devices being plugged or unplugged.
     35 Some examples of "off" that are acceptable:
     36   * A system in ACPI power state S5.
     37   * A system held in reset indefinitely by asserting `cold_reset:on`.
     38   * In general, any system where power is not supplied to the AP.
     39 
     40 While a DUT is turned off, it is allowed to respond to the power button,
     41 the lid, and to the reset signals in ways that are hardware dependent.
     42 The signals may be ignored, or they may have any other effect that's
     43 appropriate to the hardware, such as turning the DUT on.
     44 
     45 ### Setting `power_state:on` and `power_state:rec`
     46 After applying `power_state:off` to turn a DUT off, setting
     47 `power_state:on` or `power_state:rec` will turn the DUT on.
     48   * Setting "on" turns the DUT on in normal mode.  The DUT will
     49     boot normally as for a cold start.
     50   * Setting "rec" turns the DUT on in recovery mode.  The DUT
     51     will go to the recovery screen and wait for a USB stick to be
     52     inserted.
     53 
     54 If a DUT is not off because of setting `power_state:off`, the response
     55 to "on" and "rec" may be hardware dependent.  The settings may turn the
     56 device on as specified, they may have no effect, or they may provide any
     57 other response that's appropriate to the hardware.
     58 
     59 ### Setting `power_state:reset`
     60 Applying `power_state:reset` has the same visible effects as setting
     61 `power_state:off` followed by `power_state:on`.  The operation must have
     62 the same effect regardless of the state of the DUT prior to the
     63 operation.  The operation is not allowed to fail.
     64 
     65 Additionally, applying reset will assert and deassert `cold_reset` to
     66 ensure a full hardware reset.
     67 
     68 ### Timing Constraints
     69 The servo implementation for `power_state` cannot impose timing
     70 constraints on Autotest.  If the hardware imposes constraints, the servo
     71 implemention must provide the necessary time delays by sleeping.
     72 
     73 In particular:
     74   * After `power_state:off`, it is allowed to immediately apply either
     75     `on` or `rec`.
     76   * After `rec`, USB stick insertion must be recognized immediately.
     77   * Setting `power_state:reset` twice in a row must work reliably.
     78 
     79 ### Requirements on Autotest
     80 ### Setting `power_state:off`
     81 Once a DUT has been turned "off", changing signals such as the power
     82 button, the lid, or reset signals isn't allowed.  The only allowed
     83 state changes are these:
     84   * USB muxes may be switched at will.
     85   * The `power_state` control can be set to any valid setting.
     86 
     87 Any other operation may cause the DUT to change state unpredictably
     88 (e.g. by powering the DUT on).
     89 
     90 ## Setting `power_state:on` and `power_state:rec`
     91 Autotest can only apply `power_state:on` or `power_state:rec` if the DUT
     92 was previously turned off with `power_state:off`.
     93 
     94 """
     95 
     96 import logging
     97 import time
     98 
     99 # STOP!  You are not allowed to import anything from FAFT.  Read the
    100 # comments above.
    101 from autotest_lib.client.common_lib import error
    102 from autotest_lib.server import test
    103 
    104 
    105 class platform_ServoPowerStateController(test.test):
    106     """Test servo can power on and off DUT in recovery and non-recovery mode."""
    107     version = 1
    108 
    109 
    110     def initialize(self, host):
    111         """Initialize DUT for testing."""
    112         pass
    113 
    114 
    115     def cleanup(self):
    116         """Clean up DUT after servo actions."""
    117         if not self.host.ssh_ping():
    118             # Power off, then power on DUT from internal storage.
    119             self.controller.power_off()
    120             self.host.servo.switch_usbkey('off')
    121             self.controller.power_on(self.controller.REC_OFF)
    122 
    123 
    124     def assert_dut_on(self, rec_on=False):
    125         """Confirm DUT is powered on, claim test failure if DUT is off.
    126 
    127         @param rec_on: True if DUT should boot from external USB stick as in
    128                        recovery mode.
    129 
    130         @raise TestFail: If DUT is off or DUT boot from wrong source.
    131         """
    132         if not self.host.wait_up(timeout=300):
    133             raise error.TestFail('power_state:%s did not turn DUT on.' %
    134                                  ('rec' if rec_on else 'on'))
    135 
    136         # Check boot source. Raise TestFail if DUT boot from wrong source.
    137         boot_from_usb = self.host.is_boot_from_usb()
    138         if boot_from_usb != rec_on:
    139             boot_source = ('USB' if boot_from_usb else
    140                            'non-removable storage')
    141             raise error.TestFail('power_state:%s booted from %s.' %
    142                                  ('rec' if rec_on else 'on', boot_source))
    143 
    144 
    145     def assert_dut_off(self, error_message):
    146         """Confirm DUT is off and does not turn back on after 30 seconds.
    147 
    148         @param error_message: Error message to raise if DUT stays on.
    149         @raise TestFail: If DUT stays on.
    150         """
    151         if not self.host.ping_wait_down(timeout=10):
    152             raise error.TestFail(error_message)
    153 
    154         if self.host.ping_wait_up(timeout=30):
    155             raise error.TestFail('%s. %s' % (error_message, 'DUT turns back on'
    156                                              ' after it is turned off.'))
    157 
    158 
    159     def test_with_usb_plugged_in(self):
    160         """Run test when USB stick is plugged in to servo."""
    161         logging.info('Power off DUT')
    162         self.controller.power_off()
    163         self.assert_dut_off('power_state:off did not turn DUT off.')
    164 
    165         logging.info('Power DUT on in recovery mode, DUT shall boot from USB.')
    166         self.host.servo.switch_usbkey('off')
    167         self.controller.power_on(self.controller.REC_ON)
    168         self.assert_dut_off('power_state:rec didn\'t stay at recovery screen.')
    169 
    170         self.host.servo.switch_usbkey('dut')
    171         time.sleep(30)
    172         self.assert_dut_on(rec_on=True)
    173 
    174         logging.info('Power off DUT which is up in recovery mode.')
    175         self.controller.power_off()
    176         self.assert_dut_off('power_state:off failed after boot from external '
    177                             'USB stick.')
    178 
    179         logging.info('Power DUT off in recovery mode without booting.')
    180         self.host.servo.switch_usbkey('off')
    181         self.controller.power_on(self.controller.REC_ON)
    182         self.controller.power_off()
    183         self.assert_dut_off('power_state:off failed at recovery screen ')
    184 
    185         # Power DUT on in non-recovery mode with USB stick plugged in.
    186         # DUT shall boot from internal storage.
    187         logging.info('Power on DUT in non-recovery mode.')
    188         self.host.servo.switch_usbkey('dut')
    189         self.controller.power_on(self.controller.REC_OFF)
    190         self.assert_dut_on()
    191         self.host.servo.switch_usbkey('off')
    192 
    193 
    194     def test_with_usb_unplugged(self):
    195         """Run test when USB stick is not plugged in servo."""
    196         # Power off DUT regardless its current status.
    197         logging.info('Power off DUT.')
    198         self.controller.power_off()
    199         self.assert_dut_off('power_state:off did not turn DUT off.')
    200 
    201         # Try to power off the DUT again, make sure the DUT stays off.
    202         logging.info('Power off DUT which is already off.')
    203         self.controller.power_off()
    204         self.assert_dut_off('power_state:off turned DUT on.')
    205 
    206         # USB stick should be unplugged before the test.
    207         self.host.servo.switch_usbkey('off')
    208 
    209         logging.info('Power on in non-recovery mode.')
    210         self.controller.power_on(self.controller.REC_OFF)
    211         self.assert_dut_on(rec_on=False)
    212 
    213         logging.info('Power DUT off and on without delay. DUT should be '
    214                      'on after power_on is completed.')
    215         self.controller.power_off()
    216         self.controller.power_on(self.controller.REC_OFF)
    217         self.assert_dut_on(rec_on=False)
    218 
    219         logging.info('Power off DUT which is up in non-recovery mode.')
    220         self.controller.power_off()
    221         self.assert_dut_off('power_state:off failed after boot from '
    222                             'internal storage.')
    223 
    224         logging.info('Power DUT off and reset. DUT should be on after '
    225                      'reset is completed.')
    226         self.controller.reset()
    227         self.assert_dut_on(rec_on=False)
    228 
    229         logging.info('Reset DUT when it\'s on. DUT should be on after '
    230                      'reset is completed.')
    231         boot_id = self.host.get_boot_id()
    232         self.controller.reset()
    233         self.assert_dut_on(rec_on=False)
    234         new_boot_id = self.host.get_boot_id()
    235         if not new_boot_id or boot_id == new_boot_id:
    236             raise error.TestFail('power_state:reset failed to reboot DUT.')
    237 
    238 
    239     def run_once(self, host, usb_available=True):
    240         """Run the test.
    241 
    242         @param host: host object of tested DUT.
    243         @param usb_plugged_in: True if USB stick is plugged in servo.
    244         """
    245         self.host = host
    246         self.controller = host.servo.get_power_state_controller()
    247 
    248         self.test_with_usb_unplugged()
    249         if usb_available:
    250             self.test_with_usb_plugged_in()
    251