blob: a3dd2add9a55d3bc17d0655e9bfe96499b437fb8 [file] [log] [blame]
Tianjie Xu5fe5eb62018-03-20 16:07:39 -07001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Tao Bao1fe1afe2018-05-01 15:56:05 -070017#include <stddef.h>
Tao Bao152e0eb2018-05-13 00:34:45 -070018#include <stdio.h>
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070019
Tao Bao6cd81682018-05-03 21:53:11 -070020#include <functional>
21#include <map>
22#include <memory>
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070023#include <string>
Tao Bao1fe1afe2018-05-01 15:56:05 -070024#include <vector>
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070025
Tao Bao6cd81682018-05-03 21:53:11 -070026#include <android-base/logging.h>
Tao Bao152e0eb2018-05-13 00:34:45 -070027#include <android-base/stringprintf.h>
28#include <android-base/test_utils.h>
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070029#include <gtest/gtest.h>
30
Tao Bao6cd81682018-05-03 21:53:11 -070031#include "common/test_constants.h"
32#include "device.h"
33#include "otautil/paths.h"
34#include "private/resources.h"
Tao Bao1fe1afe2018-05-01 15:56:05 -070035#include "screen_ui.h"
36
37static const std::vector<std::string> HEADERS{ "header" };
38static const std::vector<std::string> ITEMS{ "item1", "item2", "item3", "item4", "1234567890" };
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070039
40TEST(ScreenUITest, StartPhoneMenuSmoke) {
Tao Bao1fe1afe2018-05-01 15:56:05 -070041 Menu menu(false, 10, 20, HEADERS, ITEMS, 0);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070042 ASSERT_FALSE(menu.scrollable());
Tao Bao1fe1afe2018-05-01 15:56:05 -070043 ASSERT_EQ(HEADERS[0], menu.text_headers()[0]);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070044 ASSERT_EQ(5u, menu.ItemsCount());
45
46 std::string message;
47 ASSERT_FALSE(menu.ItemsOverflow(&message));
48 for (size_t i = 0; i < menu.ItemsCount(); i++) {
49 ASSERT_EQ(ITEMS[i], menu.TextItem(i));
50 }
51
52 ASSERT_EQ(0, menu.selection());
53}
54
55TEST(ScreenUITest, StartWearMenuSmoke) {
Tao Bao1fe1afe2018-05-01 15:56:05 -070056 Menu menu(true, 10, 8, HEADERS, ITEMS, 1);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070057 ASSERT_TRUE(menu.scrollable());
Tao Bao1fe1afe2018-05-01 15:56:05 -070058 ASSERT_EQ(HEADERS[0], menu.text_headers()[0]);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070059 ASSERT_EQ(5u, menu.ItemsCount());
60
61 std::string message;
62 ASSERT_FALSE(menu.ItemsOverflow(&message));
63 for (size_t i = 0; i < menu.ItemsCount() - 1; i++) {
64 ASSERT_EQ(ITEMS[i], menu.TextItem(i));
65 }
66 // Test of the last item is truncated
67 ASSERT_EQ("12345678", menu.TextItem(4));
68 ASSERT_EQ(1, menu.selection());
69}
70
71TEST(ScreenUITest, StartPhoneMenuItemsOverflow) {
Tao Bao1fe1afe2018-05-01 15:56:05 -070072 Menu menu(false, 1, 20, HEADERS, ITEMS, 0);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070073 ASSERT_FALSE(menu.scrollable());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070074 ASSERT_EQ(1u, menu.ItemsCount());
75
76 std::string message;
77 ASSERT_FALSE(menu.ItemsOverflow(&message));
78 for (size_t i = 0; i < menu.ItemsCount(); i++) {
79 ASSERT_EQ(ITEMS[i], menu.TextItem(i));
80 }
81
82 ASSERT_EQ(0u, menu.MenuStart());
83 ASSERT_EQ(1u, menu.MenuEnd());
84}
85
86TEST(ScreenUITest, StartWearMenuItemsOverflow) {
Tao Bao1fe1afe2018-05-01 15:56:05 -070087 Menu menu(true, 1, 20, HEADERS, ITEMS, 0);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070088 ASSERT_TRUE(menu.scrollable());
Tianjie Xu5fe5eb62018-03-20 16:07:39 -070089 ASSERT_EQ(5u, menu.ItemsCount());
90
91 std::string message;
92 ASSERT_TRUE(menu.ItemsOverflow(&message));
93 ASSERT_EQ("Current item: 1/5", message);
94
95 for (size_t i = 0; i < menu.ItemsCount(); i++) {
96 ASSERT_EQ(ITEMS[i], menu.TextItem(i));
97 }
98
99 ASSERT_EQ(0u, menu.MenuStart());
100 ASSERT_EQ(1u, menu.MenuEnd());
101}
102
103TEST(ScreenUITest, PhoneMenuSelectSmoke) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700104 int sel = 0;
Tao Bao1fe1afe2018-05-01 15:56:05 -0700105 Menu menu(false, 10, 20, HEADERS, ITEMS, sel);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700106 // Mimic down button 10 times (2 * items size)
107 for (int i = 0; i < 10; i++) {
108 sel = menu.Select(++sel);
109 ASSERT_EQ(sel, menu.selection());
110
111 // Wraps the selection for unscrollable menu when it reaches the boundary.
112 int expected = (i + 1) % 5;
113 ASSERT_EQ(expected, menu.selection());
114
115 ASSERT_EQ(0u, menu.MenuStart());
116 ASSERT_EQ(5u, menu.MenuEnd());
117 }
118
119 // Mimic up button 10 times
120 for (int i = 0; i < 10; i++) {
121 sel = menu.Select(--sel);
122 ASSERT_EQ(sel, menu.selection());
123
124 int expected = (9 - i) % 5;
125 ASSERT_EQ(expected, menu.selection());
126
127 ASSERT_EQ(0u, menu.MenuStart());
128 ASSERT_EQ(5u, menu.MenuEnd());
129 }
130}
131
132TEST(ScreenUITest, WearMenuSelectSmoke) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700133 int sel = 0;
Tao Bao1fe1afe2018-05-01 15:56:05 -0700134 Menu menu(true, 10, 20, HEADERS, ITEMS, sel);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700135 // Mimic pressing down button 10 times (2 * items size)
136 for (int i = 0; i < 10; i++) {
137 sel = menu.Select(++sel);
138 ASSERT_EQ(sel, menu.selection());
139
140 // Stops the selection at the boundary if the menu is scrollable.
141 int expected = std::min(i + 1, 4);
142 ASSERT_EQ(expected, menu.selection());
143
144 ASSERT_EQ(0u, menu.MenuStart());
145 ASSERT_EQ(5u, menu.MenuEnd());
146 }
147
148 // Mimic pressing up button 10 times
149 for (int i = 0; i < 10; i++) {
150 sel = menu.Select(--sel);
151 ASSERT_EQ(sel, menu.selection());
152
153 int expected = std::max(3 - i, 0);
154 ASSERT_EQ(expected, menu.selection());
155
156 ASSERT_EQ(0u, menu.MenuStart());
157 ASSERT_EQ(5u, menu.MenuEnd());
158 }
159}
160
161TEST(ScreenUITest, WearMenuSelectItemsOverflow) {
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700162 int sel = 1;
Tao Bao1fe1afe2018-05-01 15:56:05 -0700163 Menu menu(true, 3, 20, HEADERS, ITEMS, sel);
Tianjie Xu5fe5eb62018-03-20 16:07:39 -0700164 ASSERT_EQ(5u, menu.ItemsCount());
165
166 // Scroll the menu to the end, and check the start & end of menu.
167 for (int i = 0; i < 3; i++) {
168 sel = menu.Select(++sel);
169 ASSERT_EQ(i + 2, sel);
170 ASSERT_EQ(static_cast<size_t>(i), menu.MenuStart());
171 ASSERT_EQ(static_cast<size_t>(i + 3), menu.MenuEnd());
172 }
173
174 // Press down button one more time won't change the MenuStart() and MenuEnd().
175 sel = menu.Select(++sel);
176 ASSERT_EQ(4, sel);
177 ASSERT_EQ(2u, menu.MenuStart());
178 ASSERT_EQ(5u, menu.MenuEnd());
179
180 // Scroll the menu to the top.
181 // The expected menu sel, start & ends are:
182 // sel 3, start 2, end 5
183 // sel 2, start 2, end 5
184 // sel 1, start 1, end 4
185 // sel 0, start 0, end 3
186 for (int i = 0; i < 4; i++) {
187 sel = menu.Select(--sel);
188 ASSERT_EQ(3 - i, sel);
189 ASSERT_EQ(static_cast<size_t>(std::min(3 - i, 2)), menu.MenuStart());
190 ASSERT_EQ(static_cast<size_t>(std::min(6 - i, 5)), menu.MenuEnd());
191 }
192
193 // Press up button one more time won't change the MenuStart() and MenuEnd().
194 sel = menu.Select(--sel);
195 ASSERT_EQ(0, sel);
196 ASSERT_EQ(0u, menu.MenuStart());
197 ASSERT_EQ(3u, menu.MenuEnd());
198}
Tao Bao6cd81682018-05-03 21:53:11 -0700199
200static constexpr int kMagicAction = 101;
201
202enum class KeyCode : int {
203 TIMEOUT = -1,
204 NO_OP = 0,
205 UP = 1,
206 DOWN = 2,
207 ENTER = 3,
208 MAGIC = 1001,
209 LAST,
210};
211
212static const std::map<KeyCode, int> kKeyMapping{
213 // clang-format off
214 { KeyCode::NO_OP, Device::kNoAction },
215 { KeyCode::UP, Device::kHighlightUp },
216 { KeyCode::DOWN, Device::kHighlightDown },
217 { KeyCode::ENTER, Device::kInvokeItem },
218 { KeyCode::MAGIC, kMagicAction },
219 // clang-format on
220};
221
222class TestableScreenRecoveryUI : public ScreenRecoveryUI {
223 public:
224 int WaitKey() override;
225
226 void SetKeyBuffer(const std::vector<KeyCode>& buffer);
227
228 int KeyHandler(int key, bool visible) const;
229
Tao Bao152e0eb2018-05-13 00:34:45 -0700230 // The following functions expose the protected members for test purpose.
231 void RunLoadAnimation() {
232 LoadAnimation();
233 }
234
235 size_t GetLoopFrames() const {
236 return loop_frames;
237 }
238
239 size_t GetIntroFrames() const {
240 return intro_frames;
241 }
242
Tao Bao6cd81682018-05-03 21:53:11 -0700243 bool GetRtlLocale() const {
244 return rtl_locale_;
245 }
246
247 private:
248 std::vector<KeyCode> key_buffer_;
249 size_t key_buffer_index_;
250};
251
252void TestableScreenRecoveryUI::SetKeyBuffer(const std::vector<KeyCode>& buffer) {
253 key_buffer_ = buffer;
254 key_buffer_index_ = 0;
255}
256
257int TestableScreenRecoveryUI::KeyHandler(int key, bool) const {
258 KeyCode key_code = static_cast<KeyCode>(key);
259 if (kKeyMapping.find(key_code) != kKeyMapping.end()) {
260 return kKeyMapping.at(key_code);
261 }
262 return Device::kNoAction;
263}
264
265int TestableScreenRecoveryUI::WaitKey() {
266 CHECK_LT(key_buffer_index_, key_buffer_.size());
267 return static_cast<int>(key_buffer_[key_buffer_index_++]);
268}
269
270class ScreenRecoveryUITest : public ::testing::Test {
271 protected:
272 const std::string kTestLocale = "en-US";
273 const std::string kTestRtlLocale = "ar";
Tao Bao347a6592018-05-08 15:58:29 -0700274 const std::string kTestRtlLocaleWithSuffix = "ar-EG";
Tao Bao6cd81682018-05-03 21:53:11 -0700275
276 void SetUp() override {
277 ui_ = std::make_unique<TestableScreenRecoveryUI>();
278
Tao Bao152e0eb2018-05-13 00:34:45 -0700279 testdata_dir_ = from_testdata_base("");
280 Paths::Get().set_resource_dir(testdata_dir_);
281 res_set_resource_dir(testdata_dir_);
Tao Bao6cd81682018-05-03 21:53:11 -0700282 }
283
284 std::unique_ptr<TestableScreenRecoveryUI> ui_;
Tao Bao152e0eb2018-05-13 00:34:45 -0700285 std::string testdata_dir_;
Tao Bao6cd81682018-05-03 21:53:11 -0700286};
287
288TEST_F(ScreenRecoveryUITest, Init) {
Tao Bao26ea9592018-05-09 16:32:02 -0700289 ASSERT_TRUE(ui_->Init(kTestLocale));
Tao Bao6cd81682018-05-03 21:53:11 -0700290 ASSERT_EQ(kTestLocale, ui_->GetLocale());
291 ASSERT_FALSE(ui_->GetRtlLocale());
292 ASSERT_FALSE(ui_->IsTextVisible());
293 ASSERT_FALSE(ui_->WasTextEverVisible());
294}
295
Tao Bao94371fd2018-06-06 07:38:54 -0700296TEST_F(ScreenRecoveryUITest, dtor_NotCallingInit) {
297 ui_.reset();
298 ASSERT_FALSE(ui_);
299}
300
Tao Bao6cd81682018-05-03 21:53:11 -0700301TEST_F(ScreenRecoveryUITest, ShowText) {
Tao Bao26ea9592018-05-09 16:32:02 -0700302 ASSERT_TRUE(ui_->Init(kTestLocale));
Tao Bao6cd81682018-05-03 21:53:11 -0700303 ASSERT_FALSE(ui_->IsTextVisible());
304 ui_->ShowText(true);
305 ASSERT_TRUE(ui_->IsTextVisible());
306 ASSERT_TRUE(ui_->WasTextEverVisible());
307
308 ui_->ShowText(false);
309 ASSERT_FALSE(ui_->IsTextVisible());
310 ASSERT_TRUE(ui_->WasTextEverVisible());
311}
312
313TEST_F(ScreenRecoveryUITest, RtlLocale) {
314 ASSERT_TRUE(ui_->Init(kTestRtlLocale));
315 ASSERT_TRUE(ui_->GetRtlLocale());
Tao Bao26ea9592018-05-09 16:32:02 -0700316}
Tao Bao6cd81682018-05-03 21:53:11 -0700317
Tao Bao26ea9592018-05-09 16:32:02 -0700318TEST_F(ScreenRecoveryUITest, RtlLocaleWithSuffix) {
Tao Bao6cd81682018-05-03 21:53:11 -0700319 ASSERT_TRUE(ui_->Init(kTestRtlLocaleWithSuffix));
320 ASSERT_TRUE(ui_->GetRtlLocale());
321}
322
323TEST_F(ScreenRecoveryUITest, ShowMenu) {
Tao Bao26ea9592018-05-09 16:32:02 -0700324 ASSERT_TRUE(ui_->Init(kTestLocale));
Tao Bao6cd81682018-05-03 21:53:11 -0700325 ui_->SetKeyBuffer({
326 KeyCode::UP,
327 KeyCode::DOWN,
328 KeyCode::UP,
329 KeyCode::DOWN,
330 KeyCode::ENTER,
331 });
332 ASSERT_EQ(3u, ui_->ShowMenu(HEADERS, ITEMS, 3, true,
333 std::bind(&TestableScreenRecoveryUI::KeyHandler, ui_.get(),
334 std::placeholders::_1, std::placeholders::_2)));
335
336 ui_->SetKeyBuffer({
337 KeyCode::UP,
338 KeyCode::UP,
339 KeyCode::NO_OP,
340 KeyCode::NO_OP,
341 KeyCode::UP,
342 KeyCode::ENTER,
343 });
344 ASSERT_EQ(2u, ui_->ShowMenu(HEADERS, ITEMS, 0, true,
345 std::bind(&TestableScreenRecoveryUI::KeyHandler, ui_.get(),
346 std::placeholders::_1, std::placeholders::_2)));
347}
348
349TEST_F(ScreenRecoveryUITest, ShowMenu_NotMenuOnly) {
Tao Bao26ea9592018-05-09 16:32:02 -0700350 ASSERT_TRUE(ui_->Init(kTestLocale));
Tao Bao6cd81682018-05-03 21:53:11 -0700351 ui_->SetKeyBuffer({
352 KeyCode::MAGIC,
353 });
354 ASSERT_EQ(static_cast<size_t>(kMagicAction),
355 ui_->ShowMenu(HEADERS, ITEMS, 3, false,
356 std::bind(&TestableScreenRecoveryUI::KeyHandler, ui_.get(),
357 std::placeholders::_1, std::placeholders::_2)));
358}
359
360TEST_F(ScreenRecoveryUITest, ShowMenu_TimedOut) {
Tao Bao26ea9592018-05-09 16:32:02 -0700361 ASSERT_TRUE(ui_->Init(kTestLocale));
Tao Bao6cd81682018-05-03 21:53:11 -0700362 ui_->SetKeyBuffer({
363 KeyCode::TIMEOUT,
364 });
365 ASSERT_EQ(static_cast<size_t>(-1), ui_->ShowMenu(HEADERS, ITEMS, 3, true, nullptr));
366}
367
368TEST_F(ScreenRecoveryUITest, ShowMenu_TimedOut_TextWasEverVisible) {
Tao Bao26ea9592018-05-09 16:32:02 -0700369 ASSERT_TRUE(ui_->Init(kTestLocale));
Tao Bao6cd81682018-05-03 21:53:11 -0700370 ui_->ShowText(true);
371 ui_->ShowText(false);
372 ASSERT_TRUE(ui_->WasTextEverVisible());
373
374 ui_->SetKeyBuffer({
375 KeyCode::TIMEOUT,
376 KeyCode::DOWN,
377 KeyCode::ENTER,
378 });
379 ASSERT_EQ(4u, ui_->ShowMenu(HEADERS, ITEMS, 3, true,
380 std::bind(&TestableScreenRecoveryUI::KeyHandler, ui_.get(),
381 std::placeholders::_1, std::placeholders::_2)));
382}
Tao Bao152e0eb2018-05-13 00:34:45 -0700383
384TEST_F(ScreenRecoveryUITest, LoadAnimation) {
Tao Bao26ea9592018-05-09 16:32:02 -0700385 ASSERT_TRUE(ui_->Init(kTestLocale));
Tao Bao152e0eb2018-05-13 00:34:45 -0700386 // Make a few copies of loop00000.png from testdata.
387 std::string image_data;
388 ASSERT_TRUE(android::base::ReadFileToString(testdata_dir_ + "/loop00000.png", &image_data));
389
390 std::vector<std::string> tempfiles;
391 TemporaryDir resource_dir;
392 for (const auto& name : { "00002", "00100", "00050" }) {
393 tempfiles.push_back(android::base::StringPrintf("%s/loop%s.png", resource_dir.path, name));
394 ASSERT_TRUE(android::base::WriteStringToFile(image_data, tempfiles.back()));
395 }
396 for (const auto& name : { "00", "01" }) {
397 tempfiles.push_back(android::base::StringPrintf("%s/intro%s.png", resource_dir.path, name));
398 ASSERT_TRUE(android::base::WriteStringToFile(image_data, tempfiles.back()));
399 }
400 Paths::Get().set_resource_dir(resource_dir.path);
401
402 ui_->RunLoadAnimation();
403
404 ASSERT_EQ(2u, ui_->GetIntroFrames());
405 ASSERT_EQ(3u, ui_->GetLoopFrames());
406
407 for (const auto& name : tempfiles) {
408 ASSERT_EQ(0, unlink(name.c_str()));
409 }
410}
411
412TEST_F(ScreenRecoveryUITest, LoadAnimation_MissingAnimation) {
Tao Bao26ea9592018-05-09 16:32:02 -0700413 ASSERT_TRUE(ui_->Init(kTestLocale));
Tao Bao152e0eb2018-05-13 00:34:45 -0700414 TemporaryDir resource_dir;
415 Paths::Get().set_resource_dir(resource_dir.path);
Tao Bao42be0d42018-06-05 14:03:34 -0700416
417 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
Tao Bao152e0eb2018-05-13 00:34:45 -0700418 ASSERT_EXIT(ui_->RunLoadAnimation(), ::testing::KilledBySignal(SIGABRT), "");
419}