477 lines
16 KiB
Python
477 lines
16 KiB
Python
# 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.
|
|
|
|
import abc
|
|
import logging
|
|
import six
|
|
|
|
from autotest_lib.client.common_lib import error
|
|
|
|
|
|
@six.add_metaclass(abc.ABCMeta)
|
|
class _BaseMenuModeSwitcher:
|
|
"""
|
|
Base class for mode switch with menu navigator.
|
|
"""
|
|
|
|
def __init__(self, faft_framework, menu_navigator):
|
|
self.test = faft_framework
|
|
self.faft_config = self.test.faft_config
|
|
self.servo = self.test.servo
|
|
self.menu = menu_navigator
|
|
self.checkers = faft_framework.checkers
|
|
|
|
self.minidiag_enabled = self.faft_config.minidiag_enabled
|
|
self.minios_enabled = self.faft_config.minios_enabled
|
|
|
|
@abc.abstractmethod
|
|
def trigger_rec_to_dev(self):
|
|
"""
|
|
Trigger to-dev transition.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
@abc.abstractmethod
|
|
def dev_boot_from_internal(self):
|
|
"""
|
|
Boot from internal disk in developer mode.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
@abc.abstractmethod
|
|
def dev_boot_from_external(self):
|
|
"""Boot from external disk in developer mode."""
|
|
raise NotImplementedError
|
|
|
|
@abc.abstractmethod
|
|
def trigger_dev_to_normal(self):
|
|
"""
|
|
Trigger dev-to-norm transition.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
@abc.abstractmethod
|
|
def power_off(self):
|
|
"""
|
|
Power off the device.
|
|
|
|
This method should work in both developer and recovery screens.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
|
|
class _TabletDetachableMenuModeSwitcher(_BaseMenuModeSwitcher):
|
|
"""
|
|
Mode switcher with menu navigator for legacy menu UI.
|
|
|
|
The "legacy menu UI" is an old menu-based UI, which has been replaced
|
|
by the new one, called "menu UI".
|
|
"""
|
|
|
|
def trigger_rec_to_dev(self):
|
|
"""
|
|
Trigger to-dev transition.
|
|
"""
|
|
self.test.switcher.trigger_rec_to_dev()
|
|
|
|
def dev_boot_from_internal(self):
|
|
"""
|
|
Boot from internal disk in developer mode.
|
|
|
|
Menu items in developer warning screen:
|
|
0. Developer Options
|
|
1. Show Debug Info
|
|
2. Enable OS Verification
|
|
*3. Power Off
|
|
4. Language
|
|
|
|
Menu items in developer boot options screen:
|
|
0. Boot From Network
|
|
1. Boot Legacy BIOS
|
|
2. Boot From USB or SD Card
|
|
*3. Boot From Internal Disk
|
|
4. Cancel
|
|
5. Power Off
|
|
|
|
(*) is the default selection.
|
|
"""
|
|
self.test.wait_for('firmware_screen')
|
|
self.menu.move_to(3, 0)
|
|
self.menu.select('Selecting "Developer Options"...')
|
|
self.test.wait_for('keypress_delay')
|
|
self.menu.select('Selecting "Boot From Internal Disk"...')
|
|
|
|
def dev_boot_from_external(self):
|
|
"""Boot from external disk in developer mode.
|
|
|
|
Menu items in developer warning screen:
|
|
0. Developer Options
|
|
1. Show Debug Info
|
|
2. Enable OS Verification
|
|
*3. Power Off
|
|
4. Language
|
|
|
|
Menu items in developer boot options screen:
|
|
0. Boot From Network
|
|
1. Boot Legacy BIOS
|
|
2. Boot From USB or SD Card
|
|
*3. Boot From Internal Disk
|
|
4. Cancel
|
|
5. Power Off
|
|
6. Language
|
|
"""
|
|
self.test.wait_for('firmware_screen')
|
|
self.menu.move_to(3, 0)
|
|
self.menu.select('Selecting "Developer Options"...')
|
|
self.test.wait_for('keypress_delay')
|
|
self.menu.move_to(3, 2)
|
|
self.menu.select('Selecting "Boot From USB or SD Card"...')
|
|
|
|
def trigger_dev_to_normal(self):
|
|
"""
|
|
Trigger dev-to-norm transition.
|
|
|
|
Menu items in developer warning screen:
|
|
0. Developer Options
|
|
1. Show Debug Info
|
|
2. Enable OS Verification
|
|
*3. Power Off
|
|
4. Language
|
|
|
|
Menu items in to-norm confirmation screen:
|
|
*0. Confirm Enabling OS Verification
|
|
1. Cancel
|
|
2. Power Off
|
|
3. Language
|
|
|
|
(*) is the default selection.
|
|
"""
|
|
self.test.wait_for('firmware_screen')
|
|
self.menu.move_to(3, 2)
|
|
self.menu.select('Selecting "Enable OS Verification"...')
|
|
self.test.wait_for('keypress_delay')
|
|
self.menu.select('Selecing "Confirm Enabling OS Verification"...')
|
|
|
|
def power_off(self):
|
|
"""
|
|
Power off the device.
|
|
|
|
This method should work in both developer and recovery screens.
|
|
"""
|
|
self.test.wait_for('firmware_screen')
|
|
# Either in developer or recovery screen, the "Power Off" option is the
|
|
# default one.
|
|
self.menu.select('Selecting "Power Off"...')
|
|
|
|
|
|
class _MenuModeSwitcher(_BaseMenuModeSwitcher):
|
|
"""
|
|
Mode switcher with menu navigator for menu UI.
|
|
|
|
The "menu UI" aims to replace both "legacy clamshell UI" and "legacy
|
|
menu UI". See chromium:1033815 for the discussion about the naming.
|
|
|
|
Menu items in recovery select screen:
|
|
0. Language
|
|
1. Recovery using phone (always hidden)
|
|
2. Recovery using external disk
|
|
3. Recovery using internet connection (shown if minios_enabled)
|
|
4. Launch diagnostics (shown if minidiag_enabled)
|
|
5. Advanced options
|
|
6. Power off
|
|
"""
|
|
RECOVERY_SELECT_ITEM_COUNT = 7
|
|
|
|
def _confirm_to_dev(self):
|
|
if self.faft_config.rec_button_dev_switch:
|
|
logging.info('Confirm to-dev by RECOVERY button')
|
|
self.servo.toggle_recovery_switch()
|
|
elif self.faft_config.power_button_dev_switch:
|
|
logging.info('Confirm to-dev by POWER button')
|
|
self.servo.power_normal_press()
|
|
else:
|
|
self.menu.select('Confirm to-dev by menu selection')
|
|
|
|
def trigger_rec_to_dev(self):
|
|
"""
|
|
Trigger to-dev transition.
|
|
|
|
Menu items in advanced options screen:
|
|
0. Language
|
|
*1. Enable developer mode
|
|
2. Back
|
|
3. Power off
|
|
|
|
Menu items in to-dev screen:
|
|
0. Language
|
|
*1. Confirm
|
|
2. Cancel
|
|
3. Power off
|
|
|
|
(*) is the default selection.
|
|
"""
|
|
self.test.wait_for('firmware_screen')
|
|
# The default selection is unknown, navigate to the last item first
|
|
self.menu.move_to(0, self.RECOVERY_SELECT_ITEM_COUNT)
|
|
# Navigate to "Advanced options"
|
|
self.menu.up()
|
|
self.test.wait_for('keypress_delay')
|
|
self.menu.select('Selecting "Advanced options"...')
|
|
self.test.wait_for('keypress_delay')
|
|
self.menu.select('Selecting "Enable developer mode"...')
|
|
self.test.wait_for('keypress_delay')
|
|
# Confirm to-dev transition
|
|
self._confirm_to_dev()
|
|
|
|
def dev_boot_from_internal(self):
|
|
"""
|
|
Boot from internal disk in developer mode.
|
|
|
|
Menu items in developer mode screen:
|
|
0. Language
|
|
1. Return to secure mode
|
|
2. Boot from internal disk
|
|
3. Boot from external disk
|
|
4. Advanced options
|
|
5. Power off
|
|
"""
|
|
self.test.wait_for('firmware_screen')
|
|
# Since the default selection is unknown, navigate to item 0 first
|
|
self.menu.move_to(5, 0)
|
|
# Navigate to "Boot from internal disk"
|
|
self.menu.move_to(0, 2)
|
|
self.menu.select('Selecting "Boot from internal disk"...')
|
|
|
|
def dev_boot_from_external(self):
|
|
"""Boot from external disk in developer mode.
|
|
|
|
Menu items in developer mode screen:
|
|
0. Language
|
|
1. Return to secure mode
|
|
2. Boot from internal disk
|
|
3. Boot from external disk
|
|
4. Advanced options
|
|
5. Power off
|
|
"""
|
|
self.test.wait_for('firmware_screen')
|
|
# Since the default selection is unknown, navigate to item 0 first
|
|
self.menu.move_to(5, 0)
|
|
# Navigate to "Boot from external disk"
|
|
self.menu.move_to(0, 3)
|
|
self.menu.select('Selecting "Boot from external disk"...')
|
|
|
|
def trigger_dev_to_normal(self):
|
|
"""
|
|
Trigger dev-to-norm transition.
|
|
|
|
Menu items in developer mode screen:
|
|
0. Language
|
|
1. Return to secure mode
|
|
2. Boot from internal disk
|
|
3. Boot from external disk
|
|
4. Advanced options
|
|
5. Power off
|
|
|
|
Menu items in to-norm screen:
|
|
0. Language
|
|
*1. Confirm
|
|
2. Cancel
|
|
3. Power off
|
|
|
|
(*) is the default selection.
|
|
"""
|
|
self.test.wait_for('firmware_screen')
|
|
# Since the default selection is unknown, navigate to item 0 first
|
|
self.menu.move_to(5, 0)
|
|
# Navigate to "Return to secure mode"
|
|
self.menu.down()
|
|
self.test.wait_for('keypress_delay')
|
|
self.menu.select('Selecting "Return to secure mode"...')
|
|
self.test.wait_for('keypress_delay')
|
|
self.menu.select('Selecing "Confirm"...')
|
|
|
|
def power_off(self):
|
|
"""
|
|
Power off the device.
|
|
|
|
This method should work in both developer and recovery screens.
|
|
"""
|
|
self.test.wait_for('firmware_screen')
|
|
# Since there are at most 6 menu items in dev/rec screen, move the
|
|
# cursor down 6 times to ensure we reach the last menu item.
|
|
self.menu.move_to(0, 6)
|
|
self.menu.select('Selecting "Power off"...')
|
|
|
|
def trigger_rec_to_minidiag(self):
|
|
"""
|
|
Trigger rec-to-MiniDiag.
|
|
|
|
@raise TestError if MiniDiag is not enabled.
|
|
"""
|
|
|
|
# Validity check; this only applicable for MiniDiag enabled devices.
|
|
if not self.minidiag_enabled:
|
|
raise error.TestError('MiniDiag is not enabled for this board')
|
|
|
|
self.test.wait_for('firmware_screen')
|
|
# The default selection is unknown, so navigate to the last item first
|
|
self.menu.move_to(0, self.RECOVERY_SELECT_ITEM_COUNT)
|
|
# Navigate to "Launch diagnostics"
|
|
self.menu.up()
|
|
self.test.wait_for('keypress_delay')
|
|
self.menu.up()
|
|
self.test.wait_for('keypress_delay')
|
|
self.menu.select('Selecting "Launch diagnostics"...')
|
|
self.test.wait_for('firmware_screen')
|
|
|
|
def navigate_minidiag_storage(self):
|
|
"""
|
|
Navigate to storage screen.
|
|
|
|
Menu items in storage screen:
|
|
0. Language
|
|
1. Page up (disabled)
|
|
2. Page down
|
|
3. Back
|
|
4. Power off
|
|
|
|
@raise TestError if MiniDiag is not enabled.
|
|
"""
|
|
|
|
# Validity check; this only applicable for MiniDiag enabled devices.
|
|
if not self.minidiag_enabled:
|
|
raise error.TestError('MiniDiag is not enabled for this board')
|
|
|
|
# From root screen to storage screen
|
|
self.menu.select('Selecting "Storage"...')
|
|
self.test.wait_for('keypress_delay')
|
|
# Since the default selection is unknown, navigate to item 4 first
|
|
self.menu.move_to(0, 4)
|
|
# Navigate to "Back"
|
|
self.menu.up()
|
|
self.test.wait_for('keypress_delay')
|
|
self.menu.select('Back to MiniDiag root screen...')
|
|
self.test.wait_for('keypress_delay')
|
|
|
|
def navigate_minidiag_quick_memory_check(self):
|
|
"""
|
|
Navigate to quick memory test screen.
|
|
|
|
Menu items in quick memory test screen:
|
|
0. Language
|
|
1. Page up (disabled)
|
|
2. Page down (disabled
|
|
3. Back
|
|
4. Power off
|
|
|
|
@raise TestError if MiniDiag is not enabled.
|
|
"""
|
|
|
|
# Validity check; this only applicable for MiniDiag enabled devices.
|
|
if not self.minidiag_enabled:
|
|
raise error.TestError('MiniDiag is not enabled for this board')
|
|
|
|
# From root screen to quick memory test screen
|
|
# There might be self test items, so navigate to the last item first
|
|
self.menu.move_to(0, 5)
|
|
self.menu.up() # full memory test
|
|
self.test.wait_for('keypress_delay')
|
|
self.menu.up() # quick memory test
|
|
self.test.wait_for('keypress_delay')
|
|
self.menu.select('Selecting "Quick memory test"...')
|
|
self.test.wait_for('keypress_delay')
|
|
# Wait for quick memory test
|
|
self.menu.select('Back to MiniDiag root screen...')
|
|
self.test.wait_for('keypress_delay')
|
|
|
|
def reset_and_leave_minidiag(self):
|
|
"""
|
|
Reset the DUT and normal boot to leave MiniDiag.
|
|
|
|
@raise TestError if MiniDiag is not enabled or no apreset support.
|
|
"""
|
|
|
|
# Validity check; this only applicable for MiniDiag enabled devices.
|
|
if not self.minidiag_enabled:
|
|
raise error.TestError('MiniDiag is not enabled for this board')
|
|
|
|
# Since we want to keep the cbmem log, we need an AP reset and reboot to
|
|
# normal mode
|
|
if self.test.ec.has_command('apreset'):
|
|
logging.info('Trigger apreset')
|
|
self.test.ec.send_command('apreset')
|
|
else:
|
|
raise error.TestError('EC command apreset is not supported')
|
|
|
|
def trigger_rec_to_minios(self, older_version=False):
|
|
"""
|
|
Trigger recovery-to-MiniOS transition.
|
|
|
|
Menu items in advanced options screen, developer mode:
|
|
0. Language
|
|
*1. Debug info
|
|
2. Firmware log
|
|
3. Internet recovery (older version)
|
|
4. Back
|
|
5. Power off
|
|
|
|
(*) is the default selection.
|
|
|
|
@param older_version: True for selecting the older version button in the
|
|
advanced options screen, and False for selecting
|
|
the newer one in the recovery selection screen.
|
|
@raise TestError if MiniOS is not enabled.
|
|
"""
|
|
# Validity check
|
|
if not self.minios_enabled:
|
|
raise NotImplementedError
|
|
|
|
# Boot to MiniOS through UI menu
|
|
if older_version:
|
|
logging.info('Boot to MiniOS (older version)')
|
|
# The default selection is unknown, so navigate to the last item
|
|
# first
|
|
self.menu.move_to(0, self.RECOVERY_SELECT_ITEM_COUNT)
|
|
# Navigate to "Advanced options"
|
|
self.menu.up()
|
|
self.test.wait_for('keypress_delay')
|
|
self.menu.select('Selecting "Advanced options"...')
|
|
self.test.wait_for('keypress_delay')
|
|
# Navigate to the last item in advanced options
|
|
self.menu.move_to(0, 5)
|
|
self.menu.move_to(5, 3)
|
|
self.menu.select(
|
|
'Selecting "Internet recovery (older version)"...')
|
|
else:
|
|
logging.info('Boot to MiniOS')
|
|
self.menu.down()
|
|
self.menu.select(
|
|
'Selecting "Recovery using internet connection"...')
|
|
|
|
self.test.wait_for('minios_screen')
|
|
|
|
|
|
_MENU_MODE_SWITCHER_CLASSES = {
|
|
'menu_switcher': _MenuModeSwitcher,
|
|
'tablet_detachable_switcher': _TabletDetachableMenuModeSwitcher,
|
|
}
|
|
|
|
|
|
def create_menu_mode_switcher(faft_framework, menu_navigator):
|
|
"""
|
|
Create a proper navigator based on its mode switcher type.
|
|
|
|
@param faft_framework: The main FAFT framework object.
|
|
@param menu_navigator: The menu navigator for base logic of navigation.
|
|
"""
|
|
switcher_type = faft_framework.faft_config.mode_switcher_type
|
|
switcher_class = _MENU_MODE_SWITCHER_CLASSES.get(switcher_type, None)
|
|
if switcher_class is None:
|
|
# Not all devices support menu-based UI, so it is fine to return None.
|
|
logging.info('Switcher type %s is menuless, return None',
|
|
switcher_type)
|
|
return None
|
|
return switcher_class(faft_framework, menu_navigator)
|