OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "ui/app_list/apps_grid_view.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/basictypes.h" | |
10 #include "base/compiler_specific.h" | |
11 #include "base/memory/scoped_ptr.h" | |
12 #include "base/message_loop.h" | |
13 #include "base/stringprintf.h" | |
14 #include "base/timer.h" | |
15 #include "testing/gtest/include/gtest/gtest.h" | |
16 #include "ui/app_list/app_list_item_model.h" | |
17 #include "ui/app_list/app_list_item_view.h" | |
18 #include "ui/app_list/app_list_model.h" | |
19 #include "ui/app_list/pagination_model.h" | |
20 #include "ui/app_list/test/apps_grid_view_test_api.h" | |
21 | |
22 namespace app_list { | |
23 namespace test { | |
24 | |
25 namespace { | |
26 | |
27 const int kIconDimension = 48; | |
28 const int kCols = 2; | |
29 const int kRows = 2; | |
30 const int kTilesPerPage = kCols * kRows; | |
31 | |
32 const int kWidth = 320; | |
33 const int kHeight = 240; | |
34 | |
35 class PageFlipWaiter : public PaginationModelObserver { | |
36 public: | |
37 PageFlipWaiter(MessageLoopForUI* ui_loop, | |
38 PaginationModel* model) | |
39 : ui_loop_(ui_loop), | |
40 model_(model), | |
41 wait_(false), | |
42 page_changed_(false) { | |
43 model_->AddObserver(this); | |
44 } | |
45 | |
46 virtual ~PageFlipWaiter() { | |
47 model_->RemoveObserver(this); | |
48 } | |
49 | |
50 bool Wait(int time_out_ms) { | |
51 DCHECK(!wait_); | |
52 wait_ = true; | |
53 page_changed_ = false; | |
54 wait_timer_.Stop(); | |
55 wait_timer_.Start(FROM_HERE, | |
56 base::TimeDelta::FromMilliseconds(time_out_ms), | |
57 this, &PageFlipWaiter::OnWaitTimeOut); | |
58 ui_loop_->Run(); | |
59 wait_ = false; | |
60 return page_changed_; | |
61 } | |
62 | |
63 private: | |
64 void OnWaitTimeOut() { | |
65 ui_loop_->Quit(); | |
66 } | |
67 | |
68 // PaginationModelObserver overrides: | |
69 virtual void TotalPagesChanged() OVERRIDE { | |
70 } | |
71 virtual void SelectedPageChanged(int old_selected, | |
72 int new_selected) OVERRIDE { | |
73 page_changed_ = true; | |
74 if (wait_) | |
75 ui_loop_->Quit(); | |
76 } | |
77 virtual void TransitionChanged() OVERRIDE { | |
78 } | |
79 | |
80 MessageLoopForUI* ui_loop_; | |
81 PaginationModel* model_; | |
82 bool wait_; | |
83 bool page_changed_; | |
84 base::OneShotTimer<PageFlipWaiter> wait_timer_; | |
85 | |
86 DISALLOW_COPY_AND_ASSIGN(PageFlipWaiter); | |
87 }; | |
88 | |
89 } // namespace | |
90 | |
91 class AppsGridViewTest : public testing::Test { | |
92 public: | |
93 AppsGridViewTest() {} | |
94 virtual ~AppsGridViewTest() {} | |
95 | |
96 // testing::Test overrides: | |
97 virtual void SetUp() OVERRIDE { | |
98 model_.reset(new AppListModel); | |
99 pagination_model_.reset(new PaginationModel); | |
100 | |
101 apps_grid_view_.reset(new AppsGridView(NULL, pagination_model_.get())); | |
102 apps_grid_view_->SetLayout(kIconDimension, kCols, kRows); | |
103 apps_grid_view_->SetBoundsRect(gfx::Rect(gfx::Size(kWidth, kHeight))); | |
104 apps_grid_view_->SetModel(model_.get()); | |
105 | |
106 test_api_.reset(new AppsGridViewTestApi(apps_grid_view_.get())); | |
107 } | |
108 virtual void TearDown() OVERRIDE { | |
109 apps_grid_view_.reset(); // Release apps grid view before models. | |
110 } | |
111 | |
112 protected: | |
113 void PopulateApps(int n) { | |
114 for (int i = 0; i < n; ++i) { | |
115 std::string title = base::StringPrintf("Item %d", i); | |
116 model_->apps()->Add(CreateItem(title)); | |
117 } | |
118 } | |
119 | |
120 // Get a string of all apps in |model| joined with ','. | |
121 std::string GetModelContent() { | |
122 std::string content; | |
123 for (size_t i = 0; i < model_->apps()->item_count(); ++i) { | |
124 if (i > 0) | |
125 content += ','; | |
126 content += model_->apps()->GetItemAt(i)->title(); | |
127 } | |
128 return content; | |
129 } | |
130 | |
131 AppListItemModel* CreateItem(const std::string& title) { | |
132 AppListItemModel* item = new AppListItemModel; | |
133 item->SetTitle(title); | |
134 return item; | |
135 } | |
136 | |
137 void HighlightItemAt(int index) { | |
138 AppListItemModel* item = model_->apps()->GetItemAt(index); | |
139 item->SetHighlighted(true); | |
140 } | |
141 | |
142 AppListItemView* GetItemViewAt(int index) { | |
143 return static_cast<AppListItemView*>( | |
144 test_api_->GetViewAtModelIndex(index)); | |
145 } | |
146 | |
147 AppListItemView* GetItemViewForPoint(const gfx::Point& point) { | |
148 for (size_t i = 0; i < model_->apps()->item_count(); ++i) { | |
149 AppListItemView* view = GetItemViewAt(i); | |
150 if (view->bounds().Contains(point)) | |
151 return view; | |
152 } | |
153 return NULL; | |
154 } | |
155 | |
156 gfx::Rect GetItemTileRectAt(int row, int col) { | |
157 DCHECK_GT(model_->apps()->item_count(), 0u); | |
158 | |
159 gfx::Insets insets(apps_grid_view_->GetInsets()); | |
160 gfx::Rect rect(gfx::Point(insets.left(), insets.top()), | |
161 GetItemViewAt(0)->bounds().size()); | |
162 rect.Offset(col * rect.width(), row * rect.height()); | |
163 return rect; | |
164 } | |
165 | |
166 // Points are in |apps_grid_view_|'s coordinates. | |
167 void SimulateDrag(AppsGridView::Pointer pointer, | |
168 const gfx::Point& from, | |
169 const gfx::Point& to) { | |
170 AppListItemView* view = GetItemViewForPoint(from); | |
171 DCHECK(view); | |
172 | |
173 gfx::Point translated_from = gfx::PointAtOffsetFromOrigin( | |
174 from - view->bounds().origin()); | |
175 gfx::Point translated_to = gfx::PointAtOffsetFromOrigin( | |
176 to - view->bounds().origin()); | |
177 | |
178 ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, | |
179 translated_from, from, 0); | |
180 apps_grid_view_->InitiateDrag(view, pointer, pressed_event); | |
181 | |
182 ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, | |
183 translated_to, to, 0); | |
184 apps_grid_view_->UpdateDrag(view, pointer, drag_event); | |
185 } | |
186 | |
187 void SimulateKeyPress(ui::KeyboardCode key_code) { | |
188 ui::KeyEvent key_event(ui::ET_KEY_PRESSED, key_code, 0, false); | |
189 apps_grid_view_->OnKeyPressed(key_event); | |
190 } | |
191 | |
192 scoped_ptr<AppListModel> model_; | |
193 scoped_ptr<PaginationModel> pagination_model_; | |
194 scoped_ptr<AppsGridView> apps_grid_view_; | |
195 scoped_ptr<AppsGridViewTestApi> test_api_; | |
196 | |
197 MessageLoopForUI message_loop_; | |
198 | |
199 private: | |
200 DISALLOW_COPY_AND_ASSIGN(AppsGridViewTest); | |
201 }; | |
202 | |
203 TEST_F(AppsGridViewTest, CreatePage) { | |
204 // Fully populates a page. | |
205 const int kPages = 1; | |
206 PopulateApps(kPages * kTilesPerPage); | |
207 EXPECT_EQ(kPages, pagination_model_->total_pages()); | |
208 | |
209 // Adds one more and gets a new page created. | |
210 model_->apps()->Add(CreateItem(std::string("Extra"))); | |
211 EXPECT_EQ(kPages + 1, pagination_model_->total_pages()); | |
212 } | |
213 | |
214 TEST_F(AppsGridViewTest, EnsureHighlightedVisible) { | |
215 const int kPages = 3; | |
216 PopulateApps(kPages * kTilesPerPage); | |
217 EXPECT_EQ(kPages, pagination_model_->total_pages()); | |
218 EXPECT_EQ(0, pagination_model_->selected_page()); | |
219 | |
220 // Highlight first one and last one one first page and first page should be | |
221 // selected. | |
222 HighlightItemAt(0); | |
223 EXPECT_EQ(0, pagination_model_->selected_page()); | |
224 HighlightItemAt(kTilesPerPage - 1); | |
225 EXPECT_EQ(0, pagination_model_->selected_page()); | |
226 | |
227 // Highlight first one on 2nd page and 2nd page should be selected. | |
228 HighlightItemAt(kTilesPerPage + 1); | |
229 EXPECT_EQ(1, pagination_model_->selected_page()); | |
230 | |
231 // Highlight last one in the model and last page should be selected. | |
232 HighlightItemAt(model_->apps()->item_count() - 1); | |
233 EXPECT_EQ(kPages - 1, pagination_model_->selected_page()); | |
234 } | |
235 | |
236 TEST_F(AppsGridViewTest, RemoveSelectedLastApp) { | |
237 const int kTotalItems = 2; | |
238 const int kLastItemIndex = kTotalItems - 1; | |
239 | |
240 PopulateApps(kTotalItems); | |
241 | |
242 AppListItemView* last_view = GetItemViewAt(kLastItemIndex); | |
243 apps_grid_view_->SetSelectedView(last_view); | |
244 model_->apps()->DeleteAt(kLastItemIndex); | |
245 | |
246 EXPECT_FALSE(apps_grid_view_->IsSelectedView(last_view)); | |
247 | |
248 // No crash happens. | |
249 AppListItemView* view = GetItemViewAt(0); | |
250 apps_grid_view_->SetSelectedView(view); | |
251 EXPECT_TRUE(apps_grid_view_->IsSelectedView(view)); | |
252 } | |
253 | |
254 TEST_F(AppsGridViewTest, MouseDrag) { | |
255 const int kTotalItems = 4; | |
256 PopulateApps(kTotalItems); | |
257 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"), | |
258 GetModelContent()); | |
259 | |
260 gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint(); | |
261 gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint(); | |
262 | |
263 // Dragging changes model order. | |
264 SimulateDrag(AppsGridView::MOUSE, from, to); | |
265 apps_grid_view_->EndDrag(false); | |
266 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"), | |
267 GetModelContent()); | |
268 test_api_->LayoutToIdealBounds(); | |
269 | |
270 // Canceling drag should keep existing order. | |
271 SimulateDrag(AppsGridView::MOUSE, from, to); | |
272 apps_grid_view_->EndDrag(true); | |
273 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"), | |
274 GetModelContent()); | |
275 test_api_->LayoutToIdealBounds(); | |
276 | |
277 // Deleting an item keeps remaining intact. | |
278 SimulateDrag(AppsGridView::MOUSE, from, to); | |
279 model_->apps()->DeleteAt(1); | |
280 apps_grid_view_->EndDrag(false); | |
281 EXPECT_EQ(std::string("Item 1,Item 2,Item 3"), | |
282 GetModelContent()); | |
283 test_api_->LayoutToIdealBounds(); | |
284 | |
285 // Adding a launcher item cancels the drag and respects the order. | |
286 SimulateDrag(AppsGridView::MOUSE, from, to); | |
287 model_->apps()->Add(CreateItem(std::string("Extra"))); | |
288 apps_grid_view_->EndDrag(false); | |
289 EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Extra"), | |
290 GetModelContent()); | |
291 test_api_->LayoutToIdealBounds(); | |
292 } | |
293 | |
294 TEST_F(AppsGridViewTest, MouseDragFlipPage) { | |
295 test_api_->SetPageFlipDelay(10); | |
296 pagination_model_->SetTransitionDurations(10, 10); | |
297 | |
298 PageFlipWaiter page_flip_waiter(&message_loop_, | |
299 pagination_model_.get()); | |
300 | |
301 const int kPages = 3; | |
302 PopulateApps(kPages * kTilesPerPage); | |
303 EXPECT_EQ(kPages, pagination_model_->total_pages()); | |
304 EXPECT_EQ(0, pagination_model_->selected_page()); | |
305 | |
306 gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint(); | |
307 gfx::Point to = gfx::Point(apps_grid_view_->width(), | |
308 apps_grid_view_->height() / 2); | |
309 | |
310 // Drag to right edge. | |
311 SimulateDrag(AppsGridView::MOUSE, from, to); | |
312 | |
313 // Page should be flipped after sometime. | |
314 EXPECT_TRUE(page_flip_waiter.Wait(100)); | |
315 EXPECT_EQ(1, pagination_model_->selected_page()); | |
316 | |
317 // Stay there and page should be flipped again. | |
318 EXPECT_TRUE(page_flip_waiter.Wait(100)); | |
319 EXPECT_EQ(2, pagination_model_->selected_page()); | |
320 | |
321 // Stay there longer and no page flip happen since we are at the last page. | |
322 EXPECT_FALSE(page_flip_waiter.Wait(100)); | |
323 EXPECT_EQ(2, pagination_model_->selected_page()); | |
324 | |
325 apps_grid_view_->EndDrag(true); | |
326 | |
327 // Now drag to the left edge and test the other direction. | |
328 to.set_x(0); | |
329 | |
330 SimulateDrag(AppsGridView::MOUSE, from, to); | |
331 | |
332 EXPECT_TRUE(page_flip_waiter.Wait(100)); | |
333 EXPECT_EQ(1, pagination_model_->selected_page()); | |
334 | |
335 EXPECT_TRUE(page_flip_waiter.Wait(100)); | |
336 EXPECT_EQ(0, pagination_model_->selected_page()); | |
337 | |
338 EXPECT_FALSE(page_flip_waiter.Wait(100)); | |
339 EXPECT_EQ(0, pagination_model_->selected_page()); | |
340 apps_grid_view_->EndDrag(true); | |
341 } | |
342 | |
343 TEST_F(AppsGridViewTest, SimultaneousDrag) { | |
344 const int kTotalItems = 4; | |
345 PopulateApps(kTotalItems); | |
346 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"), | |
347 GetModelContent()); | |
348 | |
349 gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint(); | |
350 gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint(); | |
351 | |
352 gfx::Point touch_from = GetItemTileRectAt(1, 0).CenterPoint(); | |
353 gfx::Point touch_to = GetItemTileRectAt(1, 1).CenterPoint(); | |
354 | |
355 // Starts a mouse drag first then a touch drag. | |
356 SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to); | |
357 SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to); | |
358 // Finishes the drag and mouse drag wins. | |
359 apps_grid_view_->EndDrag(false); | |
360 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"), | |
361 GetModelContent()); | |
362 test_api_->LayoutToIdealBounds(); | |
363 | |
364 // Starts a touch drag first then a mouse drag. | |
365 SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to); | |
366 SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to); | |
367 // Finishes the drag and touch drag wins. | |
368 apps_grid_view_->EndDrag(false); | |
369 EXPECT_EQ(std::string("Item 1,Item 0,Item 3,Item 2"), | |
370 GetModelContent()); | |
371 test_api_->LayoutToIdealBounds(); | |
372 } | |
373 | |
374 TEST_F(AppsGridViewTest, HighlightWithKeyboard) { | |
375 const int kTotalitems = 2; | |
376 PopulateApps(kTotalitems); | |
377 apps_grid_view_->SetSelectedView(GetItemViewAt(0)); | |
378 | |
379 // Try moving off the item to an invalid location. | |
380 SimulateKeyPress(ui::VKEY_UP); | |
381 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(0))); | |
382 SimulateKeyPress(ui::VKEY_DOWN); | |
383 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(0))); | |
384 SimulateKeyPress(ui::VKEY_LEFT); | |
385 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(0))); | |
386 | |
387 // Move to the second item and try to go past it. | |
388 SimulateKeyPress(ui::VKEY_RIGHT); | |
389 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(1))); | |
390 SimulateKeyPress(ui::VKEY_RIGHT); | |
391 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(1))); | |
392 } | |
393 | |
394 } // namespace test | |
395 } // namespace app_list | |
OLD | NEW |