blob: bbf63b18580cb55b7bd864fe5f416d6e33556af9 [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/payments/view_stack.h"
#include <memory>
#include "base/observer_list.h"
#include "base/run_loop.h"
#include "chrome/test/views/chrome_views_test_base.h"
#include "ui/gfx/animation/test_animation_delegate.h"
class TestStackView : public views::View {
public:
class Observer {
public:
Observer() = default;
void OnViewBeingDeleted() { view_deleted_ = true; }
bool view_deleted() { return view_deleted_; }
private:
bool view_deleted_ = false;
};
TestStackView() = default;
TestStackView(const TestStackView&) = delete;
TestStackView& operator=(const TestStackView&) = delete;
~TestStackView() override {
observers_.Notify(&Observer::OnViewBeingDeleted);
}
void AddObserver(Observer* observer) { observers_.AddObserver(observer); }
private:
base::ObserverList<Observer>::Unchecked observers_;
};
class ViewStackTest : public ChromeViewsTestBase {
public:
ViewStackTest() : view_stack_(std::make_unique<ViewStack>()) {
view_stack_->SetBounds(0, 0, 10, 10);
view_stack_->Push(std::make_unique<TestStackView>(), false);
view_stack_->slide_in_animator_->SetAnimationDuration(
base::Milliseconds(1));
view_stack_->slide_out_animator_->SetAnimationDuration(
base::Milliseconds(1));
}
ViewStackTest(const ViewStackTest&) = delete;
ViewStackTest& operator=(const ViewStackTest&) = delete;
void AssertViewOnTopOfStack(views::View* view) {
gfx::Rect target = view_stack_->bounds();
target.set_origin(gfx::Point(0, 0));
EXPECT_EQ(target, view->bounds());
}
void AssertViewCompletelyNextToStack(views::View* view) {
gfx::Rect target = view_stack_->bounds();
target.set_origin(gfx::Point(view_stack_->width(), 0));
EXPECT_EQ(target, view->bounds());
}
// Pushes a view on the stack, waits for its animation to be over, then
// returns a pointer to the pushed view.
views::View* PushViewOnStackAndWait() {
base::RunLoop loop;
std::unique_ptr<TestStackView> view = std::make_unique<TestStackView>();
views::View* view_ptr = view.get();
view_stack_->Push(std::move(view), true);
EXPECT_TRUE(view_stack_->slide_in_animator_->IsAnimating());
view_stack_->slide_in_animator_->SetAnimationDelegate(
view_ptr,
std::unique_ptr<gfx::AnimationDelegate>(
new gfx::TestAnimationDelegate(loop.QuitWhenIdleClosure())));
loop.Run();
EXPECT_FALSE(view_stack_->slide_in_animator_->IsAnimating());
return view_ptr;
}
// Pops |n| views from the stack, then waits for |top_view_ptr|'s animation to
// be over.
void PopManyAndWait(int n, views::View* top_view_ptr) {
base::RunLoop loop;
view_stack_->PopMany(n);
EXPECT_TRUE(view_stack_->slide_out_animator_->IsAnimating());
view_stack_->slide_out_animator_->SetAnimationDelegate(
top_view_ptr,
std::unique_ptr<gfx::AnimationDelegate>(
new gfx::TestAnimationDelegate(loop.QuitWhenIdleClosure())));
loop.Run();
EXPECT_FALSE(view_stack_->slide_out_animator_->IsAnimating());
}
std::unique_ptr<ViewStack> view_stack_;
};
TEST_F(ViewStackTest, TestInitialStateAddedAsChildView) {
EXPECT_EQ(1u, view_stack_->children().size());
// This child was added without any animation so it's on top of its parent
// already.
AssertViewOnTopOfStack(view_stack_->top());
}
TEST_F(ViewStackTest, TestPushStateAddsViewToChildren) {
view_stack_->Push(std::make_unique<TestStackView>(), true);
EXPECT_EQ(2u, view_stack_->children().size());
AssertViewCompletelyNextToStack(view_stack_->top());
}
TEST_F(ViewStackTest, TestPopStateRemovesChildViewAndCleansUpState) {
base::RunLoop loop1;
base::RunLoop loop2;
TestStackView::Observer observer;
std::unique_ptr<TestStackView> view = std::make_unique<TestStackView>();
view->AddObserver(&observer);
views::View* view_ptr = view.get();
view_stack_->Push(std::move(view), true);
EXPECT_TRUE(view_stack_->slide_in_animator_->IsAnimating());
view_stack_->slide_in_animator_->SetAnimationDelegate(
view_ptr,
std::unique_ptr<gfx::AnimationDelegate>(
new gfx::TestAnimationDelegate(loop1.QuitWhenIdleClosure())));
AssertViewCompletelyNextToStack(view_ptr);
loop1.Run();
AssertViewOnTopOfStack(view_ptr);
EXPECT_FALSE(view_stack_->slide_in_animator_->IsAnimating());
view_stack_->Pop();
EXPECT_TRUE(view_stack_->slide_out_animator_->IsAnimating());
view_stack_->slide_out_animator_->SetAnimationDelegate(
view_ptr,
std::unique_ptr<gfx::AnimationDelegate>(
new gfx::TestAnimationDelegate(loop2.QuitWhenIdleClosure())));
loop2.Run();
EXPECT_FALSE(view_stack_->slide_out_animator_->IsAnimating());
ASSERT_TRUE(observer.view_deleted());
}
TEST_F(ViewStackTest, TestDeletingViewCleansUpState) {
TestStackView::Observer observer;
base::RunLoop loop;
std::unique_ptr<TestStackView> view = std::make_unique<TestStackView>();
view->AddObserver(&observer);
views::View* view_ptr = view.get();
view_stack_->Push(std::move(view), true);
EXPECT_TRUE(view_stack_->slide_in_animator_->IsAnimating());
view_stack_->slide_in_animator_->SetAnimationDelegate(
view_ptr,
std::unique_ptr<gfx::AnimationDelegate>(
new gfx::TestAnimationDelegate(loop.QuitWhenIdleClosure())));
AssertViewCompletelyNextToStack(view_ptr);
loop.Run();
AssertViewOnTopOfStack(view_ptr);
EXPECT_FALSE(view_stack_->slide_in_animator_->IsAnimating());
view_stack_->Pop();
EXPECT_TRUE(view_stack_->slide_out_animator_->IsAnimating());
view_stack_.reset();
ASSERT_TRUE(observer.view_deleted());
}
TEST_F(ViewStackTest, TestLayoutUpdatesAnimations) {
TestStackView::Observer observer;
base::RunLoop loop1;
base::RunLoop loop2;
std::unique_ptr<TestStackView> view = std::make_unique<TestStackView>();
view->AddObserver(&observer);
views::View* view_ptr = view.get();
view_stack_->Push(std::move(view), true);
EXPECT_TRUE(view_stack_->slide_in_animator_->IsAnimating());
view_stack_->slide_in_animator_->SetAnimationDelegate(
view_ptr,
std::unique_ptr<gfx::AnimationDelegate>(
new gfx::TestAnimationDelegate(loop1.QuitWhenIdleClosure())));
view_stack_->SetBounds(10, 10, 30, 30);
loop1.Run();
AssertViewOnTopOfStack(view_ptr);
EXPECT_FALSE(view_stack_->slide_in_animator_->IsAnimating());
view_stack_->Pop();
EXPECT_TRUE(view_stack_->slide_out_animator_->IsAnimating());
view_stack_->slide_out_animator_->SetAnimationDelegate(
view_ptr,
std::unique_ptr<gfx::AnimationDelegate>(
new gfx::TestAnimationDelegate(loop2.QuitWhenIdleClosure())));
loop2.Run();
EXPECT_FALSE(view_stack_->slide_out_animator_->IsAnimating());
ASSERT_TRUE(observer.view_deleted());
}
TEST_F(ViewStackTest, TestPopMany) {
views::View* top = PushViewOnStackAndWait();
EXPECT_EQ(2U, view_stack_->GetSize());
PopManyAndWait(1, top);
EXPECT_EQ(1U, view_stack_->GetSize());
top = PushViewOnStackAndWait();
top = PushViewOnStackAndWait();
top = PushViewOnStackAndWait();
EXPECT_EQ(4U, view_stack_->GetSize());
PopManyAndWait(3, top);
EXPECT_EQ(1U, view_stack_->GetSize());
top = PushViewOnStackAndWait();
top = PushViewOnStackAndWait();
EXPECT_EQ(3U, view_stack_->GetSize());
}