#include "ash/public/cpp/pagination/pagination_model.h"
#include <string>
#include "ash/public/cpp/pagination/pagination_model_observer.h"
#include "base/compiler_specific.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "ui/views/test/widget_test.h"
namespace ash {
namespace test {
class TestPaginationModelObserver : public PaginationModelObserver {
public:
TestPaginationModelObserver() = default;
TestPaginationModelObserver(const TestPaginationModelObserver&) = delete;
TestPaginationModelObserver& operator=(const TestPaginationModelObserver&) =
delete;
~TestPaginationModelObserver() override = default;
void Reset() {
selection_count_ = 0;
transition_start_count_ = 0;
transition_end_count_ = 0;
selected_pages_.clear();
transition_start_call_count_ = 0;
transition_ended_call_count_ = 0;
wait_loop_ = nullptr;
}
void set_model(PaginationModel* model) { model_ = model; }
void set_wait_loop(base::RunLoop* wait_loop) { wait_loop_ = wait_loop; }
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_; }
int transition_start_call_count() const {
return transition_start_call_count_;
}
int transition_end_call_count() const { return transition_ended_call_count_; }
private:
void AppendSelectedPage(int page) {
if (selected_pages_.length())
selected_pages_.append(std::string(" "));
selected_pages_.append(base::NumberToString(page));
}
void TotalPagesChanged(int previous_page_count, int new_page_count) override {
}
void SelectedPageChanged(int old_selected, int new_selected) override {
AppendSelectedPage(new_selected);
++selection_count_;
if (wait_loop_ && expected_page_selection_ &&
selection_count_ == expected_page_selection_) {
wait_loop_->Quit();
}
}
void TransitionStarted() override { ++transition_start_call_count_; }
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 (!wait_loop_)
return;
if ((expected_transition_start_ &&
transition_start_count_ == expected_transition_start_) ||
(expected_transition_end_ &&
transition_end_count_ == expected_transition_end_)) {
wait_loop_->Quit();
}
}
void TransitionEnded() override { ++transition_ended_call_count_; }
raw_ptr<PaginationModel, DanglingUntriaged> model_ = nullptr;
int expected_page_selection_ = 0;
int expected_transition_start_ = 0;
int expected_transition_end_ = 0;
int selection_count_ = 0;
int transition_start_count_ = 0;
int transition_end_count_ = 0;
int transition_page_ = -1;
std::string selected_pages_;
int transition_start_call_count_ = 0;
int transition_ended_call_count_ = 0;
raw_ptr<base::RunLoop, DanglingUntriaged> wait_loop_ = nullptr;
};
class PaginationModelTest : public views::test::WidgetTest {
public:
PaginationModelTest() = default;
PaginationModelTest(const PaginationModelTest&) = delete;
PaginationModelTest& operator=(const PaginationModelTest&) = delete;
~PaginationModelTest() override = default;
void SetUp() override {
views::test::WidgetTest::SetUp();
widget_.reset(CreateTopLevelPlatformWidget());
pagination_ = std::make_unique<PaginationModel>(widget_->GetContentsView());
pagination_->SetTotalPages(5);
pagination_->SetTransitionDurations(base::Milliseconds(1),
base::Milliseconds(1));
observer_.set_model(pagination_.get());
pagination_->AddObserver(&observer_);
}
void TearDown() override {
pagination_->RemoveObserver(&observer_);
observer_.set_model(NULL);
widget_.reset();
views::test::WidgetTest::TearDown();
}
protected:
void SetStartPageAndExpects(int start_page,
int expected_selection,
int expected_transition_start,
int expected_transition_end) {
observer_.set_wait_loop(nullptr);
pagination_->SelectPage(start_page, false);
observer_.Reset();
paging_animation_wait_loop_ = std::make_unique<base::RunLoop>();
observer_.set_wait_loop(paging_animation_wait_loop_.get());
observer_.set_expected_page_selection(expected_selection);
observer_.set_expected_transition_start(expected_transition_start);
observer_.set_expected_transition_end(expected_transition_end);
}
void WaitForPagingAnimation() {
ASSERT_TRUE(paging_animation_wait_loop_);
paging_animation_wait_loop_->Run();
}
void WaitForRevertAnimation() {
while (pagination()->IsRevertingCurrentTransition()) {
base::RunLoop run_loop;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(100));
run_loop.Run();
}
}
PaginationModel* pagination() { return pagination_.get(); }
TestPaginationModelObserver observer_;
private:
WidgetAutoclosePtr widget_;
std::unique_ptr<PaginationModel> pagination_;
std::unique_ptr<base::RunLoop> paging_animation_wait_loop_;
};
TEST_F(PaginationModelTest, SelectPage) {
pagination()->SelectPage(2, false);
pagination()->SelectPage(4, false);
pagination()->SelectPage(3, false);
pagination()->SelectPage(1, false);
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());
pagination()->SelectPage(1, false);
EXPECT_EQ(4, observer_.selection_count());
EXPECT_EQ(std::string("2 4 3 1"), observer_.selected_pages());
}
TEST_F(PaginationModelTest, IsValidPageRelative) {
pagination()->SelectPage(0, false );
ASSERT_FALSE(pagination()->IsValidPageRelative(-1));
ASSERT_FALSE(pagination()->IsValidPageRelative(5));
ASSERT_TRUE(pagination()->IsValidPageRelative(1));
ASSERT_TRUE(pagination()->IsValidPageRelative(4));
}
TEST_F(PaginationModelTest, SelectPageAnimated) {
const int kStartPage = 0;
SetStartPageAndExpects(kStartPage, 1, 0, 0);
pagination()->SelectPage(1, true);
WaitForPagingAnimation();
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());
SetStartPageAndExpects(kStartPage, 2, 0, 0);
pagination()->SelectPage(1, true);
pagination()->SelectPage(3, true);
WaitForPagingAnimation();
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());
SetStartPageAndExpects(kStartPage, 1, 0, 0);
pagination()->SelectPage(1, true);
pagination()->SelectPage(1, true);
WaitForPagingAnimation();
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());
SetStartPageAndExpects(kStartPage, 2, 0, 0);
pagination()->SelectPage(1, true);
pagination()->SelectPage(3, true);
pagination()->SelectPage(4, true);
pagination()->SelectPage(2, true);
WaitForPagingAnimation();
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());
SetStartPageAndExpects(kStartPage, 1, 0, 0);
pagination()->SelectPage(1, true);
pagination()->SelectPage(2, true);
pagination()->SelectPage(kStartPage, true);
pagination()->SelectPage(3, true);
WaitForPagingAnimation();
EXPECT_EQ(std::string("3"), observer_.selected_pages());
}
TEST_F(PaginationModelTest, SimpleScroll) {
const int kStartPage = 2;
SetStartPageAndExpects(kStartPage, 1, 0, 0);
pagination()->StartScroll();
pagination()->UpdateScroll(-0.1);
EXPECT_EQ(kStartPage + 1, pagination()->transition().target_page);
pagination()->EndScroll(false);
WaitForPagingAnimation();
EXPECT_EQ(1, observer_.selection_count());
SetStartPageAndExpects(kStartPage, 1, 0, 0);
pagination()->StartScroll();
pagination()->UpdateScroll(0.1);
EXPECT_EQ(kStartPage - 1, pagination()->transition().target_page);
pagination()->EndScroll(false);
WaitForPagingAnimation();
EXPECT_EQ(1, observer_.selection_count());
SetStartPageAndExpects(kStartPage, 0, 1, 0);
pagination()->StartScroll();
pagination()->UpdateScroll(-0.1);
EXPECT_EQ(kStartPage + 1, pagination()->transition().target_page);
pagination()->EndScroll(true);
WaitForPagingAnimation();
EXPECT_EQ(0, observer_.selection_count());
SetStartPageAndExpects(kStartPage, 0, 1, 0);
pagination()->StartScroll();
pagination()->UpdateScroll(0.1);
EXPECT_EQ(kStartPage - 1, pagination()->transition().target_page);
pagination()->EndScroll(true);
WaitForPagingAnimation();
EXPECT_EQ(0, observer_.selection_count());
}
TEST_F(PaginationModelTest, ScrollWithTransition) {
const int kStartPage = 2;
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);
WaitForPagingAnimation();
EXPECT_EQ(1, observer_.selection_count());
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);
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);
WaitForPagingAnimation();
EXPECT_EQ(1, observer_.selection_count());
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;
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);
WaitForPagingAnimation();
EXPECT_EQ(2, observer_.selection_count());
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);
pagination()->UpdateScroll(-0.5);
EXPECT_EQ(kStartPage + 1, pagination()->transition().target_page);
pagination()->EndScroll(false);
WaitForPagingAnimation();
EXPECT_EQ(1, observer_.selection_count());
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);
WaitForPagingAnimation();
EXPECT_EQ(2, observer_.selection_count());
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);
pagination()->UpdateScroll(0.5);
EXPECT_EQ(kStartPage - 1, pagination()->transition().target_page);
pagination()->EndScroll(false);
WaitForPagingAnimation();
EXPECT_EQ(1, observer_.selection_count());
}
TEST_F(PaginationModelTest, FireTransitionZero) {
const int kStartPage = 2;
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);
EXPECT_EQ(1, observer_.transition_start_count());
pagination()->EndScroll(true);
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);
EXPECT_EQ(1, observer_.transition_start_count());
pagination()->EndScroll(true);
}
TEST_F(PaginationModelTest, SelectedPageIsLost) {
pagination()->SetTotalPages(2);
EXPECT_EQ(0, pagination()->selected_page());
pagination()->SelectPage(1, false);
EXPECT_EQ(1, pagination()->selected_page());
pagination()->SetTotalPages(3);
EXPECT_EQ(1, pagination()->selected_page());
pagination()->SetTotalPages(2);
EXPECT_EQ(1, pagination()->selected_page());
pagination()->SetTotalPages(1);
EXPECT_EQ(0, pagination()->selected_page());
}
TEST_F(PaginationModelTest, SelectPageRelativeBeginning) {
pagination()->SelectPage(1, false);
pagination()->SelectPageRelative(-1, false);
EXPECT_EQ(0, pagination()->selected_page());
}
TEST_F(PaginationModelTest, SelectPageRelativeMiddle) {
pagination()->SelectPage(2, false);
pagination()->SelectPageRelative(-1, false);
EXPECT_EQ(1, pagination()->selected_page());
pagination()->SelectPageRelative(1, false);
EXPECT_EQ(2, pagination()->selected_page());
}
TEST_F(PaginationModelTest, NoTransitionEndForRevertingAnimation) {
SetStartPageAndExpects(0, 0, 0, 1);
pagination()->SelectPageRelative(-1, true);
WaitForPagingAnimation();
WaitForRevertAnimation();
EXPECT_EQ(1, observer_.transition_start_call_count());
EXPECT_EQ(1, observer_.transition_end_call_count());
SetStartPageAndExpects(pagination()->total_pages() - 1, 0, 0, 1);
pagination()->SelectPageRelative(1, true);
WaitForPagingAnimation();
WaitForRevertAnimation();
EXPECT_EQ(1, observer_.transition_start_call_count());
EXPECT_EQ(1, observer_.transition_end_call_count());
}
TEST_F(PaginationModelTest, CancelAnimationHasOneTransitionEnd) {
const int kStartPage = 2;
SetStartPageAndExpects(kStartPage, 0, 1, 0);
pagination()->StartScroll();
pagination()->UpdateScroll(-0.1);
EXPECT_EQ(kStartPage + 1, pagination()->transition().target_page);
pagination()->EndScroll(true);
WaitForPagingAnimation();
EXPECT_EQ(0, observer_.selection_count());
EXPECT_EQ(1, observer_.transition_start_call_count());
EXPECT_EQ(1, observer_.transition_end_call_count());
}
}
}