blob: 1057999375057e71e33884ca843bcbe125c3f985 [file] [log] [blame]
# 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)