summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2018-05-12 11:31:26 +0200
committerandroid-build-team Robot <android-build-team-robot@google.com>2018-05-12 11:31:26 +0200
commite39b6742f30b9a2cbda7965591fc2cc5a35efee0 (patch)
tree3639d50415a008b071b82babcd33951ea2affd94
parentSnap for 4775956 from 76e673417377b9ef960fba4dc7d1e8c2a505a679 to qt-release (diff)
parentMerge "Clean up the Makefile for recovery and tests." am: 1f0b9962b0 am: 0c2998ffe3 (diff)
downloadandroid_bootable_recovery-e39b6742f30b9a2cbda7965591fc2cc5a35efee0.tar
android_bootable_recovery-e39b6742f30b9a2cbda7965591fc2cc5a35efee0.tar.gz
android_bootable_recovery-e39b6742f30b9a2cbda7965591fc2cc5a35efee0.tar.bz2
android_bootable_recovery-e39b6742f30b9a2cbda7965591fc2cc5a35efee0.tar.lz
android_bootable_recovery-e39b6742f30b9a2cbda7965591fc2cc5a35efee0.tar.xz
android_bootable_recovery-e39b6742f30b9a2cbda7965591fc2cc5a35efee0.tar.zst
android_bootable_recovery-e39b6742f30b9a2cbda7965591fc2cc5a35efee0.zip
-rw-r--r--Android.mk115
-rw-r--r--device.cpp80
-rw-r--r--device.h6
-rw-r--r--minui/include/private/resources.h3
-rw-r--r--minui/resources.cpp9
-rw-r--r--otautil/include/otautil/paths.h10
-rw-r--r--otautil/paths.cpp2
-rw-r--r--recovery.cpp4
-rw-r--r--screen_ui.cpp12
-rw-r--r--tests/Android.mk100
-rw-r--r--tests/testdata/font.pngbin0 -> 24437 bytes
-rw-r--r--tests/testdata/loop00000.pngbin0 -> 5900 bytes
-rw-r--r--tests/unit/screen_ui_test.cpp167
13 files changed, 377 insertions, 131 deletions
diff --git a/Android.mk b/Android.mk
index dbd2eb788..09feba289 100644
--- a/Android.mk
+++ b/Android.mk
@@ -23,44 +23,21 @@ RECOVERY_FSTAB_VERSION := 2
# librecovery_ui_default, which uses ScreenRecoveryUI.
TARGET_RECOVERY_UI_LIB ?= librecovery_ui_default
-# librecovery (static library)
-# ===============================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- install.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
-
-ifeq ($(AB_OTA_UPDATER),true)
- LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
-endif
-
-LOCAL_MODULE := librecovery
-
-LOCAL_STATIC_LIBRARIES := \
- libminui \
- libotautil \
- libvintf_recovery \
- libcrypto_utils \
- libcrypto \
- libbase \
- libziparchive \
-
-include $(BUILD_STATIC_LIBRARY)
+recovery_common_cflags := \
+ -Wall \
+ -Werror \
+ -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
# librecovery_ui (static library)
# ===============================
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
+ device.cpp \
screen_ui.cpp \
ui.cpp \
vr_ui.cpp \
wear_ui.cpp
-LOCAL_CFLAGS := -Wall -Werror
-
LOCAL_MODULE := librecovery_ui
LOCAL_STATIC_LIBRARIES := \
@@ -68,6 +45,8 @@ LOCAL_STATIC_LIBRARIES := \
libotautil \
libbase
+LOCAL_CFLAGS := $(recovery_common_cflags)
+
ifneq ($(TARGET_RECOVERY_UI_MARGIN_HEIGHT),)
LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_HEIGHT=$(TARGET_RECOVERY_UI_MARGIN_HEIGHT)
else
@@ -118,13 +97,38 @@ endif
include $(BUILD_STATIC_LIBRARY)
+# librecovery (static library)
+# ===============================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ install.cpp
+
+LOCAL_CFLAGS := $(recovery_common_cflags)
+
+ifeq ($(AB_OTA_UPDATER),true)
+ LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
+endif
+
+LOCAL_MODULE := librecovery
+
+LOCAL_STATIC_LIBRARIES := \
+ libminui \
+ libotautil \
+ libvintf_recovery \
+ libcrypto_utils \
+ libcrypto \
+ libbase \
+ libziparchive \
+
+include $(BUILD_STATIC_LIBRARY)
+
# recovery (static executable)
# ===============================
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
adb_install.cpp \
- device.cpp \
fuse_sdcard_provider.cpp \
logging.cpp \
recovery.cpp \
@@ -135,20 +139,13 @@ LOCAL_MODULE := recovery
LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+
# Cannot link with LLD: undefined symbol: UsbNoPermissionsLongHelpText
# http://b/77543887, lld does not handle -Wl,--gc-sections as well as ld.
LOCAL_USE_CLANG_LLD := false
-LOCAL_REQUIRED_MODULES := e2fsdroid_static mke2fs_static mke2fs.conf
-
-ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
-ifeq ($(HOST_OS),linux)
-LOCAL_REQUIRED_MODULES += sload.f2fs mkfs.f2fs
-endif
-endif
-
-LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
-LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS := $(recovery_common_cflags)
LOCAL_C_INCLUDES += \
system/vold \
@@ -169,42 +166,52 @@ LOCAL_STATIC_LIBRARIES := \
LOCAL_STATIC_LIBRARIES += \
librecovery \
$(TARGET_RECOVERY_UI_LIB) \
- libverifier \
libbootloader_message \
- libfs_mgr \
- libext4_utils \
- libsparse \
- libziparchive \
- libotautil \
- libminadbd \
- libasyncio \
libfusesideload \
+ libminadbd \
librecovery_ui \
libminui \
- libpng \
+ libverifier \
+ libotautil \
+ libasyncio \
+ libbatterymonitor \
libcrypto_utils \
libcrypto \
+ libext4_utils \
+ libfs_mgr \
+ libpng \
+ libsparse \
libvintf_recovery \
libvintf \
libhidl-gen-utils \
libtinyxml2 \
+ libziparchive \
libbase \
libutils \
libcutils \
liblog \
libselinux \
- libz
+ libz \
LOCAL_HAL_STATIC_LIBRARIES := libhealthd
-ifeq ($(AB_OTA_UPDATER),true)
- LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
-endif
+LOCAL_REQUIRED_MODULES := \
+ e2fsdroid_static \
+ mke2fs_static \
+ mke2fs.conf
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
+ifeq ($(HOST_OS),linux)
+LOCAL_REQUIRED_MODULES += \
+ sload.f2fs \
+ mkfs.f2fs
+endif
+endif
ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),)
-LOCAL_REQUIRED_MODULES += recovery-persist recovery-refresh
+LOCAL_REQUIRED_MODULES += \
+ recovery-persist \
+ recovery-refresh
endif
include $(BUILD_EXECUTABLE)
diff --git a/device.cpp b/device.cpp
index 5cf9cc242..3c6334e5c 100644
--- a/device.cpp
+++ b/device.cpp
@@ -16,59 +16,57 @@
#include "device.h"
+#include <algorithm>
+#include <string>
+#include <utility>
+#include <vector>
+
#include <android-base/logging.h>
-#include <android-base/macros.h>
#include "ui.h"
-// clang-format off
-static constexpr const char* kItems[]{
- "Reboot system now",
- "Reboot to bootloader",
- "Apply update from ADB",
- "Apply update from SD card",
- "Wipe data/factory reset",
-#ifndef AB_OTA_UPDATER
- "Wipe cache partition",
-#endif // !AB_OTA_UPDATER
- "Mount /system",
- "View recovery logs",
- "Run graphics test",
- "Run locale test",
- "Power off",
-};
-// clang-format on
-
-// clang-format off
-static constexpr Device::BuiltinAction kMenuActions[] {
- Device::REBOOT,
- Device::REBOOT_BOOTLOADER,
- Device::APPLY_ADB_SIDELOAD,
- Device::APPLY_SDCARD,
- Device::WIPE_DATA,
-#ifndef AB_OTA_UPDATER
- Device::WIPE_CACHE,
-#endif // !AB_OTA_UPDATER
- Device::MOUNT_SYSTEM,
- Device::VIEW_RECOVERY_LOGS,
- Device::RUN_GRAPHICS_TEST,
- Device::RUN_LOCALE_TEST,
- Device::SHUTDOWN,
+static std::vector<std::pair<std::string, Device::BuiltinAction>> g_menu_actions{
+ { "Reboot system now", Device::REBOOT },
+ { "Reboot to bootloader", Device::REBOOT_BOOTLOADER },
+ { "Apply update from ADB", Device::APPLY_ADB_SIDELOAD },
+ { "Apply update from SD card", Device::APPLY_SDCARD },
+ { "Wipe data/factory reset", Device::WIPE_DATA },
+ { "Wipe cache partition", Device::WIPE_CACHE },
+ { "Mount /system", Device::MOUNT_SYSTEM },
+ { "View recovery logs", Device::VIEW_RECOVERY_LOGS },
+ { "Run graphics test", Device::RUN_GRAPHICS_TEST },
+ { "Run locale test", Device::RUN_LOCALE_TEST },
+ { "Power off", Device::SHUTDOWN },
};
-// clang-format on
-static_assert(arraysize(kItems) == arraysize(kMenuActions),
- "kItems and kMenuActions should have the same length.");
+static std::vector<std::string> g_menu_items;
+
+static void PopulateMenuItems() {
+ g_menu_items.clear();
+ std::transform(g_menu_actions.cbegin(), g_menu_actions.cend(), std::back_inserter(g_menu_items),
+ [](const auto& entry) { return entry.first; });
+}
+
+Device::Device(RecoveryUI* ui) : ui_(ui) {
+ PopulateMenuItems();
+}
+
+void Device::RemoveMenuItemForAction(Device::BuiltinAction action) {
+ g_menu_actions.erase(
+ std::remove_if(g_menu_actions.begin(), g_menu_actions.end(),
+ [action](const auto& entry) { return entry.second == action; }));
+ CHECK(!g_menu_actions.empty());
-static const std::vector<std::string> kMenuItems(kItems, kItems + arraysize(kItems));
+ // Re-populate the menu items.
+ PopulateMenuItems();
+}
const std::vector<std::string>& Device::GetMenuItems() {
- return kMenuItems;
+ return g_menu_items;
}
Device::BuiltinAction Device::InvokeMenuItem(size_t menu_position) {
- // CHECK_LT(menu_position, );
- return kMenuActions[menu_position];
+ return g_menu_actions[menu_position].second;
}
int Device::HandleMenuKey(int key, bool visible) {
diff --git a/device.h b/device.h
index bf148f576..9c433715b 100644
--- a/device.h
+++ b/device.h
@@ -49,7 +49,7 @@ class Device {
RUN_LOCALE_TEST = 12,
};
- explicit Device(RecoveryUI* ui) : ui_(ui) {}
+ explicit Device(RecoveryUI* ui);
virtual ~Device() {}
// Returns a raw pointer to the RecoveryUI object.
@@ -96,6 +96,10 @@ class Device {
// here and return NO_ACTION.
virtual BuiltinAction InvokeMenuItem(size_t menu_position);
+ // Removes the menu item for the given action. This allows tailoring the menu based on the
+ // runtime info, such as the availability of /cache or /sdcard.
+ virtual void RemoveMenuItemForAction(Device::BuiltinAction action);
+
// Called before and after we do a wipe data/factory reset operation, either via a reboot from the
// main system with the --wipe_data flag, or when the user boots into recovery image manually and
// selects the option from the menu, to perform whatever device-specific wiping actions as needed.
diff --git a/minui/include/private/resources.h b/minui/include/private/resources.h
index 2a83a1028..047ebe2e3 100644
--- a/minui/include/private/resources.h
+++ b/minui/include/private/resources.h
@@ -82,3 +82,6 @@ class PngHandler {
// After initialization, we'll keep the file pointer open before destruction of PngHandler.
std::unique_ptr<FILE, decltype(&fclose)> png_fp_{ nullptr, fclose };
};
+
+// Overrides the default resource dir, for testing purpose.
+void res_set_resource_dir(const std::string&);
diff --git a/minui/resources.cpp b/minui/resources.cpp
index 9f67cf844..c018d9b8c 100644
--- a/minui/resources.cpp
+++ b/minui/resources.cpp
@@ -32,7 +32,6 @@
#include <string>
#include <vector>
-#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <png.h>
@@ -40,6 +39,8 @@
#define SURFACE_DATA_ALIGNMENT 8
+static std::string g_resource_dir{ "/res/images" };
+
static GRSurface* malloc_surface(size_t data_size) {
size_t size = sizeof(GRSurface) + data_size + SURFACE_DATA_ALIGNMENT;
unsigned char* temp = static_cast<unsigned char*>(malloc(size));
@@ -51,7 +52,7 @@ static GRSurface* malloc_surface(size_t data_size) {
}
PngHandler::PngHandler(const std::string& name) {
- std::string res_path = android::base::StringPrintf("/res/images/%s.png", name.c_str());
+ std::string res_path = g_resource_dir + "/" + name + ".png";
png_fp_.reset(fopen(res_path.c_str(), "rbe"));
// Try to read from |name| if the resource path does not work.
if (!png_fp_) {
@@ -340,6 +341,10 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface) {
return 0;
}
+void res_set_resource_dir(const std::string& dirname) {
+ g_resource_dir = dirname;
+}
+
// This function tests if a locale string stored in PNG (prefix) matches
// the locale string provided by the system (locale).
bool matches_locale(const std::string& prefix, const std::string& locale) {
diff --git a/otautil/include/otautil/paths.h b/otautil/include/otautil/paths.h
index 788c3de33..39088f100 100644
--- a/otautil/include/otautil/paths.h
+++ b/otautil/include/otautil/paths.h
@@ -48,6 +48,13 @@ class Paths {
last_command_file_ = last_command_file;
}
+ std::string resource_dir() const {
+ return resource_dir_;
+ }
+ void set_resource_dir(const std::string& resource_dir) {
+ resource_dir_ = resource_dir;
+ }
+
std::string stash_directory_base() const {
return stash_directory_base_;
}
@@ -85,6 +92,9 @@ class Paths {
// Path to the last command file.
std::string last_command_file_;
+ // Path to the resource dir;
+ std::string resource_dir_;
+
// Path to the base directory to write stashes during update.
std::string stash_directory_base_;
diff --git a/otautil/paths.cpp b/otautil/paths.cpp
index ad9ec1145..f08e51c7a 100644
--- a/otautil/paths.cpp
+++ b/otautil/paths.cpp
@@ -19,6 +19,7 @@
constexpr const char kDefaultCacheLogDirectory[] = "/cache/recovery";
constexpr const char kDefaultCacheTempSource[] = "/cache/saved.file";
constexpr const char kDefaultLastCommandFile[] = "/cache/recovery/last_command";
+constexpr const char kDefaultResourceDirectory[] = "/res/images";
constexpr const char kDefaultStashDirectoryBase[] = "/cache/recovery";
constexpr const char kDefaultTemporaryInstallFile[] = "/tmp/last_install";
constexpr const char kDefaultTemporaryLogFile[] = "/tmp/recovery.log";
@@ -32,6 +33,7 @@ Paths::Paths()
: cache_log_directory_(kDefaultCacheLogDirectory),
cache_temp_source_(kDefaultCacheTempSource),
last_command_file_(kDefaultLastCommandFile),
+ resource_dir_(kDefaultResourceDirectory),
stash_directory_base_(kDefaultStashDirectoryBase),
temporary_install_file_(kDefaultTemporaryInstallFile),
temporary_log_file_(kDefaultTemporaryLogFile) {}
diff --git a/recovery.cpp b/recovery.cpp
index 38784b0b3..f03cec3fe 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -1202,6 +1202,10 @@ int start_recovery(int argc, char** argv) {
}
ui = device->GetUI();
+ if (!has_cache) {
+ device->RemoveMenuItemForAction(Device::WIPE_CACHE);
+ }
+
// Set background string to "installing security update" for security update,
// otherwise set it to "installing system update".
ui->SetSystemUpdateText(security_update);
diff --git a/screen_ui.cpp b/screen_ui.cpp
index f5dadf7f0..fd7a1bea5 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -41,9 +41,10 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#include <minui/minui.h>
#include "device.h"
+#include "minui/minui.h"
+#include "otautil/paths.h"
#include "ui.h"
// Return the current time as a double (including fractions of a second).
@@ -756,7 +757,8 @@ std::string ScreenRecoveryUI::GetLocale() const {
}
void ScreenRecoveryUI::LoadAnimation() {
- std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/res/images"), closedir);
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(Paths::Get().resource_dir().c_str()),
+ closedir);
dirent* de;
std::vector<std::string> intro_frame_names;
std::vector<std::string> loop_frame_names;
@@ -1099,9 +1101,9 @@ void ScreenRecoveryUI::SetLocale(const std::string& new_locale) {
rtl_locale_ = false;
if (!new_locale.empty()) {
- size_t underscore = new_locale.find('_');
- // lang has the language prefix prior to '_', or full string if '_' doesn't exist.
- std::string lang = new_locale.substr(0, underscore);
+ size_t separator = new_locale.find('-');
+ // lang has the language prefix prior to the separator, or full string if none exists.
+ std::string lang = new_locale.substr(0, separator);
// A bit cheesy: keep an explicit list of supported RTL languages.
if (lang == "ar" || // Arabic
diff --git a/tests/Android.mk b/tests/Android.mk
index 538ae63e2..cdc5b523a 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -27,6 +27,7 @@ LOCAL_STATIC_LIBRARIES := \
libminui \
libotautil \
libupdater \
+ libpng \
libziparchive \
libutils \
libz \
@@ -93,6 +94,26 @@ LOCAL_SRC_FILES := \
LOCAL_SHARED_LIBRARIES := \
libhidlbase
+# libapplypatch, libapplypatch_modes, libimgdiff, libimgpatch.
+libapplypatch_static_libraries := \
+ libapplypatch_modes \
+ libapplypatch \
+ libedify \
+ libimgdiff \
+ libimgpatch \
+ libotafault \
+ libotautil \
+ libbsdiff \
+ libbspatch \
+ libdivsufsort \
+ libdivsufsort64 \
+ libutils \
+ libbase \
+ libbz \
+ libcrypto \
+ libz \
+ libziparchive \
+
tune2fs_static_libraries := \
libext2_com_err \
libext2_blkid \
@@ -101,50 +122,73 @@ tune2fs_static_libraries := \
libext2_e2p \
libext2fs
-LOCAL_STATIC_LIBRARIES := \
- libapplypatch_modes \
+libupdater_static_libraries := \
+ libupdater \
libapplypatch \
- libedify \
- libimgdiff \
- libimgpatch \
- libbsdiff \
libbspatch \
- libfusesideload \
- libminui \
+ libedify \
+ libziparchive \
+ libotautil \
+ libbootloader_message \
+ libutils \
libotafault \
+ libext4_utils \
+ libfec \
+ libfec_rs \
+ libfs_mgr \
+ liblog \
+ libselinux \
+ libsparse \
+ libsquashfs_utils \
+ libbz \
+ libz \
+ libbase \
+ libcrypto \
+ libcrypto_utils \
+ libcutils \
+ libtune2fs \
+ libbrotli \
+ $(tune2fs_static_libraries)
+
+librecovery_static_libraries := \
librecovery \
- libupdater \
+ $(TARGET_RECOVERY_UI_LIB) \
libbootloader_message \
+ libfusesideload \
+ libminadbd \
+ librecovery_ui \
+ libminui \
libverifier \
libotautil \
- libupdate_verifier \
- libdivsufsort \
- libdivsufsort64 \
+ libasyncio \
+ libbatterymonitor \
+ libcrypto_utils \
+ libcrypto \
+ libext4_utils \
libfs_mgr \
libpng \
+ libsparse \
libvintf_recovery \
libvintf \
libhidl-gen-utils \
libtinyxml2 \
- libselinux \
- libext4_utils \
- libsparse \
- libcrypto_utils \
- libcrypto \
- libbz \
libziparchive \
- liblog \
- libutils \
- libz \
libbase \
- libtune2fs \
- libfec \
- libfec_rs \
- libsquashfs_utils \
libcutils \
- libbrotli \
- libBionicGtestMain \
- $(tune2fs_static_libraries)
+ libutils \
+ liblog \
+ libselinux \
+ libz \
+
+libupdate_verifier_static_libraries := \
+ libupdate_verifier \
+
+LOCAL_STATIC_LIBRARIES := \
+ $(libapplypatch_static_libraries) \
+ $(librecovery_static_libraries) \
+ $(libupdate_verifier_static_libraries) \
+ $(libupdater_static_libraries) \
+ libBionicGtestMain
LOCAL_TEST_DATA := \
$(call find-test-data-in-subdirs, $(LOCAL_PATH), "*", testdata) \
diff --git a/tests/testdata/font.png b/tests/testdata/font.png
new file mode 100644
index 000000000..d95408a93
--- /dev/null
+++ b/tests/testdata/font.png
Binary files differ
diff --git a/tests/testdata/loop00000.png b/tests/testdata/loop00000.png
new file mode 100644
index 000000000..0e11c0100
--- /dev/null
+++ b/tests/testdata/loop00000.png
Binary files differ
diff --git a/tests/unit/screen_ui_test.cpp b/tests/unit/screen_ui_test.cpp
index e47d7054b..03e23ca42 100644
--- a/tests/unit/screen_ui_test.cpp
+++ b/tests/unit/screen_ui_test.cpp
@@ -16,11 +16,19 @@
#include <stddef.h>
+#include <functional>
+#include <map>
+#include <memory>
#include <string>
#include <vector>
+#include <android-base/logging.h>
#include <gtest/gtest.h>
+#include "common/test_constants.h"
+#include "device.h"
+#include "otautil/paths.h"
+#include "private/resources.h"
#include "screen_ui.h"
static const std::vector<std::string> HEADERS{ "header" };
@@ -185,3 +193,162 @@ TEST(ScreenUITest, WearMenuSelectItemsOverflow) {
ASSERT_EQ(0u, menu.MenuStart());
ASSERT_EQ(3u, menu.MenuEnd());
}
+
+static constexpr int kMagicAction = 101;
+
+enum class KeyCode : int {
+ TIMEOUT = -1,
+ NO_OP = 0,
+ UP = 1,
+ DOWN = 2,
+ ENTER = 3,
+ MAGIC = 1001,
+ LAST,
+};
+
+static const std::map<KeyCode, int> kKeyMapping{
+ // clang-format off
+ { KeyCode::NO_OP, Device::kNoAction },
+ { KeyCode::UP, Device::kHighlightUp },
+ { KeyCode::DOWN, Device::kHighlightDown },
+ { KeyCode::ENTER, Device::kInvokeItem },
+ { KeyCode::MAGIC, kMagicAction },
+ // clang-format on
+};
+
+class TestableScreenRecoveryUI : public ScreenRecoveryUI {
+ public:
+ int WaitKey() override;
+
+ void SetKeyBuffer(const std::vector<KeyCode>& buffer);
+
+ int KeyHandler(int key, bool visible) const;
+
+ bool GetRtlLocale() const {
+ return rtl_locale_;
+ }
+
+ private:
+ std::vector<KeyCode> key_buffer_;
+ size_t key_buffer_index_;
+};
+
+void TestableScreenRecoveryUI::SetKeyBuffer(const std::vector<KeyCode>& buffer) {
+ key_buffer_ = buffer;
+ key_buffer_index_ = 0;
+}
+
+int TestableScreenRecoveryUI::KeyHandler(int key, bool) const {
+ KeyCode key_code = static_cast<KeyCode>(key);
+ if (kKeyMapping.find(key_code) != kKeyMapping.end()) {
+ return kKeyMapping.at(key_code);
+ }
+ return Device::kNoAction;
+}
+
+int TestableScreenRecoveryUI::WaitKey() {
+ CHECK_LT(key_buffer_index_, key_buffer_.size());
+ return static_cast<int>(key_buffer_[key_buffer_index_++]);
+}
+
+class ScreenRecoveryUITest : public ::testing::Test {
+ protected:
+ const std::string kTestLocale = "en-US";
+ const std::string kTestRtlLocale = "ar";
+ const std::string kTestRtlLocaleWithSuffix = "ar-EG";
+
+ void SetUp() override {
+ ui_ = std::make_unique<TestableScreenRecoveryUI>();
+
+ std::string testdata_dir = from_testdata_base("");
+ Paths::Get().set_resource_dir(testdata_dir);
+ res_set_resource_dir(testdata_dir);
+
+ ASSERT_TRUE(ui_->Init(kTestLocale));
+ }
+
+ std::unique_ptr<TestableScreenRecoveryUI> ui_;
+};
+
+TEST_F(ScreenRecoveryUITest, Init) {
+ ASSERT_EQ(kTestLocale, ui_->GetLocale());
+ ASSERT_FALSE(ui_->GetRtlLocale());
+ ASSERT_FALSE(ui_->IsTextVisible());
+ ASSERT_FALSE(ui_->WasTextEverVisible());
+}
+
+TEST_F(ScreenRecoveryUITest, ShowText) {
+ ASSERT_FALSE(ui_->IsTextVisible());
+ ui_->ShowText(true);
+ ASSERT_TRUE(ui_->IsTextVisible());
+ ASSERT_TRUE(ui_->WasTextEverVisible());
+
+ ui_->ShowText(false);
+ ASSERT_FALSE(ui_->IsTextVisible());
+ ASSERT_TRUE(ui_->WasTextEverVisible());
+}
+
+TEST_F(ScreenRecoveryUITest, RtlLocale) {
+ ASSERT_TRUE(ui_->Init(kTestRtlLocale));
+ ASSERT_TRUE(ui_->GetRtlLocale());
+
+ ASSERT_TRUE(ui_->Init(kTestRtlLocaleWithSuffix));
+ ASSERT_TRUE(ui_->GetRtlLocale());
+}
+
+TEST_F(ScreenRecoveryUITest, ShowMenu) {
+ ui_->SetKeyBuffer({
+ KeyCode::UP,
+ KeyCode::DOWN,
+ KeyCode::UP,
+ KeyCode::DOWN,
+ KeyCode::ENTER,
+ });
+ ASSERT_EQ(3u, ui_->ShowMenu(HEADERS, ITEMS, 3, true,
+ std::bind(&TestableScreenRecoveryUI::KeyHandler, ui_.get(),
+ std::placeholders::_1, std::placeholders::_2)));
+
+ ui_->SetKeyBuffer({
+ KeyCode::UP,
+ KeyCode::UP,
+ KeyCode::NO_OP,
+ KeyCode::NO_OP,
+ KeyCode::UP,
+ KeyCode::ENTER,
+ });
+ ASSERT_EQ(2u, ui_->ShowMenu(HEADERS, ITEMS, 0, true,
+ std::bind(&TestableScreenRecoveryUI::KeyHandler, ui_.get(),
+ std::placeholders::_1, std::placeholders::_2)));
+}
+
+TEST_F(ScreenRecoveryUITest, ShowMenu_NotMenuOnly) {
+ ui_->SetKeyBuffer({
+ KeyCode::MAGIC,
+ });
+ ASSERT_EQ(static_cast<size_t>(kMagicAction),
+ ui_->ShowMenu(HEADERS, ITEMS, 3, false,
+ std::bind(&TestableScreenRecoveryUI::KeyHandler, ui_.get(),
+ std::placeholders::_1, std::placeholders::_2)));
+}
+
+TEST_F(ScreenRecoveryUITest, ShowMenu_TimedOut) {
+ ui_->SetKeyBuffer({
+ KeyCode::TIMEOUT,
+ });
+ ASSERT_EQ(static_cast<size_t>(-1), ui_->ShowMenu(HEADERS, ITEMS, 3, true, nullptr));
+}
+
+TEST_F(ScreenRecoveryUITest, ShowMenu_TimedOut_TextWasEverVisible) {
+ ui_->ShowText(true);
+ ui_->ShowText(false);
+ ASSERT_TRUE(ui_->WasTextEverVisible());
+
+ ui_->SetKeyBuffer({
+ KeyCode::TIMEOUT,
+ KeyCode::DOWN,
+ KeyCode::ENTER,
+ });
+ ASSERT_EQ(4u, ui_->ShowMenu(HEADERS, ITEMS, 3, true,
+ std::bind(&TestableScreenRecoveryUI::KeyHandler, ui_.get(),
+ std::placeholders::_1, std::placeholders::_2)));
+}