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
diff --git a/Android.mk b/Android.mk
index afbc950..e6bea07 100644
--- a/Android.mk
+++ b/Android.mk
@@ -156,20 +156,20 @@
LOCAL_STATIC_LIBRARIES := \
librecovery \
$(TARGET_RECOVERY_UI_LIB) \
- libverifier \
- libbatterymonitor \
- libbootloader_message \
- libfs_mgr \
- libext4_utils \
- libsparse \
- libziparchive \
- libotautil \
- libminadbd \
- libasyncio \
- libfusesideload \
librecovery_ui \
libminui \
+ libverifier \
+ libbootloader_message \
+ libfusesideload \
+ libminadbd \
+ libotautil \
+ libasyncio \
+ libbatterymonitor \
+ libfs_mgr \
+ libext4_utils \
libpng \
+ libsparse \
+ libziparchive \
libcrypto_utils \
libcrypto \
libvintf_recovery \
diff --git a/minui/include/private/resources.h b/minui/include/private/resources.h
index 2a83a10..047ebe2 100644
--- a/minui/include/private/resources.h
+++ b/minui/include/private/resources.h
@@ -82,3 +82,6 @@
// 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 9f67cf8..c018d9b 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 @@
}
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 @@
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 788c3de..39088f1 100644
--- a/otautil/include/otautil/paths.h
+++ b/otautil/include/otautil/paths.h
@@ -48,6 +48,13 @@
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 @@
// 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 ad9ec11..f08e51c 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 @@
: 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 90e0e30..9198073 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).
@@ -754,7 +755,8 @@
}
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;
diff --git a/tests/Android.mk b/tests/Android.mk
index 538ae63..7234b52 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -27,6 +27,7 @@
libminui \
libotautil \
libupdater \
+ libpng \
libziparchive \
libutils \
libz \
diff --git a/tests/testdata/font.png b/tests/testdata/font.png
new file mode 100644
index 0000000..d95408a
--- /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 0000000..0e11c01
--- /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 e47d705..ff8a35d 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 @@
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)));
+}