/* Copyright 2020 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 developer and recovery mode UIs.
 */

#include "2api.h"
#include "2common.h"
#include "2misc.h"
#include "2nvstorage.h"
#include "2struct.h"
#include "2ui.h"
#include "2ui_private.h"
#include "test_common.h"
#include "vboot_kernel.h"

/* Fixed value for ignoring some checks */
#define MOCK_IGNORE 0xffffu

/* Fuzzy matches for check_time() */
#define FUZZ_MS 200

/* Mock data */
/* TODO(b/156448738): Add tests for timer_disabled and error_code */
struct display_call {
	const struct vb2_screen_info *screen;
	uint32_t locale_id;
	uint32_t selected_item;
	uint32_t disabled_item_mask;
	uint32_t hidden_item_mask;
	int timer_disabled;
	uint32_t current_page;
	enum vb2_ui_error error_code;
} __attribute__((packed));

struct beep_call {
	uint32_t msec;
	uint32_t frequency;
	uint32_t time_expected;
};

static uint8_t workbuf[VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE]
	__attribute__((aligned(VB2_WORKBUF_ALIGN)));
static struct vb2_context *ctx;
static struct vb2_shared_data *sd;
static struct vb2_gbb_header gbb;

static struct vb2_ui_context mock_ui_context;
static struct vb2_screen_state mock_state;

static struct display_call mock_displayed[64];
static int mock_displayed_count;
static int mock_displayed_i;

static uint32_t mock_locale_count;

static int mock_calls_until_shutdown;

/* Iteration counter starts from 0
   Mock inputs should response according to this */
static int mock_iters;

static uint32_t mock_key[64];
static int mock_key_trusted[64];
static int mock_key_total;

static uint32_t mock_time_ms;
static const uint32_t mock_time_start_ms = 31ULL * VB2_MSEC_PER_SEC;

static struct beep_call mock_beep[8];
static int mock_beep_count;
static int mock_beep_total;

static enum vb2_dev_default_boot_target mock_default_boot;
static int mock_dev_boot_allowed;
static int mock_dev_boot_legacy_allowed;
static int mock_dev_boot_external_allowed;

static int mock_vbexlegacy_called;
static enum VbAltFwIndex_t mock_altfw_num_last;
static uint32_t mock_bootloader_count;

static vb2_error_t mock_vbtlk_retval[32];
static uint32_t mock_vbtlk_expected_flag[32];
static int mock_vbtlk_total;

static int mock_allow_recovery;

/* mock_pp_* = mock data for physical presence button */
static int mock_pp_pressed[64];
static int mock_pp_pressed_total;

static int mock_enable_dev_mode;

#define MOCK_PREPARE_LOG_SIZE 32

static int mock_snapshot_count;
static char mock_prepare_log[64][MOCK_PREPARE_LOG_SIZE];
static int mock_prepare_log_count;
static uint32_t mock_log_page_count;

static void add_mock_key(uint32_t press, int trusted)
{
	if (mock_key_total >= ARRAY_SIZE(mock_key) ||
	    mock_key_total >= ARRAY_SIZE(mock_key_trusted)) {
		TEST_TRUE(0, "  mock_key ran out of entries!");
		return;
	}

	mock_key[mock_key_total] = press;
	mock_key_trusted[mock_key_total] = trusted;
	mock_key_total++;
}

static void add_mock_keypress(uint32_t press)
{
	add_mock_key(press, 0);
}

static void add_mock_vbtlk(vb2_error_t retval, uint32_t get_info_flags)
{
	if (mock_vbtlk_total >= ARRAY_SIZE(mock_vbtlk_retval) ||
	    mock_vbtlk_total >= ARRAY_SIZE(mock_vbtlk_expected_flag)) {
		TEST_TRUE(0, "  mock_vbtlk ran out of entries!");
		return;
	}

	mock_vbtlk_retval[mock_vbtlk_total] = retval;
	mock_vbtlk_expected_flag[mock_vbtlk_total] = get_info_flags;
	mock_vbtlk_total++;
}

static void add_mock_pp_pressed(int pressed)
{
	if (mock_pp_pressed_total >= ARRAY_SIZE(mock_pp_pressed)) {
		TEST_TRUE(0, "  mock_pp ran out of entries!");
		return;
	}

	mock_pp_pressed[mock_pp_pressed_total++] = pressed;
}

static void extend_calls_until_shutdown(void)
{
	if (mock_calls_until_shutdown < mock_key_total)
		mock_calls_until_shutdown = mock_key_total;
	if (mock_calls_until_shutdown < mock_vbtlk_total)
		mock_calls_until_shutdown = mock_vbtlk_total;
	if (mock_calls_until_shutdown < mock_pp_pressed_total)
		mock_calls_until_shutdown = mock_pp_pressed_total;
	mock_calls_until_shutdown++;
}

static void displayed_eq(const char *text,
			 enum vb2_screen screen,
			 uint32_t locale_id,
			 uint32_t selected_item,
			 uint32_t disabled_item_mask,
			 uint32_t hidden_item_mask,
			 uint32_t current_page,
			 int line)
{
	char text_info[32], text_buf[128];

	sprintf(text_info, "(line #%d, displayed #%d)", line, mock_displayed_i);

	if (mock_displayed_i >= mock_displayed_count) {
		sprintf(text_buf, "  %s missing screen %s",
			text_info, text);
		TEST_TRUE(0, text_buf);
		return;
	}

	if (screen != MOCK_IGNORE) {
		sprintf(text_buf, "  %s screen of %s", text_info, text);
		TEST_EQ(mock_displayed[mock_displayed_i].screen->id, screen,
			text_buf);
	}
	if (locale_id != MOCK_IGNORE) {
		sprintf(text_buf, "  %s locale_id of %s", text_info, text);
		TEST_EQ(mock_displayed[mock_displayed_i].locale_id, locale_id,
			text_buf);
	}
	if (selected_item != MOCK_IGNORE) {
		sprintf(text_buf, "  %s selected_item of %s",
			text_info, text);
		TEST_EQ(mock_displayed[mock_displayed_i].selected_item,
			selected_item, text_buf);
	}
	if (disabled_item_mask != MOCK_IGNORE) {
		sprintf(text_buf, "  %s disabled_item_mask of %s",
			text_info, text);
		TEST_EQ(mock_displayed[mock_displayed_i].disabled_item_mask,
			disabled_item_mask, text_buf);
	}
	if (hidden_item_mask != MOCK_IGNORE) {
		sprintf(text_buf, "  %s hidden_item_mask of %s",
			text_info, text);
		TEST_EQ(mock_displayed[mock_displayed_i].hidden_item_mask,
			hidden_item_mask, text_buf);
	}
	if (current_page != MOCK_IGNORE) {
		sprintf(text_buf, "  %s current_page of %s",
			text_info, text);
		TEST_EQ(mock_displayed[mock_displayed_i].current_page,
			current_page, text_buf);
	}
	mock_displayed_i++;
}

static void displayed_no_extra(int line)
{
	char text_info[32], text_buf[128];

	sprintf(text_info, "(line #%d)", line);

	if (mock_displayed_i == 0)
		sprintf(text_buf, "  %s no screen", text_info);
	else
		sprintf(text_buf, "  %s no extra screens", text_info);
	TEST_EQ(mock_displayed_count, mock_displayed_i, text_buf);
}

#define DISPLAYED_EQ(...) displayed_eq(__VA_ARGS__, __LINE__)

#define DISPLAYED_PASS() \
	displayed_eq("", MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, \
		     MOCK_IGNORE, MOCK_IGNORE, __LINE__)

#define DISPLAYED_NO_EXTRA() displayed_no_extra(__LINE__)

static void expect_beep(uint32_t msec,
			uint32_t frequency,
			uint32_t time_expected)
{
	if (mock_beep_total >= ARRAY_SIZE(mock_beep)) {
		TEST_TRUE(0, "  mock_beep ran out of entries!");
		return;
	}

	mock_beep[mock_beep_total++] = (struct beep_call){
		.msec = msec,
		.frequency = frequency,
		.time_expected = time_expected,
	};
}

/* Check if the result time falls in range [expected, expected + FUZZ_MS) */
static void check_time(uint32_t result, uint32_t expected, const char *desc)
{
	TEST_TRUE(result >= expected, desc);
	TEST_TRUE(result - expected < FUZZ_MS, "  within FUZZ_MS");
}

/* Type of test to reset for */
enum reset_type {
	FOR_DEVELOPER,
	FOR_BROKEN_RECOVERY,
	FOR_MANUAL_RECOVERY,
	FOR_DIAGNOSTICS,
};

/* Reset mock data (for use before each test) */
static void reset_common_data(enum reset_type t)
{
	TEST_SUCC(vb2api_init(workbuf, sizeof(workbuf), &ctx),
		  "vb2api_init failed");

	memset(&gbb, 0, sizeof(gbb));

	vb2_nv_init(ctx);

	sd = vb2_get_sd(ctx);
	sd->status |= VB2_SD_STATUS_SECDATA_KERNEL_INIT;

	if (t == FOR_DEVELOPER) {
		ctx->flags |= VB2_CONTEXT_DEVELOPER_MODE;
		sd->flags |= VB2_SD_FLAG_DEV_MODE_ENABLED;
	}

	/* Mock ui_context based on real screens */
	memset(&mock_ui_context, 0, sizeof(mock_ui_context));
	mock_ui_context.ctx = ctx;
	mock_ui_context.state = &mock_state;

	/* For vb2ex_display_ui */
	memset(mock_displayed, 0, sizeof(mock_displayed));
	mock_displayed_count = 0;
	mock_displayed_i = 0;

	/* For vb2ex_get_locale_count */
	mock_locale_count = 1;

	/* For check_shutdown_request */
	if (t == FOR_DEVELOPER)
		mock_calls_until_shutdown = 2000;  /* Larger than 30s */
	else
		mock_calls_until_shutdown = 10;

	/* For iteration counter */
	mock_iters = -1;  /* Accumulates at the beginning of iterations */

	/* For VbExKeyboardRead */
	memset(mock_key, 0, sizeof(mock_key));
	memset(mock_key_trusted, 0, sizeof(mock_key_trusted));
	mock_key_total = 0;

	/* For vb2ex_mtime and vb2ex_msleep  */
	mock_time_ms = mock_time_start_ms;

	/* For vb2ex_beep */
	memset(mock_beep, 0, sizeof(mock_beep));
	mock_beep_count = 0;
	mock_beep_total = 0;

	/* For dev_boot* in 2misc.h */
	mock_default_boot = VB2_DEV_DEFAULT_BOOT_TARGET_INTERNAL;
	mock_dev_boot_allowed = 1;
	mock_dev_boot_legacy_allowed = 0;
	mock_dev_boot_external_allowed = 1;

	/* For VbExLegacy */
	mock_vbexlegacy_called = 0;
	mock_altfw_num_last = -100;
	mock_bootloader_count = 2;

	/* For VbTryLoadKernel */
	memset(mock_vbtlk_retval, 0, sizeof(mock_vbtlk_retval));
	memset(mock_vbtlk_expected_flag, 0, sizeof(mock_vbtlk_expected_flag));
	mock_vbtlk_total = 0;

	/* For vb2_allow_recovery */
	mock_allow_recovery = t == FOR_MANUAL_RECOVERY;

	/* For vb2ex_physical_presence_pressed */
	memset(mock_pp_pressed, 0, sizeof(mock_pp_pressed));
	mock_pp_pressed_total = 0;

	/* For vb2_enable_developer_mode */
	mock_enable_dev_mode = 0;

	/* For vb2ex_prepare_log_screen */
	mock_snapshot_count = 0;
	mock_prepare_log_count = 0;
	mock_log_page_count = 1;

	/* Avoid Iteration #0 */
	add_mock_keypress(0);
	if (t == FOR_MANUAL_RECOVERY)
		add_mock_vbtlk(VB2_ERROR_LK_NO_DISK_FOUND,
			       VB_DISK_FLAG_REMOVABLE);
	else
		add_mock_vbtlk(VB2_ERROR_MOCK, 0);
	add_mock_pp_pressed(0);
}

/* Mock functions */
struct vb2_gbb_header *vb2_get_gbb(struct vb2_context *c)
{
	return &gbb;
}

vb2_error_t vb2ex_display_ui(enum vb2_screen screen,
			     uint32_t locale_id,
			     uint32_t selected_item,
			     uint32_t disabled_item_mask,
			     uint32_t hidden_item_mask,
			     int timer_disabled,
			     uint32_t current_page,
			     enum vb2_ui_error error_code)
{
	struct display_call displayed = (struct display_call){
		.screen = vb2_get_screen_info(screen),
		.locale_id = locale_id,
		.selected_item = selected_item,
		.disabled_item_mask = disabled_item_mask,
		.hidden_item_mask = hidden_item_mask,
		.timer_disabled = timer_disabled,
		.current_page = current_page,
		.error_code = error_code,
	};

	/* Ignore repeated calls with same arguments */
	if (mock_displayed_count > 0 &&
	    !memcmp(&mock_displayed[mock_displayed_count - 1], &displayed,
		    sizeof(struct display_call)))
		return VB2_SUCCESS;

	VB2_DEBUG("displayed %d: screen=%#x, locale_id=%u, selected_item=%u, "
		  "disabled_item_mask=%#x, hidden_item_mask=%#x, "
		  "timer_disabled=%d, current_page=%u, error=%#x\n",
		  mock_displayed_count, screen, locale_id, selected_item,
		  disabled_item_mask, hidden_item_mask,
		  timer_disabled, current_page, error_code);

	if (mock_displayed_count >= ARRAY_SIZE(mock_displayed)) {
		TEST_TRUE(0, "  mock vb2ex_display_ui ran out of entries!");
		return VB2_ERROR_MOCK;
	}

	mock_displayed[mock_displayed_count++] = displayed;

	return VB2_SUCCESS;
}

uint32_t vb2ex_get_locale_count(void)
{
	return mock_locale_count;
}

uint32_t VbExIsShutdownRequested(void)
{
	if (mock_calls_until_shutdown < 0)  /* Never request shutdown */
		return 0;
	if (mock_calls_until_shutdown == 0)
		return 1;
	mock_calls_until_shutdown--;

	return 0;
}

uint32_t VbExKeyboardRead(void)
{
	return VbExKeyboardReadWithFlags(NULL);
}

uint32_t VbExKeyboardReadWithFlags(uint32_t *key_flags)
{
	mock_iters++;
	if (mock_iters < mock_key_total) {
		if (key_flags != NULL) {
			if (mock_key_trusted[mock_iters])
				*key_flags = VB_KEY_FLAG_TRUSTED_KEYBOARD;
			else
				*key_flags = 0;
		}
		return mock_key[mock_iters];
	}

	return 0;
}

uint32_t vb2ex_mtime(void)
{
	return mock_time_ms;
}

void vb2ex_msleep(uint32_t msec)
{
	mock_time_ms += msec;
}

void vb2ex_beep(uint32_t msec, uint32_t frequency)
{
	struct beep_call *beep;
	uint32_t cur_time = mock_time_ms - mock_time_start_ms;

	VB2_DEBUG("beep %d: msec = %d, frequency = %d at %d msec\n",
		  mock_beep_count, msec, frequency, cur_time);

	if (mock_beep_total > 0) {
		TEST_TRUE(mock_beep_count < mock_beep_total,
			  "  too many beep calls!");

		beep = &mock_beep[mock_beep_count];

		VB2_DEBUG("beep expected: msec = %d, frequency = %d, "
			  "at %d msec\n",
			  beep->msec, beep->frequency, beep->time_expected);

		TEST_EQ(msec, beep->msec, "  beep duration");
		TEST_EQ(frequency, beep->frequency, "  beep frequency");
		check_time(cur_time, beep->time_expected,
			   "  beep started after expected time");
	}

	mock_time_ms += msec;
	mock_beep_count++;
}

enum vb2_dev_default_boot_target vb2api_get_dev_default_boot_target(
	struct vb2_context *c)
{
	return mock_default_boot;
}

int vb2_dev_boot_allowed(struct vb2_context *c)
{
	return mock_dev_boot_allowed;
}

int vb2_dev_boot_legacy_allowed(struct vb2_context *c)
{
	return mock_dev_boot_legacy_allowed;
}

int vb2_dev_boot_external_allowed(struct vb2_context *c)
{
	return mock_dev_boot_external_allowed;
}

vb2_error_t VbExLegacy(enum VbAltFwIndex_t altfw_num)
{
	mock_vbexlegacy_called++;
	mock_altfw_num_last = altfw_num;

	return VB2_SUCCESS;
}

uint32_t vb2ex_get_bootloader_count(void)
{
	return mock_bootloader_count;
}

vb2_error_t VbTryLoadKernel(struct vb2_context *c, uint32_t get_info_flags)
{
	int i = mock_iters;

	/* Return last entry if called too many times */
	if (i >= mock_vbtlk_total)
		i = mock_vbtlk_total - 1;

	TEST_EQ(mock_vbtlk_expected_flag[i], get_info_flags,
		"  unexpected get_info_flags");

	return mock_vbtlk_retval[i];
}

int vb2_allow_recovery(struct vb2_context *c)
{
	return mock_allow_recovery;
}

int vb2ex_physical_presence_pressed(void)
{
	if (mock_iters >= mock_pp_pressed_total)
		return 0;

	return mock_pp_pressed[mock_iters];
}

void vb2_enable_developer_mode(struct vb2_context *c)
{
	mock_enable_dev_mode = 1;
}

const char *vb2ex_get_debug_info(struct vb2_context *c)
{
	return "mocked debug info";
}

const char *vb2ex_get_firmware_log(int reset)
{
	static char mock_firmware_log_buf[MOCK_PREPARE_LOG_SIZE];
	if (reset)
		mock_snapshot_count++;
	snprintf(mock_firmware_log_buf, MOCK_PREPARE_LOG_SIZE,
		 "%d", mock_snapshot_count);
	return mock_firmware_log_buf;
}

uint32_t vb2ex_prepare_log_screen(const char *str)
{
	if (mock_prepare_log_count < ARRAY_SIZE(mock_prepare_log))
		strncpy(mock_prepare_log[mock_prepare_log_count],
			str, MOCK_PREPARE_LOG_SIZE);
	mock_prepare_log_count++;

	return mock_log_page_count;
}

/* Tests */
static void developer_tests(void)
{
	VB2_DEBUG("Testing developer mode...\n");

	/* Power button short pressed = shutdown request */
	if (!DETACHABLE) {
		reset_common_data(FOR_DEVELOPER);
		add_mock_keypress(VB_BUTTON_POWER_SHORT_PRESS);
		mock_calls_until_shutdown = -1;
		TEST_EQ(vb2_developer_menu(ctx),
			VB2_REQUEST_SHUTDOWN,
			"power button short pressed = shutdown");
	}

	/* Proceed to internal disk after timeout */
	reset_common_data(FOR_DEVELOPER);
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
	expect_beep(250, 400, DEV_DELAY_BEEP1_MS);
	expect_beep(250, 400, DEV_DELAY_BEEP2_MS);
	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
		"proceed to internal disk after timeout");
	check_time(mock_time_ms - mock_time_start_ms, DEV_DELAY_NORMAL_MS,
		   "  finished delay");
	TEST_EQ(mock_beep_count, 2, "  beeped twice");
	TEST_TRUE(mock_iters >= mock_vbtlk_total, "  used up mock_vbtlk");

	/* Use short delay */
	reset_common_data(FOR_DEVELOPER);
	gbb.flags |= VB2_GBB_FLAG_DEV_SCREEN_SHORT_DELAY;
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
		"use short delay");
	check_time(mock_time_ms - mock_time_start_ms, DEV_DELAY_SHORT_MS,
		   "  finished delay");
	TEST_EQ(mock_beep_count, 0, "  never beeped");

	/* Stop timer on any user input: normal delay */
	reset_common_data(FOR_DEVELOPER);
	add_mock_keypress('A');
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"stop timer on any user input: normal delay");
	TEST_TRUE(mock_time_ms - mock_time_start_ms > DEV_DELAY_NORMAL_MS,
		  "  delay aborted");
	TEST_EQ(mock_calls_until_shutdown, 0, "  loop forever");
	TEST_EQ(mock_beep_count, 0, "  never beeped");

	/* Stop timer on any user input: short delay */
	reset_common_data(FOR_DEVELOPER);
	gbb.flags |= VB2_GBB_FLAG_DEV_SCREEN_SHORT_DELAY;
	add_mock_keypress('A');
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"stop timer on any user input: short delay");
	TEST_TRUE(mock_time_ms - mock_time_start_ms > DEV_DELAY_SHORT_MS,
		  "  delay aborted");
	TEST_EQ(mock_calls_until_shutdown, 0, "  loop forever");
	TEST_EQ(mock_beep_count, 0, "  never beeped");

	/* If fail to load internal disk, don't boot */
	reset_common_data(FOR_DEVELOPER);
	add_mock_vbtlk(VB2_ERROR_LK_NO_DISK_FOUND, VB_DISK_FLAG_FIXED);
	TEST_EQ(vb2_developer_menu(ctx), VB2_ERROR_LK_NO_DISK_FOUND,
		"if fail to load internal disk, don't boot");

	/* Select boot internal in dev menu */
	reset_common_data(FOR_DEVELOPER);
	add_mock_keypress(VB_KEY_ENTER);
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
		"select boot internal in dev menu");

	/* Ctrl+U = boot external */
	reset_common_data(FOR_DEVELOPER);
	add_mock_keypress(VB_KEY_CTRL('U'));
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_REMOVABLE);
	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
		"ctrl+u = boot external");

	/* Ctrl+D = boot internal */
	reset_common_data(FOR_DEVELOPER);
	add_mock_keypress(VB_KEY_CTRL('D'));
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
		"ctrl+d = boot internal");

	/* VB_BUTTON_VOL_DOWN_LONG_PRESS = boot internal */
	if (DETACHABLE) {
		reset_common_data(FOR_DEVELOPER);
		add_mock_keypress(VB_BUTTON_VOL_DOWN_LONG_PRESS);
		add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
		TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
			"VB_BUTTON_VOL_DOWN_LONG_PRESS = boot internal");
	}

	/* Proceed to external disk after timeout */
	reset_common_data(FOR_DEVELOPER);
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_REMOVABLE);
	mock_default_boot = VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL;
	expect_beep(250, 400, DEV_DELAY_BEEP1_MS);
	expect_beep(250, 400, DEV_DELAY_BEEP2_MS);
	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
		"proceed to external disk after timeout");
	check_time(mock_time_ms - mock_time_start_ms, DEV_DELAY_NORMAL_MS,
		   "  finished delay");
	TEST_EQ(mock_beep_count, 2, "  beeped twice");
	TEST_TRUE(mock_iters >= mock_vbtlk_total, "  used up mock_vbtlk");

	/* Default boot from external not allowed, don't boot */
	reset_common_data(FOR_DEVELOPER);
	mock_default_boot = VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL;
	mock_dev_boot_external_allowed = 0;
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"default boot from external disk not allowed, don't boot");

	/* If no external disk, don't boot */
	reset_common_data(FOR_DEVELOPER);
	add_mock_vbtlk(VB2_ERROR_LK_NO_DISK_FOUND, VB_DISK_FLAG_REMOVABLE);
	mock_default_boot = VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL;
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"if no external disk, don't boot");

	/* Select boot external in dev menu */
	reset_common_data(FOR_DEVELOPER);
	add_mock_keypress(VB_KEY_ENTER);
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_REMOVABLE);
	mock_default_boot = VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL;
	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
		"select boot external in dev menu");

	/* Ctrl+L = boot legacy (allowed) */
	reset_common_data(FOR_DEVELOPER);
	mock_dev_boot_legacy_allowed = 1;
	add_mock_keypress(VB_KEY_CTRL('L'));
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"ctrl+l = boot legacy");
	TEST_EQ(mock_vbexlegacy_called, 1, "  VbExLegacy called");

	/* Ctrl+L = boot legacy (disallowed) */
	reset_common_data(FOR_DEVELOPER);
	add_mock_keypress(VB_KEY_CTRL('L'));
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"ctrl+l = boot legacy");
	TEST_EQ(mock_vbexlegacy_called, 0, "  VbExLegacy not called");

	/* VB_BUTTON_VOL_UP_LONG_PRESS = boot external */
	if (DETACHABLE) {
		reset_common_data(FOR_DEVELOPER);
		add_mock_keypress(VB_BUTTON_VOL_UP_LONG_PRESS);
		add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_REMOVABLE);
		TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
			"VB_BUTTON_VOL_UP_LONG_PRESS = boot external");
	}

	/* If dev mode is disabled, goes to to_norm screen repeatedly */
	reset_common_data(FOR_DEVELOPER);
	add_mock_keypress(VB_KEY_ESC);
	mock_dev_boot_allowed = 0;
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"if dev mode is disabled, goes to to_norm screen repeatedly");
	DISPLAYED_EQ("to_norm", VB2_SCREEN_DEVELOPER_TO_NORM, MOCK_IGNORE,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_PASS();
	DISPLAYED_EQ("to_norm", VB2_SCREEN_DEVELOPER_TO_NORM, MOCK_IGNORE,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	/* Select to_norm in dev menu and confirm */
	reset_common_data(FOR_DEVELOPER);
	add_mock_keypress(VB_KEY_UP);
	add_mock_keypress(VB_KEY_ENTER);
	add_mock_keypress(VB_KEY_ENTER);
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_REBOOT,
		"select to_norm in dev menu and confirm");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_DISABLE_DEV_REQUEST), 1,
		"  disable dev request");

	/* Select to_norm in dev menu and cancel */
	reset_common_data(FOR_DEVELOPER);
	add_mock_keypress(VB_KEY_UP);
	add_mock_keypress(VB_KEY_ENTER);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"select to_norm in dev menu and cancel");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_DISABLE_DEV_REQUEST), 0,
		"  disable dev request");

	/* Ctrl+S = to_norm */
	reset_common_data(FOR_DEVELOPER);
	add_mock_keypress(VB_KEY_CTRL('S'));
	add_mock_keypress(VB_KEY_ENTER);
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_REBOOT,
		"ctrl+s = to_norm");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_DISABLE_DEV_REQUEST), 1,
		"  disable dev request");

	/* Dev mode forced by GBB flag */
	reset_common_data(FOR_DEVELOPER);
	gbb.flags |= VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON;
	add_mock_keypress(VB_KEY_CTRL('S'));
	add_mock_keypress(VB_KEY_ENTER);
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"dev mode forced by GBB flag");
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_DISABLE_DEV_REQUEST), 0,
		"  disable dev request");

	/* Power off */
	reset_common_data(FOR_DEVELOPER);
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
	/* Navigate to the bottom most menu item */
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"select power off");

	VB2_DEBUG("...done.\n");
}

static void broken_recovery_tests(void)
{
	VB2_DEBUG("Testing broken recovery mode...\n");

	/* Power button short pressed = shutdown request */
	if (!DETACHABLE) {
		reset_common_data(FOR_BROKEN_RECOVERY);
		add_mock_keypress(VB_BUTTON_POWER_SHORT_PRESS);
		mock_calls_until_shutdown = -1;
		TEST_EQ(vb2_broken_recovery_menu(ctx),
			VB2_REQUEST_SHUTDOWN,
			"power button short pressed = shutdown");
	}

	/* Shortcuts that are always ignored in BROKEN */
	reset_common_data(FOR_BROKEN_RECOVERY);
	add_mock_key(VB_KEY_CTRL('D'), 1);
	add_mock_key(VB_KEY_CTRL('U'), 1);
	add_mock_key(VB_KEY_CTRL('L'), 1);
	add_mock_key(VB_BUTTON_VOL_UP_DOWN_COMBO_PRESS, 1);
	add_mock_key(VB_BUTTON_VOL_UP_LONG_PRESS, 1);
	add_mock_key(VB_BUTTON_VOL_DOWN_LONG_PRESS, 1);
	TEST_EQ(vb2_broken_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"Shortcuts ignored in BROKEN");
	TEST_EQ(mock_calls_until_shutdown, 0, "  loop forever");
	TEST_EQ(mock_displayed_count, 1, "  root screen only");

	VB2_DEBUG("...done.\n");
}

static void manual_recovery_tests(void)
{
	VB2_DEBUG("Testing manual recovery mode...\n");

	/* Timeout, shutdown */
	reset_common_data(FOR_MANUAL_RECOVERY);
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"timeout, shutdown");
	TEST_EQ(mock_displayed_count, 1, "  root screen only");

	/* Power button short pressed = shutdown request */
	if (!DETACHABLE) {
		reset_common_data(FOR_MANUAL_RECOVERY);
		add_mock_keypress(VB_BUTTON_POWER_SHORT_PRESS);
		TEST_EQ(vb2_manual_recovery_menu(ctx),
			VB2_REQUEST_SHUTDOWN,
			"power button short pressed = shutdown");
	}

	/* Boots if we have a valid image on first try */
	reset_common_data(FOR_MANUAL_RECOVERY);
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_REMOVABLE);
	add_mock_vbtlk(VB2_ERROR_MOCK, VB_DISK_FLAG_REMOVABLE);
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_SUCCESS,
		"boots if valid on first try");

	/* Boots eventually if we get a valid image later */
	reset_common_data(FOR_MANUAL_RECOVERY);
	add_mock_vbtlk(VB2_ERROR_LK_NO_DISK_FOUND, VB_DISK_FLAG_REMOVABLE);
	add_mock_vbtlk(VB2_ERROR_LK_NO_DISK_FOUND, VB_DISK_FLAG_REMOVABLE);
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_REMOVABLE);
	add_mock_vbtlk(VB2_ERROR_MOCK, VB_DISK_FLAG_REMOVABLE);
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_SUCCESS,
		"boots after valid image appears");

	/* Invalid image, then remove, then valid image */
	reset_common_data(FOR_MANUAL_RECOVERY);
	add_mock_vbtlk(VB2_ERROR_MOCK, VB_DISK_FLAG_REMOVABLE);
	add_mock_vbtlk(VB2_ERROR_LK_NO_DISK_FOUND, VB_DISK_FLAG_REMOVABLE);
	add_mock_vbtlk(VB2_ERROR_LK_NO_DISK_FOUND, VB_DISK_FLAG_REMOVABLE);
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_REMOVABLE);
	add_mock_vbtlk(VB2_ERROR_MOCK, VB_DISK_FLAG_REMOVABLE);
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_SUCCESS,
		"boots after valid image appears");
	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	DISPLAYED_EQ("recovery invalid", VB2_SCREEN_RECOVERY_INVALID,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	/* Ctrl+D = to_dev; space = cancel */
	reset_common_data(FOR_MANUAL_RECOVERY);
	add_mock_key(VB_KEY_CTRL('D'), 1);
	add_mock_keypress(' ');
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"ctrl+d = to_dev; space = cancel");
	TEST_EQ(mock_enable_dev_mode, 0, "  dev mode not enabled");
	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	DISPLAYED_EQ("to_dev", VB2_SCREEN_RECOVERY_TO_DEV,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	/* Cancel to_dev transition */
	reset_common_data(FOR_MANUAL_RECOVERY);
	add_mock_key(VB_KEY_CTRL('D'), 1);
	if (PHYSICAL_PRESENCE_KEYBOARD)
		add_mock_key(VB_KEY_DOWN, 1);
	add_mock_key(VB_KEY_ENTER, 1);
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"cancel to_dev transition");
	TEST_EQ(mock_enable_dev_mode, 0, "  dev mode not enabled");

	/* Confirm to_dev transition */
	reset_common_data(FOR_MANUAL_RECOVERY);
	add_mock_key(VB_KEY_CTRL('D'), 1);
	if (PHYSICAL_PRESENCE_KEYBOARD) {
		add_mock_key(VB_KEY_ENTER, 1);
	} else {
		add_mock_pp_pressed(0);
		add_mock_pp_pressed(1);
		add_mock_pp_pressed(1);
		add_mock_pp_pressed(0);
	}
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_REBOOT_EC_TO_RO,
		"confirm to_dev transition");
	if (!PHYSICAL_PRESENCE_KEYBOARD)
		TEST_TRUE(mock_iters >= mock_pp_pressed_total - 1,
			  "  used up mock_pp_pressed");
	TEST_EQ(mock_enable_dev_mode, 1, "  dev mode enabled");

	/* Cannot confirm physical presence by untrusted keyboard */
	if (PHYSICAL_PRESENCE_KEYBOARD) {
		reset_common_data(FOR_MANUAL_RECOVERY);
		add_mock_key(VB_KEY_CTRL('D'), 1);
		add_mock_key(VB_KEY_ENTER, 0);
		TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
			"cannot confirm physical presence"
			" by untrusted keyboard");
		TEST_EQ(mock_enable_dev_mode, 0, "  dev mode not enabled");
	}

	/* Cannot enable dev mode if already enabled */
	reset_common_data(FOR_MANUAL_RECOVERY);
	sd->flags |= VB2_SD_FLAG_DEV_MODE_ENABLED;
	add_mock_key(VB_KEY_CTRL('D'), 1);
	if (PHYSICAL_PRESENCE_KEYBOARD) {
		add_mock_key(VB_KEY_ENTER, 1);
	} else {
		add_mock_pp_pressed(0);
		add_mock_pp_pressed(1);
		add_mock_pp_pressed(0);
	}
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"cannot enable dev mode if already enabled");
	TEST_EQ(mock_enable_dev_mode, 0, "  dev mode already on");

	/* Physical presence button tests */
	if (!PHYSICAL_PRESENCE_KEYBOARD) {
		/* Physical presence button stuck? */
		reset_common_data(FOR_MANUAL_RECOVERY);
		add_mock_key(VB_KEY_CTRL('D'), 1);
		add_mock_pp_pressed(1);  /* Hold since boot */
		add_mock_pp_pressed(0);
		TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
			"physical presence button stuck?");
		TEST_EQ(mock_enable_dev_mode, 0, "  dev mode not enabled");
		DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
			     MOCK_IGNORE, MOCK_IGNORE);
		DISPLAYED_NO_EXTRA();

		/* Button stuck, enter to_dev again */
		reset_common_data(FOR_MANUAL_RECOVERY);
		add_mock_key(VB_KEY_CTRL('D'), 1);
		add_mock_key(VB_KEY_CTRL('D'), 1);
		add_mock_pp_pressed(1);  /* Hold since boot */
		add_mock_pp_pressed(0);
		add_mock_pp_pressed(1);  /* Press again */
		add_mock_pp_pressed(0);
		TEST_EQ(vb2_manual_recovery_menu(ctx),
			VB2_REQUEST_REBOOT_EC_TO_RO,
			"button stuck, enter to_dev again");
		TEST_TRUE(mock_iters >= mock_pp_pressed_total - 1,
			  "  used up mock_pp_pressed");
		TEST_EQ(mock_enable_dev_mode, 1, "  dev mode enabled");
		DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
			     MOCK_IGNORE, MOCK_IGNORE);
		DISPLAYED_EQ("to_dev", VB2_SCREEN_RECOVERY_TO_DEV,
			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
			     MOCK_IGNORE, MOCK_IGNORE);
		DISPLAYED_NO_EXTRA();

		/* Cancel with holding pp button, enter again */
		reset_common_data(FOR_MANUAL_RECOVERY);
		/* Enter to_dev */
		add_mock_key(VB_KEY_CTRL('D'), 1);
		add_mock_pp_pressed(0);
		/* Press pp button */
		add_mock_keypress(0);
		add_mock_pp_pressed(1);
		/* Space = back */
		add_mock_keypress(' ');
		add_mock_pp_pressed(1);
		/* Wait */
		add_mock_keypress(0);
		add_mock_pp_pressed(0);
		/* Enter to_dev again */
		add_mock_key(VB_KEY_CTRL('D'), 1);
		add_mock_pp_pressed(0);
		/* Press pp button again */
		add_mock_pp_pressed(1);
		/* Release */
		add_mock_pp_pressed(0);
		TEST_EQ(vb2_manual_recovery_menu(ctx),
			VB2_REQUEST_REBOOT_EC_TO_RO,
			"cancel with holding pp button, enter again");
		TEST_TRUE(mock_iters >= mock_pp_pressed_total - 1,
			  "  used up mock_pp_pressed");
		TEST_EQ(mock_enable_dev_mode, 1, "  dev mode enabled");
		DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
			     MOCK_IGNORE, MOCK_IGNORE);
		DISPLAYED_EQ("to_dev", VB2_SCREEN_RECOVERY_TO_DEV,
			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
			     MOCK_IGNORE, MOCK_IGNORE);
		DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
			     MOCK_IGNORE, MOCK_IGNORE);
		DISPLAYED_EQ("to_dev", VB2_SCREEN_RECOVERY_TO_DEV,
			     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
			     MOCK_IGNORE, MOCK_IGNORE);
		DISPLAYED_NO_EXTRA();
	}

	/* Enter diagnostics */
	if (DIAGNOSTIC_UI) {
		/* Launch diagnostics is inside manual recovery */
		reset_common_data(FOR_MANUAL_RECOVERY);
		add_mock_keypress(VB_KEY_DOWN);
		add_mock_keypress(VB_KEY_DOWN);
		add_mock_keypress(VB_KEY_ENTER);
		TEST_EQ(vb2_manual_recovery_menu(ctx),
			VB2_REQUEST_REBOOT,
			"Reboot immediately after request diagnostics");
		TEST_EQ(vb2_nv_get(ctx, VB2_NV_DIAG_REQUEST), 1,
			"VB2_NV_DIAG_REQUEST is set");
	}

	VB2_DEBUG("...done.\n");
}

static void language_selection_tests(void)
{
	VB2_DEBUG("Testing language selection...\n");

	/* Enter language menu and change language */
	reset_common_data(FOR_MANUAL_RECOVERY);
	mock_locale_count = 100;
	vb2_nv_set(ctx, VB2_NV_LOCALIZATION_INDEX, 23);
	add_mock_keypress(VB_KEY_UP);
	add_mock_keypress(VB_KEY_ENTER);	/* select language */
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);	/* select locale 24 */
	add_mock_vbtlk(VB2_ERROR_LK_NO_DISK_FOUND, VB_DISK_FLAG_REMOVABLE);
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"change language");
	DISPLAYED_EQ("RECOVERY_SELECT default", VB2_SCREEN_RECOVERY_SELECT,
		     23, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("RECOVERY_SELECT lang", VB2_SCREEN_RECOVERY_SELECT,
		     23, 0, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("LANGUAGE_SELECT 23", VB2_SCREEN_LANGUAGE_SELECT,
		     23, 23, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("LANGUAGE_SELECT 24", VB2_SCREEN_LANGUAGE_SELECT,
		     23, 24, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("RECOVERY_SELECT new locale", VB2_SCREEN_RECOVERY_SELECT,
		     24, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();
	TEST_EQ(vb2_nv_get(ctx, VB2_NV_LOCALIZATION_INDEX), 24,
		"  locale 24 saved to nvdata");

	/* Locale count = 0 */
	reset_common_data(FOR_MANUAL_RECOVERY);
	mock_locale_count = 0;
	vb2_nv_set(ctx, VB2_NV_LOCALIZATION_INDEX, 23);
	add_mock_keypress(VB_KEY_UP);
	add_mock_keypress(VB_KEY_ENTER);	/* select language */
	add_mock_keypress(VB_KEY_ENTER);	/* select locale 0 */
	add_mock_vbtlk(VB2_ERROR_LK_NO_DISK_FOUND, VB_DISK_FLAG_REMOVABLE);
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"enter language menu");
	DISPLAYED_EQ("RECOVERY_SELECT default", VB2_SCREEN_RECOVERY_SELECT,
		     23, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("RECOVERY_SELECT lang", VB2_SCREEN_RECOVERY_SELECT,
		     23, 0, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("LANGUAGE_SELECT index 0", VB2_SCREEN_LANGUAGE_SELECT,
		     23, 0, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("RECOVERY_SELECT locale 0", VB2_SCREEN_RECOVERY_SELECT,
		     0, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	VB2_DEBUG("...done.\n");
}

static void debug_info_tests(void)
{
	VB2_DEBUG("Testing debug info screen...\n");

	/* Tab = debug info for all menus */
	reset_common_data(FOR_DEVELOPER);
	add_mock_keypress('\t');
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"tab = debug info in dev mode");
	DISPLAYED_PASS();
	DISPLAYED_EQ("debug info", VB2_SCREEN_DEBUG_INFO, MOCK_IGNORE,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	reset_common_data(FOR_BROKEN_RECOVERY);
	add_mock_keypress('\t');
	TEST_EQ(vb2_broken_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"tab = debug info in broken recovery mode");
	DISPLAYED_PASS();
	DISPLAYED_EQ("debug info", VB2_SCREEN_DEBUG_INFO, MOCK_IGNORE,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	reset_common_data(FOR_MANUAL_RECOVERY);
	add_mock_keypress('\t');
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"tab = debug info in manual recovery mode");
	DISPLAYED_PASS();
	DISPLAYED_EQ("debug info", VB2_SCREEN_DEBUG_INFO, MOCK_IGNORE,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	/* Failed to enter debug info */
	reset_common_data(FOR_MANUAL_RECOVERY);
	mock_log_page_count = 0;
	add_mock_keypress('\t');
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"failed to enter debug info");
	DISPLAYED_PASS();
	DISPLAYED_PASS();  /* error code */
	DISPLAYED_NO_EXTRA();

	/* Get a one-page debug info */
	reset_common_data(FOR_MANUAL_RECOVERY);
	add_mock_keypress('\t');
	add_mock_keypress(VB_KEY_ENTER);  /* back */
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"get a one-page debug info");
	DISPLAYED_PASS();
	DISPLAYED_EQ("debug info", VB2_SCREEN_DEBUG_INFO,
		     MOCK_IGNORE, 3, 0x6, 0x0, 0);
	DISPLAYED_EQ("back to root screen", VB2_SCREEN_RECOVERY_SELECT,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	/* Get a three-page debug info and navigate */
	reset_common_data(FOR_MANUAL_RECOVERY);
	mock_log_page_count = 3;
	add_mock_keypress('\t');
	add_mock_keypress(VB_KEY_ENTER);  /* page 0, select on page down */
	add_mock_keypress(VB_KEY_ENTER);  /* page 1, select on page down */
	add_mock_keypress(VB_KEY_UP);	  /* page 2, select on page down */
	add_mock_keypress(VB_KEY_ENTER);  /* page 2, select on page up */
	add_mock_keypress(VB_KEY_ENTER);  /* page 1, select on page up */
	add_mock_keypress(VB_KEY_DOWN);	  /* page 0, select on page up */
	add_mock_keypress(VB_KEY_ENTER);  /* page 0, select on page down */
	add_mock_keypress(VB_KEY_DOWN);	  /* page 1, select on page down */
	add_mock_keypress(VB_KEY_ENTER);  /* page 1, select on back */
	extend_calls_until_shutdown();
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"get a three-page debug info and navigate");
	DISPLAYED_PASS();
	DISPLAYED_EQ("debug info page #0", VB2_SCREEN_DEBUG_INFO,
		     MOCK_IGNORE, 2, 0x2, 0x0, 0);
	DISPLAYED_EQ("debug info page #1", VB2_SCREEN_DEBUG_INFO,
		     MOCK_IGNORE, 2, 0x0, 0x0, 1);
	DISPLAYED_EQ("debug info page #2", VB2_SCREEN_DEBUG_INFO,
		     MOCK_IGNORE, 2, 0x4, 0x0, 2);
	DISPLAYED_EQ("debug info page #2", VB2_SCREEN_DEBUG_INFO,
		     MOCK_IGNORE, 1, 0x4, 0x0, 2);
	DISPLAYED_EQ("debug info page #1", VB2_SCREEN_DEBUG_INFO,
		     MOCK_IGNORE, 1, 0x0, 0x0, 1);
	DISPLAYED_EQ("debug info page #0", VB2_SCREEN_DEBUG_INFO,
		     MOCK_IGNORE, 1, 0x2, 0x0, 0);
	DISPLAYED_EQ("debug info page #0", VB2_SCREEN_DEBUG_INFO,
		     MOCK_IGNORE, 2, 0x2, 0x0, 0);
	DISPLAYED_EQ("debug info page #1", VB2_SCREEN_DEBUG_INFO,
		     MOCK_IGNORE, 2, 0x0, 0x0, 1);
	DISPLAYED_EQ("debug info page #1", VB2_SCREEN_DEBUG_INFO,
		     MOCK_IGNORE, 3, 0x0, 0x0, 1);
	DISPLAYED_EQ("back to root screen", VB2_SCREEN_RECOVERY_SELECT,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	VB2_DEBUG("...done.\n");
}

static void firmware_log_tests(void)
{
	VB2_DEBUG("Testing firmware log screens...\n");

	/* Get firmware log */
	reset_common_data(FOR_MANUAL_RECOVERY);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);
	if (DIAGNOSTIC_UI)
		add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"get firmware log");
	TEST_EQ(mock_prepare_log_count, 1,
		"  prepared firmware log once");
	TEST_EQ(strcmp(mock_prepare_log[0], "1"), 0,
		"  got correct firmware log");

	/* Enter firmware log screen again will reacquire a newer one */
	reset_common_data(FOR_MANUAL_RECOVERY);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);
	if (DIAGNOSTIC_UI)
		add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_ENTER);
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"enter the screen again and reacquire a new log");
	TEST_EQ(mock_prepare_log_count, 2,
		"  prepared firmware log twice");
	TEST_EQ(strcmp(mock_prepare_log[0], "1"), 0,
		"  got correct firmware log");
	TEST_EQ(strcmp(mock_prepare_log[1], "2"), 0,
		"  got a new firmware log");

	/* Back to firmware log screen again will not reacquire a newer one */
	reset_common_data(FOR_MANUAL_RECOVERY);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);
	if (DIAGNOSTIC_UI)
		add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	add_mock_keypress('\t');  /* enter debug info screen */
	add_mock_keypress(VB_KEY_ESC);
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"back to the screen and do not reacquire a new log");
	TEST_EQ(mock_prepare_log_count, 3,
		"  prepared firmware log three times");
	TEST_EQ(strcmp(mock_prepare_log[0], "1"), 0,
		"  got correct firmware log");
	/* Skip entry #1 which is for preparing debug info */
	TEST_EQ(strcmp(mock_prepare_log[2], "1"), 0,
		"  got the same firmware log");

	VB2_DEBUG("...done.\n");
}

static void developer_screen_tests(void)
{
	VB2_DEBUG("Testing developer mode screens...\n");

	/* Dev mode: default selected item */
	reset_common_data(FOR_DEVELOPER);
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
		"dev mode screen: set default selection to boot internal");
	DISPLAYED_EQ("dev mode screen", VB2_SCREEN_DEVELOPER_MODE,
		     MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);

	reset_common_data(FOR_DEVELOPER);
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_REMOVABLE);
	mock_default_boot = VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL;
	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
		"dev mode screen: set default selection to boot external");
	DISPLAYED_EQ("dev mode screen", VB2_SCREEN_DEVELOPER_MODE,
		     MOCK_IGNORE, 3, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);

	/* Dev mode: disabled and hidden item mask */
	reset_common_data(FOR_DEVELOPER);
	mock_dev_boot_legacy_allowed = 1;
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
		"dev mode screen: no disabled or hidden item");
	DISPLAYED_EQ("dev mode screen", VB2_SCREEN_DEVELOPER_MODE,
		     MOCK_IGNORE, MOCK_IGNORE, 0x0, 0x0, MOCK_IGNORE);

	reset_common_data(FOR_DEVELOPER);
	mock_dev_boot_legacy_allowed = 1;
	gbb.flags |= VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON;
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
		"dev mode screen: hide to_norm item");
	DISPLAYED_EQ("dev mode screen", VB2_SCREEN_DEVELOPER_MODE,
		     MOCK_IGNORE, MOCK_IGNORE, 0x0, 0x2, MOCK_IGNORE);

	reset_common_data(FOR_DEVELOPER);
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
	mock_dev_boot_external_allowed = 0;
	mock_dev_boot_legacy_allowed = 1;
	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
		"dev mode screen: hide boot external");
	DISPLAYED_EQ("dev mode screen", VB2_SCREEN_DEVELOPER_MODE,
		     MOCK_IGNORE, MOCK_IGNORE, 0x0, 0x8, MOCK_IGNORE);

	/* Dev mode screen */
	reset_common_data(FOR_DEVELOPER);  /* Select #2 by default */
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
	/* #0: Language menu */
	add_mock_keypress(VB_KEY_UP);
	add_mock_keypress(VB_KEY_UP);
	add_mock_keypress(VB_KEY_ENTER);
	/* #1: Return to secure mode */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* #2: Boot internal */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
		"dev mode screen");
	/* #0: Language menu */
	DISPLAYED_PASS();
	DISPLAYED_PASS();
	DISPLAYED_EQ("dev mode", VB2_SCREEN_DEVELOPER_MODE,
		     MOCK_IGNORE, 0, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#0: language menu", VB2_SCREEN_LANGUAGE_SELECT,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #1: Return to secure mode */
	DISPLAYED_PASS();
	DISPLAYED_EQ("dev mode", VB2_SCREEN_DEVELOPER_MODE,
		     MOCK_IGNORE, 1, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#1: return to secure mode", VB2_SCREEN_DEVELOPER_TO_NORM,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #2: Boot internal */
	DISPLAYED_PASS();
	DISPLAYED_EQ("dev mode", VB2_SCREEN_DEVELOPER_MODE,
		     MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	VB2_DEBUG("#2: boot internal (no extra screen)\n");
	DISPLAYED_NO_EXTRA();

	reset_common_data(FOR_DEVELOPER);  /* Select #3 by default */
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_REMOVABLE);
	mock_default_boot = VB2_DEV_DEFAULT_BOOT_TARGET_EXTERNAL;
	/* #3: Boot external */
	add_mock_keypress(VB_KEY_ENTER);
	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
		"dev mode screen");
	/* #3: Boot external */
	DISPLAYED_EQ("dev mode", VB2_SCREEN_DEVELOPER_MODE,
		     MOCK_IGNORE, 3, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	VB2_DEBUG("#3: boot external (no extra screen)\n");
	DISPLAYED_NO_EXTRA();

	reset_common_data(FOR_DEVELOPER);  /* Select #2 by default */
	mock_dev_boot_legacy_allowed = 1;
	/* #4: Alternate boot */
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	add_mock_keypress(VB_KEY_ENTER);
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"dev mode screen");
	TEST_EQ(mock_vbexlegacy_called, 1, "  VbExLegacy called");

	reset_common_data(FOR_DEVELOPER);  /* Select #2 by default */
	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
	/* #5: Advanced options */
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* End of menu */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);  /* Blocked */
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"dev mode screen");
	/* #5: Advanced options */
	DISPLAYED_PASS();
	DISPLAYED_PASS();
	DISPLAYED_EQ("dev mode", VB2_SCREEN_DEVELOPER_MODE,
		     MOCK_IGNORE, 5, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#4: advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* End of menu */
	DISPLAYED_PASS();
	DISPLAYED_EQ("end of menu", VB2_SCREEN_DEVELOPER_MODE,
		     MOCK_IGNORE, 6, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	/* Advanced options screen: disabled and hidden item mask */
	reset_common_data(FOR_DEVELOPER);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"advanced options screen: hide to_dev");
	DISPLAYED_PASS();
	DISPLAYED_PASS();
	DISPLAYED_PASS();
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, MOCK_IGNORE, 0x0, 0x2, MOCK_IGNORE);

	/* Advanced options screen */
	reset_common_data(FOR_DEVELOPER);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* #0: Language menu */
	add_mock_keypress(VB_KEY_UP);
	add_mock_keypress(VB_KEY_ENTER);
	/* #1: (Hidden) */
	/* #2: Debug info */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* #3: Firmware log */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* #4: Back */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* End of menu */
	add_mock_keypress(VB_KEY_ENTER);
	extend_calls_until_shutdown();
	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"advanced options screen");
	DISPLAYED_PASS();
	DISPLAYED_PASS();
	DISPLAYED_PASS();
	DISPLAYED_PASS();
	/* #0: Language menu */
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 0, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#0: language menu", VB2_SCREEN_LANGUAGE_SELECT,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #1: (Hidden) */
	/* #2: Debug info */
	DISPLAYED_PASS();
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#2: debug info", VB2_SCREEN_DEBUG_INFO,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #3: Firmware log */
	DISPLAYED_PASS();
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 3, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#3: firmware log", VB2_SCREEN_FIRMWARE_LOG,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #4: Back */
	DISPLAYED_PASS();
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 4, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#4: back", VB2_SCREEN_DEVELOPER_MODE,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* End of menu */
	DISPLAYED_EQ("end of menu", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	VB2_DEBUG("...done.\n");
}

static void broken_recovery_screen_tests(void)
{
	/* Broken screen: disabled and hidden item mask */
	reset_common_data(FOR_BROKEN_RECOVERY);
	TEST_EQ(vb2_broken_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"broken screen: no disabled and hidden item mask");
	DISPLAYED_EQ("broken screen", VB2_SCREEN_RECOVERY_BROKEN,
		     MOCK_IGNORE, MOCK_IGNORE, 0x0, 0x0, MOCK_IGNORE);

	/* Broken screen */
	reset_common_data(FOR_BROKEN_RECOVERY);
	/* #0: Language menu */
	add_mock_keypress(VB_KEY_UP);
	add_mock_keypress(VB_KEY_ENTER);
	/* #1: Advanced options */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* End of menu */
	add_mock_keypress(VB_KEY_ESC);
	extend_calls_until_shutdown();
	TEST_EQ(vb2_broken_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"broken screen");
	/* #0: Language menu */
	DISPLAYED_PASS();
	DISPLAYED_EQ("broken screen", VB2_SCREEN_RECOVERY_BROKEN,
		     MOCK_IGNORE, 0, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#0: language menu", VB2_SCREEN_LANGUAGE_SELECT,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #1: Advanced options */
	DISPLAYED_PASS();
	DISPLAYED_EQ("broken screen", VB2_SCREEN_RECOVERY_BROKEN,
		     MOCK_IGNORE, 1, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#1: advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* End of menu */
	DISPLAYED_EQ("end of menu", VB2_SCREEN_RECOVERY_BROKEN,
		     MOCK_IGNORE, 1, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	/* Advanced options screen: disabled and hidden item mask */
	reset_common_data(FOR_BROKEN_RECOVERY);
	add_mock_keypress(VB_KEY_ENTER);
	TEST_EQ(vb2_broken_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"advanced options screen: hide to_dev item");
	DISPLAYED_PASS();
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, MOCK_IGNORE, 0x0, 0x2, MOCK_IGNORE);

	/* Advanced options screen */
	reset_common_data(FOR_BROKEN_RECOVERY);
	add_mock_keypress(VB_KEY_ENTER);
	/* #0: Language menu */
	add_mock_keypress(VB_KEY_UP);
	add_mock_keypress(VB_KEY_ENTER);
	/* #1: (Hidden) */
	/* #2: Debug info */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* #3: Firmware log */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* #4: Back */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* End of menu */
	add_mock_keypress(VB_KEY_ENTER);
	extend_calls_until_shutdown();
	TEST_EQ(vb2_broken_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"advanced options screen");
	DISPLAYED_PASS();
	DISPLAYED_PASS();
	/* #0: Language menu */
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 0, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#0: language menu", VB2_SCREEN_LANGUAGE_SELECT,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #1: (Hidden) */
	/* #2: Debug info */
	DISPLAYED_PASS();
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#2: debug info", VB2_SCREEN_DEBUG_INFO,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #3: Firmware log */
	DISPLAYED_PASS();
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 3, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#3: firmware log", VB2_SCREEN_FIRMWARE_LOG,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #4: Back */
	DISPLAYED_PASS();
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 4, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#4: back", VB2_SCREEN_RECOVERY_BROKEN,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* End of menu */
	DISPLAYED_EQ("end of menu", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	VB2_DEBUG("...done.\n");
}

static void manual_recovery_screen_tests(void)
{
	/* Recovery select screen: disabled and hidden item mask */
	reset_common_data(FOR_MANUAL_RECOVERY);
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		DIAGNOSTIC_UI ?
			"recovery select screen: no disabled or hidden item" :
			"recovery select screen: hide `launch diag`");
	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
		     MOCK_IGNORE, MOCK_IGNORE, 0x0, DIAGNOSTIC_UI ? 0x0 : 0x8,
		     MOCK_IGNORE);

	/* Recovery select screen */
	reset_common_data(FOR_MANUAL_RECOVERY);
	/* #0: Language menu */
	add_mock_keypress(VB_KEY_UP);
	add_mock_keypress(VB_KEY_ENTER);
	/* #1: Phone recovery */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* #2: External disk recovery */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	add_mock_keypress(VB_KEY_ESC);
	/* #3: Launch diagnostics */
	if (DIAGNOSTIC_UI)
		add_mock_keypress(VB_KEY_DOWN);
	/* #4: Advanced options */
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* End of menu */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);  /* Blocked */
	extend_calls_until_shutdown();
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"recovery select screen");
	/* #0: Language menu */
	DISPLAYED_PASS();
	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
		     MOCK_IGNORE, 0, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#0: language menu", VB2_SCREEN_LANGUAGE_SELECT,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #1: Phone recovery */
	DISPLAYED_PASS();
	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
		     MOCK_IGNORE, 1, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#1: phone recovery", VB2_SCREEN_RECOVERY_PHONE_STEP1,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #2: External disk recovery */
	DISPLAYED_PASS();
	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
		     MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#2: disk recovery", VB2_SCREEN_RECOVERY_DISK_STEP1,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
		MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	/* #3: Launch diagnostics */
	if (DIAGNOSTIC_UI)
		DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
			MOCK_IGNORE, 3, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	/* #4: Advanced options */
	DISPLAYED_EQ("recovery select", VB2_SCREEN_RECOVERY_SELECT,
		     MOCK_IGNORE, 4, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);

	DISPLAYED_EQ("#3: advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* End of menu */
	DISPLAYED_PASS();
	DISPLAYED_EQ("end of menu", VB2_SCREEN_RECOVERY_SELECT,
		     MOCK_IGNORE, 5, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	/* Advanced options screen: disabled and hidden item mask */
	reset_common_data(FOR_MANUAL_RECOVERY);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);
	if (DIAGNOSTIC_UI)
		add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"advanced options screen: no disabled or hidden item");
	DISPLAYED_PASS();
	DISPLAYED_PASS();
	if (DIAGNOSTIC_UI)
		DISPLAYED_PASS();
	DISPLAYED_PASS();
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, MOCK_IGNORE, 0x0, 0x0, MOCK_IGNORE);

	/* Advanced options screen */
	reset_common_data(FOR_MANUAL_RECOVERY);
	/* #0: Language menu */
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_DOWN);
	if (DIAGNOSTIC_UI)
		add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	add_mock_keypress(VB_KEY_UP);
	add_mock_keypress(VB_KEY_ENTER);
	/* #1: Enable dev mode */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* #2: Debug info */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* #3: Firmware log */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* #4: Back */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* End of menu */
	add_mock_keypress(VB_KEY_ENTER);
	add_mock_keypress(VB_KEY_DOWN);
	extend_calls_until_shutdown();
	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"advanced options screen");
	DISPLAYED_PASS();
	DISPLAYED_PASS();
	if (DIAGNOSTIC_UI)
		DISPLAYED_PASS();
	DISPLAYED_PASS();
	/* #0: Language menu */
	DISPLAYED_PASS();
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 0, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#0: language menu", VB2_SCREEN_LANGUAGE_SELECT,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #1: Enable dev mode */
	DISPLAYED_PASS();
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 1, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#1: enable dev mode", VB2_SCREEN_RECOVERY_TO_DEV,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #2: Debug info */
	DISPLAYED_PASS();
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#2: debug info", VB2_SCREEN_DEBUG_INFO,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #3: Firmware log */
	DISPLAYED_PASS();
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 3, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#3: firmware log", VB2_SCREEN_FIRMWARE_LOG,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #4: Back */
	DISPLAYED_PASS();
	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 4, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#4: back", VB2_SCREEN_RECOVERY_SELECT,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* End of menu */
	DISPLAYED_PASS();
	DISPLAYED_EQ("end of menu", VB2_SCREEN_ADVANCED_OPTIONS,
		     MOCK_IGNORE, 2, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	VB2_DEBUG("...done.\n");
}

static void diagnostics_screen_tests(void)
{
	VB2_DEBUG("Testing diagnostic screens...\n");

	/* Diagnostics screen: disabled and hidden item mask */
	reset_common_data(FOR_DIAGNOSTICS);
	TEST_EQ(vb2_diagnostic_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"diagnostic screen: no disabled or hidden item");
	DISPLAYED_EQ("diagnostic menu", VB2_SCREEN_DIAGNOSTICS,
		     MOCK_IGNORE, MOCK_IGNORE, 0x0, 0x0, MOCK_IGNORE);

	/* Diagnostics screen */
	reset_common_data(FOR_DIAGNOSTICS);

	/* #0: Language menu */
	add_mock_keypress(VB_KEY_UP);
	add_mock_keypress(VB_KEY_ENTER);
	/* #1: Storage screen */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* #2: Quick memory test screen */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* #3: Full memory test screen */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	/* #4: Power off (End of menu) */
	add_mock_keypress(VB_KEY_ESC);
	add_mock_keypress(VB_KEY_DOWN);
	add_mock_keypress(VB_KEY_ENTER);
	mock_calls_until_shutdown = -1;
	TEST_EQ(vb2_diagnostic_menu(ctx), VB2_REQUEST_SHUTDOWN,
		"diagnostic screen");

	DISPLAYED_EQ("default on first button of menu",
		     VB2_SCREEN_DIAGNOSTICS, MOCK_IGNORE, 1, MOCK_IGNORE,
		     MOCK_IGNORE, MOCK_IGNORE);
	/* #0: Language menu */
	DISPLAYED_EQ("language selection",
		     VB2_SCREEN_DIAGNOSTICS, MOCK_IGNORE, 0, MOCK_IGNORE,
		     MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#0: language menu", VB2_SCREEN_LANGUAGE_SELECT,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #1: Storage screen */
	DISPLAYED_PASS();
	DISPLAYED_EQ("storage button",
		     VB2_SCREEN_DIAGNOSTICS, MOCK_IGNORE, 1, MOCK_IGNORE,
		     MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#1: storage screen", VB2_SCREEN_DIAGNOSTICS_STORAGE,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE,
		     MOCK_IGNORE);
	/* #2: Quick memory test screen */
	DISPLAYED_PASS();
	DISPLAYED_EQ("quick memory test button",
		     VB2_SCREEN_DIAGNOSTICS, MOCK_IGNORE, 2, MOCK_IGNORE,
		     MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#1: quick memory test screen",
		     VB2_SCREEN_DIAGNOSTICS_MEMORY_QUICK, MOCK_IGNORE,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	/* #3: Full memory test screen */
	DISPLAYED_PASS();
	DISPLAYED_EQ("full memory test button",
		     VB2_SCREEN_DIAGNOSTICS, MOCK_IGNORE, 3, MOCK_IGNORE,
		     MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_EQ("#3: full memory test screen",
		     VB2_SCREEN_DIAGNOSTICS_MEMORY_FULL, MOCK_IGNORE,
		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
	/* #4: Power of (End of menu) */
	DISPLAYED_PASS();
	DISPLAYED_EQ("power off",
		     VB2_SCREEN_DIAGNOSTICS, MOCK_IGNORE, 4, MOCK_IGNORE,
		     MOCK_IGNORE, MOCK_IGNORE);
	DISPLAYED_NO_EXTRA();

	VB2_DEBUG("...done.\n");
}

int main(void)
{
	developer_tests();
	broken_recovery_tests();
	manual_recovery_tests();
	language_selection_tests();
	debug_info_tests();
	firmware_log_tests();

	/* Screen displayed */
	developer_screen_tests();
	broken_recovery_screen_tests();
	manual_recovery_screen_tests();
	diagnostics_screen_tests();

	return gTestSuccess ? 0 : 255;
}
