From 6cd816859e462e8741ced8dc24c00b7cd8304e2a Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 3 May 2018 21:53:11 -0700 Subject: tests: Add tests for ScreenRecoveryUI. In order to support that, this CL adds Paths::set_resource_dir() to override the default resource dir ("/res/images/") that's only available under recovery. Note that since there're external modules depending on libminui, it adds a separate function of res_set_resource_dir(), instead of requiring the dependency on libotautil for everyone. Test: mmma -j bootable/recovery Test: Run recovery_unit_test on marlin. Change-Id: I0a7dcf4476808bea9e634eaffc9676f6cbaf92b7 --- Android.mk | 16 ++-- minui/include/private/resources.h | 3 + minui/resources.cpp | 9 +- otautil/include/otautil/paths.h | 10 +++ otautil/paths.cpp | 2 + screen_ui.cpp | 6 +- tests/Android.mk | 1 + tests/testdata/font.png | Bin 0 -> 24437 bytes tests/testdata/loop00000.png | Bin 0 -> 5900 bytes tests/unit/screen_ui_test.cpp | 167 ++++++++++++++++++++++++++++++++++++++ 10 files changed, 202 insertions(+), 12 deletions(-) create mode 100644 tests/testdata/font.png create mode 100644 tests/testdata/loop00000.png diff --git a/Android.mk b/Android.mk index afbc950fe..e6bea0723 100644 --- a/Android.mk +++ b/Android.mk @@ -156,20 +156,20 @@ LOCAL_C_INCLUDES += \ LOCAL_STATIC_LIBRARIES := \ librecovery \ $(TARGET_RECOVERY_UI_LIB) \ + librecovery_ui \ + libminui \ libverifier \ - libbatterymonitor \ libbootloader_message \ + libfusesideload \ + libminadbd \ + libotautil \ + libasyncio \ + libbatterymonitor \ libfs_mgr \ libext4_utils \ + libpng \ libsparse \ libziparchive \ - libotautil \ - libminadbd \ - libasyncio \ - libfusesideload \ - librecovery_ui \ - libminui \ - libpng \ libcrypto_utils \ libcrypto \ libvintf_recovery \ 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 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 #include -#include #include #include @@ -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(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/screen_ui.cpp b/screen_ui.cpp index 90e0e30af..9198073df 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -41,9 +41,10 @@ #include #include #include -#include #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). @@ -754,7 +755,8 @@ std::string ScreenRecoveryUI::GetLocale() { } void ScreenRecoveryUI::LoadAnimation() { - std::unique_ptr dir(opendir("/res/images"), closedir); + std::unique_ptr dir(opendir(Paths::Get().resource_dir().c_str()), + closedir); dirent* de; std::vector intro_frame_names; std::vector loop_frame_names; diff --git a/tests/Android.mk b/tests/Android.mk index 538ae63e2..7234b521a 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -27,6 +27,7 @@ LOCAL_STATIC_LIBRARIES := \ libminui \ libotautil \ libupdater \ + libpng \ libziparchive \ libutils \ libz \ diff --git a/tests/testdata/font.png b/tests/testdata/font.png new file mode 100644 index 000000000..d95408a93 Binary files /dev/null and b/tests/testdata/font.png differ diff --git a/tests/testdata/loop00000.png b/tests/testdata/loop00000.png new file mode 100644 index 000000000..0e11c0100 Binary files /dev/null and b/tests/testdata/loop00000.png differ diff --git a/tests/unit/screen_ui_test.cpp b/tests/unit/screen_ui_test.cpp index e47d7054b..ff8a35d6c 100644 --- a/tests/unit/screen_ui_test.cpp +++ b/tests/unit/screen_ui_test.cpp @@ -16,11 +16,19 @@ #include +#include +#include +#include #include #include +#include #include +#include "common/test_constants.h" +#include "device.h" +#include "otautil/paths.h" +#include "private/resources.h" #include "screen_ui.h" static const std::vector 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 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& buffer); + + int KeyHandler(int key, bool visible) const; + + bool GetRtlLocale() const { + return rtl_locale_; + } + + private: + std::vector key_buffer_; + size_t key_buffer_index_; +}; + +void TestableScreenRecoveryUI::SetKeyBuffer(const std::vector& buffer) { + key_buffer_ = buffer; + key_buffer_index_ = 0; +} + +int TestableScreenRecoveryUI::KeyHandler(int key, bool) const { + KeyCode key_code = static_cast(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(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(); + + 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 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(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(-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))); +} -- cgit v1.2.3