blob: 873e434440f440bb6c7925d411f74853399572ad [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/app_list/pagination_model.h"
#include <string>
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_number_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/app_list/pagination_model_observer.h"
namespace app_list {
namespace test {
class TestPaginationModelObserver : public PaginationModelObserver {
public:
TestPaginationModelObserver()
: model_(NULL),
expected_page_selection_(0),
expected_transition_start_(0),
expected_transition_end_(0),
transition_page_(-1) {
Reset();
}
~TestPaginationModelObserver() override {}
void Reset() {
selection_count_ = 0;
transition_start_count_ = 0;
transition_end_count_ = 0;
selected_pages_.clear();
}
void set_model(PaginationModel* model) { model_ = model; }
void set_expected_page_selection(int expected_page_selection) {
expected_page_selection_ = expected_page_selection;
}
void set_expected_transition_start(int expected_transition_start) {
expected_transition_start_ = expected_transition_start;
}
void set_expected_transition_end(int expected_transition_end) {
expected_transition_end_ = expected_transition_end;
}
void set_transition_page(int page) { transition_page_ = page; }
const std::string& selected_pages() const { return selected_pages_; }
int selection_count() const { return selection_count_; }
int transition_start_count() const { return transition_start_count_; }
int transition_end_count() const { return transition_end_count_; }
private:
void AppendSelectedPage(int page) {
if (selected_pages_.length())
selected_pages_.append(std::string(" "));
selected_pages_.append(base::IntToString(page));
}
// PaginationModelObserver overrides:
void TotalPagesChanged() override {}
void SelectedPageChanged(int old_selected, int new_selected) override {
AppendSelectedPage(new_selected);
++selection_count_;
if (expected_page_selection_ &&
selection_count_ == expected_page_selection_) {
base::MessageLoop::current()->QuitWhenIdle();
}
}
void TransitionStarted() override {}
void TransitionChanged() override {
if (transition_page_ == -1 ||
model_->transition().target_page == transition_page_) {
if (model_->transition().progress == 0)
++transition_start_count_;
if (model_->transition().progress == 1)
++transition_end_count_;
}
if ((expected_transition_start_ &&
transition_start_count_ == expected_transition_start_) ||
(expected_transition_end_ &&
transition_end_count_ == expected_transition_end_)) {
base::MessageLoop::current()->QuitWhenIdle();
}
}
PaginationModel* model_;
int expected_page_selection_;
int expected_transition_start_;
int expected_transition_end_;
int selection_count_;
int transition_start_count_;
int transition_end_count_;
// Indicate which page index should be counted for |transition_start_count_|
// and |transition_end_count_|. -1 means all the pages should be counted.
int transition_page_;
std::string selected_pages_;
DISALLOW_COPY_AND_ASSIGN(TestPaginationModelObserver);
};
class PaginationModelTest : public testing::Test {
public:
PaginationModelTest() {}
~PaginationModelTest() override {}
// testing::Test overrides:
void SetUp() override {
pagination_.SetTotalPages(5);
pagination_.SetTransitionDurations(1, 1);
observer_.set_model(&pagination_);
pagination_.AddObserver(&observer_);
}
void TearDown() override {
pagination_.RemoveObserver(&observer_);
observer_.set_model(NULL);
}
protected:
void SetStartPageAndExpects(int start_page,
int expected_selection,
int expected_transition_start,
int expected_transition_end) {
observer_.set_expected_page_selection(0);
pagination_.SelectPage(start_page, false /* animate */);
observer_.Reset();
observer_.set_expected_page_selection(expected_selection);
observer_.set_expected_transition_start(expected_transition_start);
observer_.set_expected_transition_end(expected_transition_end);
}
PaginationModel pagination_;
TestPaginationModelObserver observer_;
private:
base::MessageLoopForUI message_loop_;
DISALLOW_COPY_AND_ASSIGN(PaginationModelTest);
};
TEST_F(PaginationModelTest, SelectPage) {
pagination_.SelectPage(2, false /* animate */);
pagination_.SelectPage(4, false /* animate */);
pagination_.SelectPage(3, false /* animate */);
pagination_.SelectPage(1, false /* animate */);
EXPECT_EQ(0, observer_.transition_start_count());
EXPECT_EQ(0, observer_.transition_end_count());
EXPECT_EQ(4, observer_.selection_count());
EXPECT_EQ(std::string("2 4 3 1"), observer_.selected_pages());
// Nothing happens if select the same page.
pagination_.SelectPage(1, false /* animate */);
EXPECT_EQ(4, observer_.selection_count());
EXPECT_EQ(std::string("2 4 3 1"), observer_.selected_pages());
}
TEST_F(PaginationModelTest, SelectPageAnimated) {
const int kStartPage = 0;
// One transition.
SetStartPageAndExpects(kStartPage, 1, 0, 0);
pagination_.SelectPage(1, true /* animate */);
base::MessageLoop::current()->Run();
EXPECT_EQ(1, observer_.transition_start_count());
EXPECT_EQ(1, observer_.transition_end_count());
EXPECT_EQ(1, observer_.selection_count());
EXPECT_EQ(std::string("1"), observer_.selected_pages());
// Two transitions in a row.
SetStartPageAndExpects(kStartPage, 2, 0, 0);
pagination_.SelectPage(1, true /* animate */);
pagination_.SelectPage(3, true /* animate */);
base::MessageLoop::current()->Run();
EXPECT_EQ(2, observer_.transition_start_count());
EXPECT_EQ(2, observer_.transition_end_count());
EXPECT_EQ(2, observer_.selection_count());
EXPECT_EQ(std::string("1 3"), observer_.selected_pages());
// Transition to same page twice and only one should happen.
SetStartPageAndExpects(kStartPage, 1, 0, 0);
pagination_.SelectPage(1, true /* animate */);
pagination_.SelectPage(1, true /* animate */); // Ignored.
base::MessageLoop::current()->Run();
EXPECT_EQ(1, observer_.transition_start_count());
EXPECT_EQ(1, observer_.transition_end_count());
EXPECT_EQ(1, observer_.selection_count());
EXPECT_EQ(std::string("1"), observer_.selected_pages());
// More than two transitions and only the first and last would happen.
SetStartPageAndExpects(kStartPage, 2, 0, 0);
pagination_.SelectPage(1, true /* animate */);
pagination_.SelectPage(3, true /* animate */); // Ignored
pagination_.SelectPage(4, true /* animate */); // Ignored
pagination_.SelectPage(2, true /* animate */);
base::MessageLoop::current()->Run();
EXPECT_EQ(2, observer_.transition_start_count());
EXPECT_EQ(2, observer_.transition_end_count());
EXPECT_EQ(2, observer_.selection_count());
EXPECT_EQ(std::string("1 2"), observer_.selected_pages());
// Multiple transitions with one transition that goes back to the original
// and followed by a new transition. Two transitions would happen. The first
// one will be reversed by the kStart transition and the second one will be
// finished.
SetStartPageAndExpects(kStartPage, 1, 0, 0);
pagination_.SelectPage(1, true /* animate */);
pagination_.SelectPage(2, true /* animate */); // Ignored
pagination_.SelectPage(kStartPage, true /* animate */);
pagination_.SelectPage(3, true /* animate */);
base::MessageLoop::current()->Run();
EXPECT_EQ(std::string("3"), observer_.selected_pages());
}
TEST_F(PaginationModelTest, SimpleScroll) {
const int kStartPage = 2;
// Scroll to the next page (negative delta) and finish it.
SetStartPageAndExpects(kStartPage, 1, 0, 0);
pagination_.StartScroll();
pagination_.UpdateScroll(-0.1);
EXPECT_EQ(kStartPage + 1, pagination_.transition().target_page);
pagination_.EndScroll(false); // Finish transition
base::MessageLoop::current()->Run();
EXPECT_EQ(1, observer_.selection_count());
// Scroll to the previous page (positive delta) and finish it.
SetStartPageAndExpects(kStartPage, 1, 0, 0);
pagination_.StartScroll();
pagination_.UpdateScroll(0.1);
EXPECT_EQ(kStartPage - 1, pagination_.transition().target_page);
pagination_.EndScroll(false); // Finish transition
base::MessageLoop::current()->Run();
EXPECT_EQ(1, observer_.selection_count());
// Scroll to the next page (negative delta) and cancel it.
SetStartPageAndExpects(kStartPage, 0, 1, 0);
pagination_.StartScroll();
pagination_.UpdateScroll(-0.1);
EXPECT_EQ(kStartPage + 1, pagination_.transition().target_page);
pagination_.EndScroll(true); // Cancel transition
base::MessageLoop::current()->Run();
EXPECT_EQ(0, observer_.selection_count());
// Scroll to the previous page (position delta) and cancel it.
SetStartPageAndExpects(kStartPage, 0, 1, 0);
pagination_.StartScroll();
pagination_.UpdateScroll(0.1);
EXPECT_EQ(kStartPage - 1, pagination_.transition().target_page);
pagination_.EndScroll(true); // Cancel transition
base::MessageLoop::current()->Run();
EXPECT_EQ(0, observer_.selection_count());
}
TEST_F(PaginationModelTest, ScrollWithTransition) {
const int kStartPage = 2;
// Scroll to the next page (negative delta) with a transition in the same
// direction.
SetStartPageAndExpects(kStartPage, 1, 0, 0);
pagination_.SetTransition(PaginationModel::Transition(kStartPage + 1, 0.5));
pagination_.StartScroll();
pagination_.UpdateScroll(-0.1);
EXPECT_EQ(kStartPage + 1, pagination_.transition().target_page);
EXPECT_EQ(0.6, pagination_.transition().progress);
pagination_.EndScroll(false);
base::MessageLoop::current()->Run();
EXPECT_EQ(1, observer_.selection_count());
// Scroll to the next page (negative delta) with a transition in a different
// direction.
SetStartPageAndExpects(kStartPage, 0, 1, 0);
pagination_.SetTransition(PaginationModel::Transition(kStartPage - 1, 0.5));
pagination_.StartScroll();
pagination_.UpdateScroll(-0.1);
EXPECT_EQ(kStartPage - 1, pagination_.transition().target_page);
EXPECT_EQ(0.4, pagination_.transition().progress);
pagination_.EndScroll(true);
// Scroll to the previous page (positive delta) with a transition in the same
// direction.
SetStartPageAndExpects(kStartPage, 1, 0, 0);
pagination_.SetTransition(PaginationModel::Transition(kStartPage - 1, 0.5));
pagination_.StartScroll();
pagination_.UpdateScroll(0.1);
EXPECT_EQ(kStartPage - 1, pagination_.transition().target_page);
EXPECT_EQ(0.6, pagination_.transition().progress);
pagination_.EndScroll(false);
base::MessageLoop::current()->Run();
EXPECT_EQ(1, observer_.selection_count());
// Scroll to the previous page (positive delta) with a transition in a
// different direction.
SetStartPageAndExpects(kStartPage, 0, 1, 0);
pagination_.SetTransition(PaginationModel::Transition(kStartPage + 1, 0.5));
pagination_.StartScroll();
pagination_.UpdateScroll(0.1);
EXPECT_EQ(kStartPage + 1, pagination_.transition().target_page);
EXPECT_EQ(0.4, pagination_.transition().progress);
pagination_.EndScroll(true);
}
TEST_F(PaginationModelTest, LongScroll) {
const int kStartPage = 2;
// Scroll to the next page (negative delta) with a transition in the same
// direction. And scroll enough to change page twice.
SetStartPageAndExpects(kStartPage, 2, 0, 0);
pagination_.SetTransition(PaginationModel::Transition(kStartPage + 1, 0.5));
pagination_.StartScroll();
pagination_.UpdateScroll(-0.1);
EXPECT_EQ(kStartPage + 1, pagination_.transition().target_page);
EXPECT_EQ(0.6, pagination_.transition().progress);
pagination_.UpdateScroll(-0.5);
EXPECT_EQ(1, observer_.selection_count());
pagination_.UpdateScroll(-0.5);
EXPECT_EQ(kStartPage + 2, pagination_.transition().target_page);
pagination_.EndScroll(false);
base::MessageLoop::current()->Run();
EXPECT_EQ(2, observer_.selection_count());
// Scroll to the next page (negative delta) with a transition in a different
// direction. And scroll enough to revert it and switch page once.
SetStartPageAndExpects(kStartPage, 1, 0, 0);
pagination_.SetTransition(PaginationModel::Transition(kStartPage - 1, 0.5));
pagination_.StartScroll();
pagination_.UpdateScroll(-0.1);
EXPECT_EQ(kStartPage - 1, pagination_.transition().target_page);
EXPECT_EQ(0.4, pagination_.transition().progress);
pagination_.UpdateScroll(-0.5); // This clears the transition.
pagination_.UpdateScroll(-0.5); // This starts a new transition.
EXPECT_EQ(kStartPage + 1, pagination_.transition().target_page);
pagination_.EndScroll(false);
base::MessageLoop::current()->Run();
EXPECT_EQ(1, observer_.selection_count());
// Similar cases as above but in the opposite direction.
// Scroll to the previous page (positive delta) with a transition in the same
// direction. And scroll enough to change page twice.
SetStartPageAndExpects(kStartPage, 2, 0, 0);
pagination_.SetTransition(PaginationModel::Transition(kStartPage - 1, 0.5));
pagination_.StartScroll();
pagination_.UpdateScroll(0.1);
EXPECT_EQ(kStartPage - 1, pagination_.transition().target_page);
EXPECT_EQ(0.6, pagination_.transition().progress);
pagination_.UpdateScroll(0.5);
EXPECT_EQ(1, observer_.selection_count());
pagination_.UpdateScroll(0.5);
EXPECT_EQ(kStartPage - 2, pagination_.transition().target_page);
pagination_.EndScroll(false);
base::MessageLoop::current()->Run();
EXPECT_EQ(2, observer_.selection_count());
// Scroll to the previous page (positive delta) with a transition in a
// different direction. And scroll enough to revert it and switch page once.
SetStartPageAndExpects(kStartPage, 1, 0, 0);
pagination_.SetTransition(PaginationModel::Transition(kStartPage + 1, 0.5));
pagination_.StartScroll();
pagination_.UpdateScroll(0.1);
EXPECT_EQ(kStartPage + 1, pagination_.transition().target_page);
EXPECT_EQ(0.4, pagination_.transition().progress);
pagination_.UpdateScroll(0.5); // This clears the transition.
pagination_.UpdateScroll(0.5); // This starts a new transition.
EXPECT_EQ(kStartPage - 1, pagination_.transition().target_page);
pagination_.EndScroll(false);
base::MessageLoop::current()->Run();
EXPECT_EQ(1, observer_.selection_count());
}
TEST_F(PaginationModelTest, FireTransitionZero) {
const int kStartPage = 2;
// Scroll to next page then revert the scroll and make sure transition
// progress 0 is fired when previous scroll is cleared.
SetStartPageAndExpects(kStartPage, 0, 0, 0);
pagination_.StartScroll();
pagination_.UpdateScroll(-0.1);
int target_page = kStartPage + 1;
EXPECT_EQ(target_page, pagination_.transition().target_page);
observer_.set_transition_page(target_page);
pagination_.UpdateScroll(0.2); // This clears the transition.
EXPECT_EQ(1, observer_.transition_start_count());
pagination_.EndScroll(true); // Cancel transition.
// Similar to above but in the other direction.
SetStartPageAndExpects(kStartPage, 0, 0, 0);
pagination_.StartScroll();
pagination_.UpdateScroll(0.1);
target_page = kStartPage - 1;
EXPECT_EQ(target_page, pagination_.transition().target_page);
observer_.set_transition_page(target_page);
pagination_.UpdateScroll(-0.2); // This clears the transition.
EXPECT_EQ(1, observer_.transition_start_count());
pagination_.EndScroll(true); // Cancel transition.
}
TEST_F(PaginationModelTest, SelectedPageIsLost) {
pagination_.SetTotalPages(2);
// The selected page is set to 0 once the total number of pages are set.
EXPECT_EQ(0, pagination_.selected_page());
pagination_.SelectPage(1, false /* animate */);
EXPECT_EQ(1, pagination_.selected_page());
// The selected page is unchanged if it's still valid.
pagination_.SetTotalPages(3);
EXPECT_EQ(1, pagination_.selected_page());
pagination_.SetTotalPages(2);
EXPECT_EQ(1, pagination_.selected_page());
// But if the currently selected_page exceeds the total number of pages,
// it automatically switches to the last page.
pagination_.SetTotalPages(1);
EXPECT_EQ(0, pagination_.selected_page());
}
} // namespace test
} // namespace app_list