vboot/ui: Add tests for developer screens

Unit tests paired with CL:2192863.

BUG=b:146399181, b:156448738
TEST=CC=x86_64-pc-linux-gnu-clang;
     make clean && make runtests
TEST=CC=x86_64-pc-linux-gnu-clang; DETACHABLE=1;
     make clean && make runtests
TEST=CC=x86_64-pc-linux-gnu-clang; PHYSICAL_PRESENCE_KEYBOARD=1;
     make clean && make runtests
BRANCH=none

Signed-off-by: Hsuan Ting Chen <roccochen@chromium.org>
Change-Id: I40bb9fbe111a1ab6e6123bc18af0753100bfb08b
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/2228255
Reviewed-by: Joel Kitching <kitching@chromium.org>
diff --git a/tests/vb2_ui_tests.c b/tests/vb2_ui_tests.c
index 6c507b8..9a69177 100644
--- a/tests/vb2_ui_tests.c
+++ b/tests/vb2_ui_tests.c
@@ -18,6 +18,9 @@
 /* Fixed value for ignoring some checks */
 #define MOCK_IGNORE 0xffffu
 
+/* Fuzzy matches for beep time */
+#define MOCK_BEEP_FUZZ_MS 200
+
 /* Mock data */
 struct display_call {
 	const struct vb2_screen_info *screen;
@@ -54,7 +57,9 @@
 static uint32_t mock_get_timer_last;
 static uint32_t mock_time;
 static const uint32_t mock_time_start = 31ULL * VB2_MSEC_PER_SEC;
-static int mock_vbexbeep_called;
+
+static int mock_beeped_ms[2];
+static int mock_vb2ex_beep_called;
 
 static enum vb2_dev_default_boot_target mock_default_boot;
 static int mock_dev_boot_allowed;
@@ -244,16 +249,19 @@
 	memset(mock_key_trusted, 0, sizeof(mock_key_trusted));
 	mock_key_total = 0;
 
-	/* For vboot_audio.h */
+	/* For vb2ex_mtime and vb2ex_msleep  */
 	mock_get_timer_last = 0;
 	mock_time = mock_time_start;
-	mock_vbexbeep_called = 0;
+
+	/* For vb2ex_beep */
+	memset(mock_beeped_ms, 0, sizeof(mock_beeped_ms));
+	mock_vb2ex_beep_called = 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 = 0;
+	mock_dev_boot_external_allowed = 1;
 
 	/* For VbExLegacy */
 	mock_vbexlegacy_called = 0;
@@ -373,7 +381,10 @@
 
 void vb2ex_beep(uint32_t msec, uint32_t frequency)
 {
-	mock_vbexbeep_called++;
+	if (mock_vb2ex_beep_called < ARRAY_SIZE(mock_beeped_ms))
+		mock_beeped_ms[mock_vb2ex_beep_called] = mock_time;
+	mock_time += msec;
+	mock_vb2ex_beep_called++;
 }
 
 enum vb2_dev_default_boot_target vb2api_get_dev_default_boot_target(
@@ -442,6 +453,16 @@
 {
 	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);
@@ -449,30 +470,189 @@
 		"proceed to internal disk after timeout");
 	TEST_TRUE(mock_get_timer_last - mock_time_start >=
 		  30 * VB2_MSEC_PER_SEC, "  finished delay");
-	TEST_EQ(mock_vbexbeep_called, 2, "  beeped twice");
+	TEST_EQ(mock_vb2ex_beep_called, 2, "  beeped twice");
+	TEST_TRUE(mock_beeped_ms[0] - mock_time_start >=
+		  20 * VB2_MSEC_PER_SEC, "  beep #0: at 20s");
+	TEST_TRUE(mock_beeped_ms[0] - mock_time_start <=
+		  20 * VB2_MSEC_PER_SEC + MOCK_BEEP_FUZZ_MS,
+		  "  beep #0: fuzzy matching");
+	TEST_TRUE(mock_beeped_ms[1] - mock_time_start >=
+		  20 * VB2_MSEC_PER_SEC + 500, "  beep #1: at 20.5s");
+	TEST_TRUE(mock_beeped_ms[1] - mock_time_start <=
+		  20 * VB2_MSEC_PER_SEC + 500 + MOCK_BEEP_FUZZ_MS,
+		  "  beep #1: fuzzy matching");
 	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");
+	TEST_TRUE(mock_get_timer_last - mock_time_start >=
+		  2 * VB2_MSEC_PER_SEC, "  finished delay");
+	TEST_TRUE(mock_get_timer_last - mock_time_start <
+		  30 * VB2_MSEC_PER_SEC, "  not a 30s delay");
+	TEST_EQ(mock_vb2ex_beep_called, 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_EQ(mock_calls_until_shutdown, 0, "  loop forever");
+	TEST_EQ(mock_vb2ex_beep_called, 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_EQ(mock_calls_until_shutdown, 0, "  loop forever");
+	TEST_EQ(mock_vb2ex_beep_called, 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");
+	TEST_TRUE(mock_get_timer_last - mock_time_start <
+		  30 * VB2_MSEC_PER_SEC, "  delay aborted");
+
+	/* 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");
+	TEST_TRUE(mock_get_timer_last - mock_time_start <
+		  30 * VB2_MSEC_PER_SEC, "  delay aborted");
+
+	/* 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");
+		TEST_TRUE(mock_get_timer_last - mock_time_start <
+			  30 * VB2_MSEC_PER_SEC, "  delay aborted");
+	}
+
 	/* 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;
-	mock_dev_boot_external_allowed = 1;
 	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
 		"proceed to external disk after timeout");
 	TEST_TRUE(mock_get_timer_last - mock_time_start >=
 		  30 * VB2_MSEC_PER_SEC, "  finished delay");
-	TEST_EQ(mock_vbexbeep_called, 2, "  beeped twice");
+	TEST_EQ(mock_vb2ex_beep_called, 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 not allowed, don't boot");
-	TEST_TRUE(mock_get_timer_last - mock_time_start >=
-		  30 * VB2_MSEC_PER_SEC, "  finished delay");
-	TEST_EQ(mock_vbexbeep_called, 2, "  beeped twice");
-	TEST_TRUE(mock_iters >= mock_vbtlk_total, "  used up mock_vbtlk");
+		"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");
+	TEST_TRUE(mock_get_timer_last - mock_time_start <
+		  30 * VB2_MSEC_PER_SEC, "  delay aborted");
+
+	/* 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");
+	TEST_TRUE(mock_get_timer_last - mock_time_start <
+		  30 * VB2_MSEC_PER_SEC, "  delay aborted");
+
+	/* 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");
+		TEST_TRUE(mock_get_timer_last - mock_time_start <
+			  30 * VB2_MSEC_PER_SEC, "  delay aborted");
+	}
+
+	/* 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);
+	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");
 
 	VB2_DEBUG("...done.\n");
 }
@@ -481,7 +661,7 @@
 {
 	VB2_DEBUG("Testing broken recovery mode...\n");
 
-	/* BROKEN screen shutdown request */
+	/* Power button short pressed = shutdown request */
 	if (!DETACHABLE) {
 		reset_common_data(FOR_BROKEN_RECOVERY);
 		add_mock_keypress(VB_BUTTON_POWER_SHORT_PRESS);
@@ -564,7 +744,7 @@
 	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");
+		"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);
@@ -574,16 +754,17 @@
 		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	DISPLAYED_NO_EXTRA();
 
-	/* Cancel */
+	/* 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");
+	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 */
+	/* Confirm to_dev transition */
 	reset_common_data(FOR_MANUAL_RECOVERY);
 	add_mock_key(VB_KEY_CTRL('D'), 1);
 	if (PHYSICAL_PRESENCE_KEYBOARD) {
@@ -595,7 +776,7 @@
 		add_mock_pp_pressed(0);
 	}
 	TEST_EQ(vb2_manual_recovery_menu(ctx), VB2_REQUEST_REBOOT_EC_TO_RO,
-		"confirm");
+		"confirm to_dev transition");
 	if (!PHYSICAL_PRESENCE_KEYBOARD)
 		TEST_TRUE(mock_iters >= mock_pp_pressed_total - 1,
 			  "  used up mock_pp_pressed");
@@ -758,19 +939,124 @@
 {
 	VB2_DEBUG("Testing developer mode screens...\n");
 
-	/* Dev mode screen */
-	/* TODO: Check items */
+	/* 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");
+		"dev mode screen: set default selection to boot internal");
 	DISPLAYED_EQ("dev mode screen", VB2_SCREEN_DEVELOPER_MODE,
+		     MOCK_IGNORE, 2, 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);
+
+	/* Dev mode: disabled item mask */
+	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: no disabled item mask");
+	DISPLAYED_EQ("dev mode screen", VB2_SCREEN_DEVELOPER_MODE,
+		     MOCK_IGNORE, MOCK_IGNORE, 0x0);
+
+	reset_common_data(FOR_DEVELOPER);
+	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: disable to_norm item");
+	DISPLAYED_EQ("dev mode screen", VB2_SCREEN_DEVELOPER_MODE,
+		     MOCK_IGNORE, MOCK_IGNORE, 0x2);
+
+	reset_common_data(FOR_DEVELOPER);
+	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
+	mock_dev_boot_external_allowed = 0;
+	TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS,
+		"dev mode screen: disable boot external");
+	DISPLAYED_EQ("dev mode screen", VB2_SCREEN_DEVELOPER_MODE,
+		     MOCK_IGNORE, MOCK_IGNORE, 0x8);
+
+	/* 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_UP);
+	add_mock_keypress(VB_KEY_ENTER);
+	/* #2: Boot internal */
+	add_mock_keypress(VB_KEY_ESC);
+	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);
+	DISPLAYED_EQ("#0: language menu", VB2_SCREEN_LANGUAGE_SELECT,
 		     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);
+	DISPLAYED_EQ("#1: return to secure mode", VB2_SCREEN_DEVELOPER_TO_NORM,
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+	/* #2: Boot internal */
+	DISPLAYED_EQ("dev mode", VB2_SCREEN_DEVELOPER_MODE,
+		     MOCK_IGNORE, 2, 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);
+	VB2_DEBUG("#3: boot external (no extra screen)\n");
+	DISPLAYED_NO_EXTRA();
+
+	reset_common_data(FOR_DEVELOPER);  /* Select #2 by default */
+	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
+	/* #4: 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);
+	add_mock_keypress(VB_KEY_DOWN);  /* Blocked */
+	TEST_EQ(vb2_developer_menu(ctx), VB2_REQUEST_SHUTDOWN,
+		"dev mode screen");
+	/* #4: Advanced options */
+	DISPLAYED_PASS();
+	DISPLAYED_PASS();
+	DISPLAYED_EQ("dev mode", VB2_SCREEN_DEVELOPER_MODE,
+		     MOCK_IGNORE, 4, MOCK_IGNORE);
+	DISPLAYED_EQ("#4: advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
+		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
+	/* End of menu */
+	DISPLAYED_PASS();
+	DISPLAYED_PASS();
+	DISPLAYED_EQ("end of menu", VB2_SCREEN_DEVELOPER_MODE,
+		     MOCK_IGNORE, 4, MOCK_IGNORE);
 	DISPLAYED_NO_EXTRA();
 
 	/* Advanced options screen */
 	reset_common_data(FOR_DEVELOPER);
-	add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED);
+	add_mock_keypress(VB_KEY_DOWN);
 	add_mock_keypress(VB_KEY_DOWN);
 	add_mock_keypress(VB_KEY_ENTER);
 	/* #0: Language menu */
@@ -780,10 +1066,12 @@
 	/* #2: Back */
 	add_mock_keypress(VB_KEY_ESC);
 	add_mock_keypress(VB_KEY_DOWN);
+	add_mock_keypress(VB_KEY_DOWN);
 	add_mock_keypress(VB_KEY_ENTER);
 	add_mock_keypress(VB_KEY_ENTER);
 	/* End of menu */
 	add_mock_keypress(VB_KEY_DOWN);
+	add_mock_keypress(VB_KEY_DOWN);
 	add_mock_keypress(VB_KEY_ENTER);
 	add_mock_keypress(VB_KEY_DOWN);
 	extend_calls_until_shutdown();
@@ -791,6 +1079,7 @@
 		"advanced options screen");
 	DISPLAYED_PASS();
 	DISPLAYED_PASS();
+	DISPLAYED_PASS();
 	/* #0: Language menu */
 	DISPLAYED_PASS();
 	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
@@ -801,12 +1090,14 @@
 	/* #2: Back */
 	DISPLAYED_PASS();
 	DISPLAYED_PASS();
+	DISPLAYED_PASS();
 	DISPLAYED_EQ("advanced options", VB2_SCREEN_ADVANCED_OPTIONS,
 		     MOCK_IGNORE, 2, 0x2);
 	DISPLAYED_EQ("#2: back", VB2_SCREEN_DEVELOPER_MODE,
 		     MOCK_IGNORE, MOCK_IGNORE, MOCK_IGNORE);
 	/* End of menu */
 	DISPLAYED_PASS();
+	DISPLAYED_PASS();
 	DISPLAYED_EQ("end of menu", VB2_SCREEN_ADVANCED_OPTIONS,
 		     MOCK_IGNORE, 2, MOCK_IGNORE);
 	DISPLAYED_NO_EXTRA();