// Copyright 2014 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 "cc/layers/layer.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/android/overscroll_refresh.h" namespace ui { class OverscrollRefreshTest : public OverscrollRefreshHandler, public testing::Test { public: OverscrollRefreshTest() {} // OverscrollRefreshHandler implementation. bool PullStart() override { started_ = true; return true; } void PullUpdate(float delta) override { delta_ += delta; } void PullRelease(bool allow_refresh) override { released_ = true; refresh_allowed_ = allow_refresh; } void PullReset() override { reset_ = true; } bool GetAndResetPullStarted() { bool result = started_; started_ = false; return result; } float GetAndResetPullDelta() { float result = delta_; delta_ = 0; return result; } bool GetAndResetPullReleased() { bool result = released_; released_ = false; return result; } bool GetAndResetRefreshAllowed() { bool result = refresh_allowed_; refresh_allowed_ = false; return result; } bool GetAndResetPullReset() { bool result = reset_; reset_ = false; return result; } private: float delta_ = 0; bool started_ = false; bool released_ = false; bool reset_ = false; bool refresh_allowed_ = false; }; TEST_F(OverscrollRefreshTest, Basic) { OverscrollRefresh effect(this); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); effect.OnScrollBegin(); EXPECT_FALSE(effect.IsActive()); EXPECT_TRUE(effect.IsAwaitingScrollUpdateAck()); // The initial scroll should not be consumed, as it should first be offered // to content. gfx::Vector2dF scroll_up(0, 10); EXPECT_FALSE(effect.WillHandleScrollUpdate(scroll_up)); EXPECT_FALSE(effect.IsActive()); EXPECT_TRUE(effect.IsAwaitingScrollUpdateAck()); // The unconsumed, overscrolling scroll will trigger the effect. effect.OnScrollUpdateAck(false); EXPECT_TRUE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); EXPECT_TRUE(GetAndResetPullStarted()); // Further scrolls will be consumed. EXPECT_TRUE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 50))); EXPECT_EQ(50.f, GetAndResetPullDelta()); EXPECT_TRUE(effect.IsActive()); // Even scrolls in the down direction should be consumed. EXPECT_TRUE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, -50))); EXPECT_EQ(-50.f, GetAndResetPullDelta()); EXPECT_TRUE(effect.IsActive()); // Ending the scroll while beyond the threshold should trigger a refresh. gfx::Vector2dF zero_velocity; EXPECT_FALSE(GetAndResetPullReleased()); effect.OnScrollEnd(zero_velocity); EXPECT_FALSE(effect.IsActive()); EXPECT_TRUE(GetAndResetPullReleased()); EXPECT_TRUE(GetAndResetRefreshAllowed()); } TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialYOffsetIsNotZero) { OverscrollRefresh effect(this); // A positive y scroll offset at the start of scroll will prevent activation, // even if the subsequent scroll overscrolls upward. gfx::Vector2dF nonzero_offset(0, 10); bool overflow_y_hidden = false; effect.OnFrameUpdated(nonzero_offset, overflow_y_hidden); effect.OnScrollBegin(); effect.OnFrameUpdated(gfx::Vector2dF(), overflow_y_hidden); ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); effect.OnScrollUpdateAck(false); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500))); effect.OnScrollEnd(gfx::Vector2dF()); EXPECT_FALSE(GetAndResetPullStarted()); EXPECT_FALSE(GetAndResetPullReleased()); } TEST_F(OverscrollRefreshTest, NotTriggeredIfOverflowYHidden) { OverscrollRefresh effect(this); // overflow-y:hidden at the start of scroll will prevent activation. gfx::Vector2dF zero_offset; bool overflow_y_hidden = true; effect.OnFrameUpdated(zero_offset, overflow_y_hidden); effect.OnScrollBegin(); ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); effect.OnScrollUpdateAck(false); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500))); effect.OnScrollEnd(gfx::Vector2dF()); EXPECT_FALSE(GetAndResetPullStarted()); EXPECT_FALSE(GetAndResetPullReleased()); } TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialScrollDownward) { OverscrollRefresh effect(this); effect.OnScrollBegin(); // A downward initial scroll will prevent activation, even if the subsequent // scroll overscrolls upward. ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, -10))); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); effect.OnScrollUpdateAck(false); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500))); effect.OnScrollEnd(gfx::Vector2dF()); EXPECT_FALSE(GetAndResetPullReleased()); } TEST_F(OverscrollRefreshTest, NotTriggeredIfInitialScrollOrTouchConsumed) { OverscrollRefresh effect(this); effect.OnScrollBegin(); ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck()); // Consumption of the initial touchmove or scroll should prevent future // activation. effect.OnScrollUpdateAck(true); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500))); effect.OnScrollUpdateAck(false); EXPECT_FALSE(effect.IsActive()); EXPECT_FALSE(effect.IsAwaitingScrollUpdateAck()); EXPECT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 500))); effect.OnScrollEnd(gfx::Vector2dF()); EXPECT_FALSE(GetAndResetPullStarted()); EXPECT_FALSE(GetAndResetPullReleased()); } TEST_F(OverscrollRefreshTest, NotTriggeredIfFlungDownward) { OverscrollRefresh effect(this); effect.OnScrollBegin(); ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck()); effect.OnScrollUpdateAck(false); ASSERT_TRUE(effect.IsActive()); EXPECT_TRUE(GetAndResetPullStarted()); // Terminating the pull with a down-directed fling should prevent triggering. effect.OnScrollEnd(gfx::Vector2dF(0, -1000)); EXPECT_TRUE(GetAndResetPullReleased()); EXPECT_FALSE(GetAndResetRefreshAllowed()); } TEST_F(OverscrollRefreshTest, NotTriggeredIfReleasedWithoutActivation) { OverscrollRefresh effect(this); effect.OnScrollBegin(); ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck()); effect.OnScrollUpdateAck(false); ASSERT_TRUE(effect.IsActive()); EXPECT_TRUE(GetAndResetPullStarted()); // An early release should prevent the refresh action from firing. effect.ReleaseWithoutActivation(); effect.OnScrollEnd(gfx::Vector2dF()); EXPECT_TRUE(GetAndResetPullReleased()); EXPECT_FALSE(GetAndResetRefreshAllowed()); } TEST_F(OverscrollRefreshTest, NotTriggeredIfReset) { OverscrollRefresh effect(this); effect.OnScrollBegin(); ASSERT_FALSE(effect.WillHandleScrollUpdate(gfx::Vector2dF(0, 10))); ASSERT_TRUE(effect.IsAwaitingScrollUpdateAck()); effect.OnScrollUpdateAck(false); ASSERT_TRUE(effect.IsActive()); EXPECT_TRUE(GetAndResetPullStarted()); // An early reset should prevent the refresh action from firing. effect.Reset(); EXPECT_TRUE(GetAndResetPullReset()); effect.OnScrollEnd(gfx::Vector2dF()); EXPECT_FALSE(GetAndResetPullReleased()); } } // namespace ui