# Copyright (c) 2014 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. """Tests for the servo `power_state` control. ## Purpose This test exists to ensure that servo's `power_state` control provides a consistent, hardware-independent implementation across all models of Chrome hardware. ==== NOTE NOTE NOTE ==== YOU ARE NOT ALLOWED TO CHANGE THIS TEST TO TREAT DIFFERENT HARDWARE DIFFERENTLY. In particular, importing from `faft.config` in order to customize this test for different hardware is never allowed. This has been tried twice before; anyone making a third attempt is liable to be taken out and shot. Or at least, be given a good talking to. This test is intended to enforce a contract between hardware-independent parts of Autotest (especially the repair code) and hardware-dependent implementations of servo in `hdctools`. The contract imposes requirements and limitations on both sides. ## Requirements on the servo implementation in `hdctools` ### Setting `power_state:off` Setting `power_state:off` must turn the DUT "off" unconditionally. The operation must have the same effect regardless of the state of the DUT prior to the operation. The operation is not allowed to fail. The meaning of "off" does not require any specific component to be actually powered off, provided that the DUT does not respond on the network, and does not respond to USB devices being plugged or unplugged. Some examples of "off" that are acceptable: * A system in ACPI power state S5. * A system held in reset indefinitely by asserting `cold_reset:on`. * In general, any system where power is not supplied to the AP. While a DUT is turned off, it is allowed to respond to the power button, the lid, and to the reset signals in ways that are hardware dependent. The signals may be ignored, or they may have any other effect that's appropriate to the hardware, such as turning the DUT on. ### Setting `power_state:on` and `power_state:rec` After applying `power_state:off` to turn a DUT off, setting `power_state:on` or `power_state:rec` will turn the DUT on. * Setting "on" turns the DUT on in normal mode. The DUT will boot normally as for a cold start. * Setting "rec" turns the DUT on in recovery mode. The DUT will go to the recovery screen and wait for a USB stick to be inserted. If a DUT is not off because of setting `power_state:off`, the response to "on" and "rec" may be hardware dependent. The settings may turn the device on as specified, they may have no effect, or they may provide any other response that's appropriate to the hardware. ### Setting `power_state:reset` Applying `power_state:reset` has the same visible effects as setting `power_state:off` followed by `power_state:on`. The operation must have the same effect regardless of the state of the DUT prior to the operation. The operation is not allowed to fail. Additionally, applying reset will assert and deassert `cold_reset` to ensure a full hardware reset. ### Timing Constraints The servo implementation for `power_state` cannot impose timing constraints on Autotest. If the hardware imposes constraints, the servo implemention must provide the necessary time delays by sleeping. In particular: * After `power_state:off`, it is allowed to immediately apply either `on` or `rec`. * After `rec`, USB stick insertion must be recognized immediately. * Setting `power_state:reset` twice in a row must work reliably. ### Requirements on Autotest ### Setting `power_state:off` Once a DUT has been turned "off", changing signals such as the power button, the lid, or reset signals isn't allowed. The only allowed state changes are these: * USB muxes may be switched at will. * The `power_state` control can be set to any valid setting. Any other operation may cause the DUT to change state unpredictably (e.g. by powering the DUT on). ## Setting `power_state:on` and `power_state:rec` Autotest can only apply `power_state:on` or `power_state:rec` if the DUT was previously turned off with `power_state:off`. """ import logging import time # STOP! You are not allowed to import anything from FAFT. Read the # comments above. from autotest_lib.client.common_lib import error from autotest_lib.server import test class platform_ServoPowerStateController(test.test): """Test servo can power on and off DUT in recovery and non-recovery mode.""" version = 1 def initialize(self, host): """Initialize DUT for testing.""" pass def cleanup(self): """Clean up DUT after servo actions.""" if not self.host.ssh_ping(): # Power off, then power on DUT from internal storage. self.controller.power_off() self.host.servo.switch_usbkey('off') self.controller.power_on(self.controller.REC_OFF) def assert_dut_on(self, rec_on=False): """Confirm DUT is powered on, claim test failure if DUT is off. @param rec_on: True if DUT should boot from external USB stick as in recovery mode. @raise TestFail: If DUT is off or DUT boot from wrong source. """ if not self.host.wait_up(timeout=300): raise error.TestFail('power_state:%s did not turn DUT on.' % ('rec' if rec_on else 'on')) # Check boot source. Raise TestFail if DUT boot from wrong source. boot_from_usb = self.host.is_boot_from_usb() if boot_from_usb != rec_on: boot_source = ('USB' if boot_from_usb else 'non-removable storage') raise error.TestFail('power_state:%s booted from %s.' % ('rec' if rec_on else 'on', boot_source)) def assert_dut_off(self, error_message): """Confirm DUT is off and does not turn back on after 30 seconds. @param error_message: Error message to raise if DUT stays on. @raise TestFail: If DUT stays on. """ if not self.host.ping_wait_down(timeout=10): raise error.TestFail(error_message) if self.host.ping_wait_up(timeout=30): raise error.TestFail('%s. %s' % (error_message, 'DUT turns back on' ' after it is turned off.')) def test_with_usb_plugged_in(self): """Run test when USB stick is plugged in to servo.""" logging.info('Power off DUT') self.controller.power_off() self.assert_dut_off('power_state:off did not turn DUT off.') logging.info('Power DUT on in recovery mode, DUT shall boot from USB.') self.host.servo.switch_usbkey('off') self.controller.power_on(self.controller.REC_ON) self.assert_dut_off('power_state:rec didn\'t stay at recovery screen.') self.host.servo.switch_usbkey('dut') time.sleep(30) self.assert_dut_on(rec_on=True) logging.info('Power off DUT which is up in recovery mode.') self.controller.power_off() self.assert_dut_off('power_state:off failed after boot from external ' 'USB stick.') logging.info('Power DUT off in recovery mode without booting.') self.host.servo.switch_usbkey('off') self.controller.power_on(self.controller.REC_ON) self.controller.power_off() self.assert_dut_off('power_state:off failed at recovery screen ') # Power DUT on in non-recovery mode with USB stick plugged in. # DUT shall boot from internal storage. logging.info('Power on DUT in non-recovery mode.') self.host.servo.switch_usbkey('dut') self.controller.power_on(self.controller.REC_OFF) self.assert_dut_on() self.host.servo.switch_usbkey('off') def test_with_usb_unplugged(self): """Run test when USB stick is not plugged in servo.""" # Power off DUT regardless its current status. logging.info('Power off DUT.') self.controller.power_off() self.assert_dut_off('power_state:off did not turn DUT off.') # Try to power off the DUT again, make sure the DUT stays off. logging.info('Power off DUT which is already off.') self.controller.power_off() self.assert_dut_off('power_state:off turned DUT on.') # USB stick should be unplugged before the test. self.host.servo.switch_usbkey('off') logging.info('Power on in non-recovery mode.') self.controller.power_on(self.controller.REC_OFF) self.assert_dut_on(rec_on=False) logging.info('Power DUT off and on without delay. DUT should be ' 'on after power_on is completed.') self.controller.power_off() self.controller.power_on(self.controller.REC_OFF) self.assert_dut_on(rec_on=False) logging.info('Power off DUT which is up in non-recovery mode.') self.controller.power_off() self.assert_dut_off('power_state:off failed after boot from ' 'internal storage.') logging.info('Power DUT off and reset. DUT should be on after ' 'reset is completed.') self.controller.reset() self.assert_dut_on(rec_on=False) logging.info('Reset DUT when it\'s on. DUT should be on after ' 'reset is completed.') boot_id = self.host.get_boot_id() self.controller.reset() self.assert_dut_on(rec_on=False) new_boot_id = self.host.get_boot_id() if not new_boot_id or boot_id == new_boot_id: raise error.TestFail('power_state:reset failed to reboot DUT.') def run_once(self, host, usb_available=True): """Run the test. @param host: host object of tested DUT. @param usb_plugged_in: True if USB stick is plugged in servo. """ self.host = host self.controller = host.servo.get_power_state_controller() self.test_with_usb_unplugged() if usb_available: self.test_with_usb_plugged_in()