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/pagination_model.h"
6
7#include <string>
8
9#include "base/basictypes.h"
10#include "base/compiler_specific.h"
11#include "base/message_loop/message_loop.h"
12#include "base/strings/stringprintf.h"
13#include "testing/gtest/include/gtest/gtest.h"
14#include "ui/app_list/pagination_model_observer.h"
15
16namespace app_list {
17namespace test {
18
19class TestPaginationModelObserver : public PaginationModelObserver {
20 public:
21  TestPaginationModelObserver()
22      : model_(NULL),
23        expected_page_selection_(0),
24        expected_transition_start_(0),
25        expected_transition_end_(0),
26        transition_page_(-1) {
27    Reset();
28  }
29  virtual ~TestPaginationModelObserver() {}
30
31  void Reset() {
32    selection_count_ = 0;
33    transition_start_count_ = 0;
34    transition_end_count_ = 0;
35    selected_pages_.clear();
36  }
37
38  void set_model(PaginationModel* model) { model_ = model; }
39
40  void set_expected_page_selection(int expected_page_selection) {
41    expected_page_selection_ = expected_page_selection;
42  }
43  void set_expected_transition_start(int expected_transition_start) {
44    expected_transition_start_ = expected_transition_start;
45  }
46  void set_expected_transition_end(int expected_transition_end) {
47    expected_transition_end_ = expected_transition_end;
48  }
49  void set_transition_page(int page) { transition_page_ = page; }
50
51  const std::string& selected_pages() const { return selected_pages_; }
52  int selection_count() const { return selection_count_; }
53  int transition_start_count() const { return transition_start_count_; }
54  int transition_end_count() const { return transition_end_count_; }
55
56 private:
57  void AppendSelectedPage(int page) {
58    if (selected_pages_.length())
59      selected_pages_.append(std::string(" "));
60    selected_pages_.append(base::StringPrintf("%d", page));
61  }
62
63  // PaginationModelObserver overrides:
64  virtual void TotalPagesChanged() OVERRIDE {}
65  virtual void SelectedPageChanged(int old_selected,
66                                   int new_selected) OVERRIDE {
67    AppendSelectedPage(new_selected);
68    ++selection_count_;
69    if (expected_page_selection_ &&
70        selection_count_ == expected_page_selection_) {
71      base::MessageLoop::current()->Quit();
72    }
73  }
74
75  virtual void TransitionStarted() OVERRIDE {
76  }
77
78  virtual void TransitionChanged() OVERRIDE {
79    if (transition_page_ == -1 ||
80        model_->transition().target_page == transition_page_) {
81      if (model_->transition().progress == 0)
82        ++transition_start_count_;
83      if (model_->transition().progress == 1)
84        ++transition_end_count_;
85    }
86
87    if ((expected_transition_start_ &&
88         transition_start_count_ == expected_transition_start_) ||
89        (expected_transition_end_ &&
90         transition_end_count_ == expected_transition_end_)) {
91      base::MessageLoop::current()->Quit();
92    }
93  }
94
95  PaginationModel* model_;
96
97  int expected_page_selection_;
98  int expected_transition_start_;
99  int expected_transition_end_;
100
101  int selection_count_;
102  int transition_start_count_;
103  int transition_end_count_;
104
105  // Indicate which page index should be counted for |transition_start_count_|
106  // and |transition_end_count_|. -1 means all the pages should be counted.
107  int transition_page_;
108
109  std::string selected_pages_;
110
111  DISALLOW_COPY_AND_ASSIGN(TestPaginationModelObserver);
112};
113
114class PaginationModelTest : public testing::Test {
115 public:
116  PaginationModelTest() {}
117  virtual ~PaginationModelTest() {}
118
119  // testing::Test overrides:
120  virtual void SetUp() OVERRIDE {
121    pagination_.SetTotalPages(5);
122    pagination_.SetTransitionDurations(1, 1);
123    observer_.set_model(&pagination_);
124    pagination_.AddObserver(&observer_);
125  }
126  virtual void TearDown() OVERRIDE {
127    pagination_.RemoveObserver(&observer_);
128    observer_.set_model(NULL);
129  }
130
131 protected:
132  void SetStartPageAndExpects(int start_page,
133                              int expected_selection,
134                              int expected_transition_start,
135                              int expected_transition_end) {
136    observer_.set_expected_page_selection(0);
137    pagination_.SelectPage(start_page, false /* animate */);
138    observer_.Reset();
139    observer_.set_expected_page_selection(expected_selection);
140    observer_.set_expected_transition_start(expected_transition_start);
141    observer_.set_expected_transition_end(expected_transition_end);
142  }
143
144  PaginationModel pagination_;
145  TestPaginationModelObserver observer_;
146
147 private:
148  base::MessageLoopForUI message_loop_;
149
150  DISALLOW_COPY_AND_ASSIGN(PaginationModelTest);
151};
152
153TEST_F(PaginationModelTest, SelectPage) {
154  pagination_.SelectPage(2, false /* animate */);
155  pagination_.SelectPage(4, false /* animate */);
156  pagination_.SelectPage(3, false /* animate */);
157  pagination_.SelectPage(1, false /* animate */);
158
159  EXPECT_EQ(0, observer_.transition_start_count());
160  EXPECT_EQ(0, observer_.transition_end_count());
161  EXPECT_EQ(4, observer_.selection_count());
162  EXPECT_EQ(std::string("2 4 3 1"), observer_.selected_pages());
163
164  // Nothing happens if select the same page.
165  pagination_.SelectPage(1, false /* animate */);
166  EXPECT_EQ(4, observer_.selection_count());
167  EXPECT_EQ(std::string("2 4 3 1"), observer_.selected_pages());
168}
169
170TEST_F(PaginationModelTest, SelectPageAnimated) {
171  const int kStartPage = 0;
172
173  // One transition.
174  SetStartPageAndExpects(kStartPage, 1, 0, 0);
175  pagination_.SelectPage(1, true /* animate */);
176  base::MessageLoop::current()->Run();
177  EXPECT_EQ(1, observer_.transition_start_count());
178  EXPECT_EQ(1, observer_.transition_end_count());
179  EXPECT_EQ(1, observer_.selection_count());
180  EXPECT_EQ(std::string("1"), observer_.selected_pages());
181
182  // Two transitions in a row.
183  SetStartPageAndExpects(kStartPage, 2, 0, 0);
184  pagination_.SelectPage(1, true /* animate */);
185  pagination_.SelectPage(3, true /* animate */);
186  base::MessageLoop::current()->Run();
187  EXPECT_EQ(2, observer_.transition_start_count());
188  EXPECT_EQ(2, observer_.transition_end_count());
189  EXPECT_EQ(2, observer_.selection_count());
190  EXPECT_EQ(std::string("1 3"), observer_.selected_pages());
191
192  // Transition to same page twice and only one should happen.
193  SetStartPageAndExpects(kStartPage, 1, 0, 0);
194  pagination_.SelectPage(1, true /* animate */);
195  pagination_.SelectPage(1, true /* animate */);  // Ignored.
196  base::MessageLoop::current()->Run();
197  EXPECT_EQ(1, observer_.transition_start_count());
198  EXPECT_EQ(1, observer_.transition_end_count());
199  EXPECT_EQ(1, observer_.selection_count());
200  EXPECT_EQ(std::string("1"), observer_.selected_pages());
201
202  // More than two transitions and only the first and last would happen.
203  SetStartPageAndExpects(kStartPage, 2, 0, 0);
204  pagination_.SelectPage(1, true /* animate */);
205  pagination_.SelectPage(3, true /* animate */);  // Ignored
206  pagination_.SelectPage(4, true /* animate */);  // Ignored
207  pagination_.SelectPage(2, true /* animate */);
208  base::MessageLoop::current()->Run();
209  EXPECT_EQ(2, observer_.transition_start_count());
210  EXPECT_EQ(2, observer_.transition_end_count());
211  EXPECT_EQ(2, observer_.selection_count());
212  EXPECT_EQ(std::string("1 2"), observer_.selected_pages());
213
214  // Multiple transitions with one transition that goes back to the original
215  // and followed by a new transition. Two transitions would happen. The first
216  // one will be reversed by the kStart transition and the second one will be
217  // finished.
218  SetStartPageAndExpects(kStartPage, 1, 0, 0);
219  pagination_.SelectPage(1, true /* animate */);
220  pagination_.SelectPage(2, true /* animate */);  // Ignored
221  pagination_.SelectPage(kStartPage, true /* animate */);
222  pagination_.SelectPage(3, true /* animate */);
223  base::MessageLoop::current()->Run();
224  EXPECT_EQ(std::string("3"), observer_.selected_pages());
225}
226
227TEST_F(PaginationModelTest, SimpleScroll) {
228  const int kStartPage = 2;
229
230  // Scroll to the next page (negative delta) and finish it.
231  SetStartPageAndExpects(kStartPage, 1, 0, 0);
232  pagination_.StartScroll();
233  pagination_.UpdateScroll(-0.1);
234  EXPECT_EQ(kStartPage + 1, pagination_.transition().target_page);
235  pagination_.EndScroll(false);  // Finish transition
236  base::MessageLoop::current()->Run();
237  EXPECT_EQ(1, observer_.selection_count());
238
239  // Scroll to the previous page (positive delta) and finish it.
240  SetStartPageAndExpects(kStartPage, 1, 0, 0);
241  pagination_.StartScroll();
242  pagination_.UpdateScroll(0.1);
243  EXPECT_EQ(kStartPage - 1, pagination_.transition().target_page);
244  pagination_.EndScroll(false);  // Finish transition
245  base::MessageLoop::current()->Run();
246  EXPECT_EQ(1, observer_.selection_count());
247
248  // Scroll to the next page (negative delta) and cancel it.
249  SetStartPageAndExpects(kStartPage, 0, 1, 0);
250  pagination_.StartScroll();
251  pagination_.UpdateScroll(-0.1);
252  EXPECT_EQ(kStartPage + 1, pagination_.transition().target_page);
253  pagination_.EndScroll(true);  // Cancel transition
254  base::MessageLoop::current()->Run();
255  EXPECT_EQ(0, observer_.selection_count());
256
257  // Scroll to the previous page (position delta) and cancel it.
258  SetStartPageAndExpects(kStartPage, 0, 1, 0);
259  pagination_.StartScroll();
260  pagination_.UpdateScroll(0.1);
261  EXPECT_EQ(kStartPage - 1, pagination_.transition().target_page);
262  pagination_.EndScroll(true);  // Cancel transition
263  base::MessageLoop::current()->Run();
264  EXPECT_EQ(0, observer_.selection_count());
265}
266
267TEST_F(PaginationModelTest, ScrollWithTransition) {
268  const int kStartPage = 2;
269
270  // Scroll to the next page (negative delta) with a transition in the same
271  // direction.
272  SetStartPageAndExpects(kStartPage, 1, 0, 0);
273  pagination_.SetTransition(PaginationModel::Transition(kStartPage + 1, 0.5));
274  pagination_.StartScroll();
275  pagination_.UpdateScroll(-0.1);
276  EXPECT_EQ(kStartPage + 1, pagination_.transition().target_page);
277  EXPECT_EQ(0.6, pagination_.transition().progress);
278  pagination_.EndScroll(false);
279  base::MessageLoop::current()->Run();
280  EXPECT_EQ(1, observer_.selection_count());
281
282  // Scroll to the next page (negative delta) with a transition in a different
283  // direction.
284  SetStartPageAndExpects(kStartPage, 0, 1, 0);
285  pagination_.SetTransition(PaginationModel::Transition(kStartPage - 1, 0.5));
286  pagination_.StartScroll();
287  pagination_.UpdateScroll(-0.1);
288  EXPECT_EQ(kStartPage - 1, pagination_.transition().target_page);
289  EXPECT_EQ(0.4, pagination_.transition().progress);
290  pagination_.EndScroll(true);
291
292  // Scroll to the previous page (positive delta) with a transition in the same
293  // direction.
294  SetStartPageAndExpects(kStartPage, 1, 0, 0);
295  pagination_.SetTransition(PaginationModel::Transition(kStartPage - 1, 0.5));
296  pagination_.StartScroll();
297  pagination_.UpdateScroll(0.1);
298  EXPECT_EQ(kStartPage - 1, pagination_.transition().target_page);
299  EXPECT_EQ(0.6, pagination_.transition().progress);
300  pagination_.EndScroll(false);
301  base::MessageLoop::current()->Run();
302  EXPECT_EQ(1, observer_.selection_count());
303
304  // Scroll to the previous page (positive delta) with a transition in a
305  // different direction.
306  SetStartPageAndExpects(kStartPage, 0, 1, 0);
307  pagination_.SetTransition(PaginationModel::Transition(kStartPage + 1, 0.5));
308  pagination_.StartScroll();
309  pagination_.UpdateScroll(0.1);
310  EXPECT_EQ(kStartPage + 1, pagination_.transition().target_page);
311  EXPECT_EQ(0.4, pagination_.transition().progress);
312  pagination_.EndScroll(true);
313}
314
315TEST_F(PaginationModelTest, LongScroll) {
316  const int kStartPage = 2;
317
318  // Scroll to the next page (negative delta) with a transition in the same
319  // direction. And scroll enough to change page twice.
320  SetStartPageAndExpects(kStartPage, 2, 0, 0);
321  pagination_.SetTransition(PaginationModel::Transition(kStartPage + 1, 0.5));
322  pagination_.StartScroll();
323  pagination_.UpdateScroll(-0.1);
324  EXPECT_EQ(kStartPage + 1, pagination_.transition().target_page);
325  EXPECT_EQ(0.6, pagination_.transition().progress);
326  pagination_.UpdateScroll(-0.5);
327  EXPECT_EQ(1, observer_.selection_count());
328  pagination_.UpdateScroll(-0.5);
329  EXPECT_EQ(kStartPage + 2, pagination_.transition().target_page);
330  pagination_.EndScroll(false);
331  base::MessageLoop::current()->Run();
332  EXPECT_EQ(2, observer_.selection_count());
333
334  // Scroll to the next page (negative delta) with a transition in a different
335  // direction. And scroll enough to revert it and switch page once.
336  SetStartPageAndExpects(kStartPage, 1, 0, 0);
337  pagination_.SetTransition(PaginationModel::Transition(kStartPage - 1, 0.5));
338  pagination_.StartScroll();
339  pagination_.UpdateScroll(-0.1);
340  EXPECT_EQ(kStartPage - 1, pagination_.transition().target_page);
341  EXPECT_EQ(0.4, pagination_.transition().progress);
342  pagination_.UpdateScroll(-0.5);  // This clears the transition.
343  pagination_.UpdateScroll(-0.5);  // This starts a new transition.
344  EXPECT_EQ(kStartPage + 1, pagination_.transition().target_page);
345  pagination_.EndScroll(false);
346  base::MessageLoop::current()->Run();
347  EXPECT_EQ(1, observer_.selection_count());
348
349  // Similar cases as above but in the opposite direction.
350  // Scroll to the previous page (positive delta) with a transition in the same
351  // direction. And scroll enough to change page twice.
352  SetStartPageAndExpects(kStartPage, 2, 0, 0);
353  pagination_.SetTransition(PaginationModel::Transition(kStartPage - 1, 0.5));
354  pagination_.StartScroll();
355  pagination_.UpdateScroll(0.1);
356  EXPECT_EQ(kStartPage - 1, pagination_.transition().target_page);
357  EXPECT_EQ(0.6, pagination_.transition().progress);
358  pagination_.UpdateScroll(0.5);
359  EXPECT_EQ(1, observer_.selection_count());
360  pagination_.UpdateScroll(0.5);
361  EXPECT_EQ(kStartPage - 2, pagination_.transition().target_page);
362  pagination_.EndScroll(false);
363  base::MessageLoop::current()->Run();
364  EXPECT_EQ(2, observer_.selection_count());
365
366  // Scroll to the previous page (positive delta) with a transition in a
367  // different direction. And scroll enough to revert it and switch page once.
368  SetStartPageAndExpects(kStartPage, 1, 0, 0);
369  pagination_.SetTransition(PaginationModel::Transition(kStartPage + 1, 0.5));
370  pagination_.StartScroll();
371  pagination_.UpdateScroll(0.1);
372  EXPECT_EQ(kStartPage + 1, pagination_.transition().target_page);
373  EXPECT_EQ(0.4, pagination_.transition().progress);
374  pagination_.UpdateScroll(0.5);  // This clears the transition.
375  pagination_.UpdateScroll(0.5);  // This starts a new transition.
376  EXPECT_EQ(kStartPage - 1, pagination_.transition().target_page);
377  pagination_.EndScroll(false);
378  base::MessageLoop::current()->Run();
379  EXPECT_EQ(1, observer_.selection_count());
380}
381
382TEST_F(PaginationModelTest, FireTransitionZero) {
383  const int kStartPage = 2;
384
385  // Scroll to next page then revert the scroll and make sure transition
386  // progress 0 is fired when previous scroll is cleared.
387  SetStartPageAndExpects(kStartPage, 0, 0, 0);
388  pagination_.StartScroll();
389  pagination_.UpdateScroll(-0.1);
390
391  int target_page = kStartPage + 1;
392  EXPECT_EQ(target_page, pagination_.transition().target_page);
393  observer_.set_transition_page(target_page);
394
395  pagination_.UpdateScroll(0.2);  // This clears the transition.
396  EXPECT_EQ(1, observer_.transition_start_count());
397  pagination_.EndScroll(true);  // Cancel transition.
398
399  // Similar to above but in the other direction.
400  SetStartPageAndExpects(kStartPage, 0, 0, 0);
401  pagination_.StartScroll();
402  pagination_.UpdateScroll(0.1);
403
404  target_page = kStartPage - 1;
405  EXPECT_EQ(target_page, pagination_.transition().target_page);
406  observer_.set_transition_page(target_page);
407
408  pagination_.UpdateScroll(-0.2);  // This clears the transition.
409  EXPECT_EQ(1, observer_.transition_start_count());
410  pagination_.EndScroll(true);  // Cancel transition.
411}
412
413TEST_F(PaginationModelTest, SelectedPageIsLost) {
414  pagination_.SetTotalPages(2);
415  // The selected page is set to 0 once the total number of pages are set.
416  EXPECT_EQ(0, pagination_.selected_page());
417
418  pagination_.SelectPage(1, false /* animate */);
419  EXPECT_EQ(1, pagination_.selected_page());
420
421  // The selected page is unchanged if it's still valid.
422  pagination_.SetTotalPages(3);
423  EXPECT_EQ(1, pagination_.selected_page());
424  pagination_.SetTotalPages(2);
425  EXPECT_EQ(1, pagination_.selected_page());
426
427  // But if the currently selected_page exceeds the total number of pages,
428  // it automatically switches to the last page.
429  pagination_.SetTotalPages(1);
430  EXPECT_EQ(0, pagination_.selected_page());
431}
432
433}  // namespace test
434}  // namespace app_list
435