[go: nahoru, domu]

blob: 8a114c3598148706c177c9a6f210afedf436c760 [file] [log] [blame]
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.swiperefreshlayout.widget;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.core.view.NestedScrollingParent2;
import androidx.core.view.ViewCompat;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
/**
* Small integration tests that verifies correctness of {@link SwipeRefreshLayout}'s
* NestedScrollingParent2 implementation.
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SwipeRefreshLayoutNestedScrollingParent2Test {
private SwipeRefreshLayout mSwipeRefreshLayout;
private NestedScrollingSpyView mParent;
private View mChild;
@Before
public void instantiateMembers() {
mSwipeRefreshLayout = new SwipeRefreshLayout(ApplicationProvider.getApplicationContext());
mParent = spy(new NestedScrollingSpyView(ApplicationProvider.getApplicationContext()));
mChild = new View(ApplicationProvider.getApplicationContext());
}
@Test
public void onStartNestedScroll_scrollAxisIncludesVerticalAndTypeTouch_returnsTrue() {
int vertical = ViewCompat.SCROLL_AXIS_VERTICAL;
int both = ViewCompat.SCROLL_AXIS_VERTICAL | ViewCompat.SCROLL_AXIS_HORIZONTAL;
onStartNestedScroll(vertical, ViewCompat.TYPE_TOUCH, true);
onStartNestedScroll(both, ViewCompat.TYPE_TOUCH, true);
}
@Test
public void onStartNestedScroll_typeIsNotTouch_returnsFalse() {
int vertical = ViewCompat.SCROLL_AXIS_VERTICAL;
int both = ViewCompat.SCROLL_AXIS_VERTICAL | ViewCompat.SCROLL_AXIS_HORIZONTAL;
onStartNestedScroll(vertical, ViewCompat.TYPE_NON_TOUCH, false);
onStartNestedScroll(both, ViewCompat.TYPE_NON_TOUCH, false);
}
@Test
public void onStartNestedScroll_scrollAxisExcludesVertical_returnsFalse() {
int horizontal = ViewCompat.SCROLL_AXIS_HORIZONTAL;
int neither = ViewCompat.SCROLL_AXIS_NONE;
onStartNestedScroll(horizontal, ViewCompat.TYPE_TOUCH, false);
onStartNestedScroll(neither, ViewCompat.TYPE_TOUCH, false);
}
@Test
public void onNestedScrollAccepted_callsParentsOnStartNestedScrollWithCorrectParams() {
setupNestedScrollViewWithParentAndChild();
mSwipeRefreshLayout.onNestedScrollAccepted(mChild, mChild,
ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
verify(mParent, times(1)).onStartNestedScroll(
mSwipeRefreshLayout,
mSwipeRefreshLayout,
ViewCompat.SCROLL_AXIS_VERTICAL,
ViewCompat.TYPE_TOUCH);
verify(mParent, times(1)).onStartNestedScroll(
any(View.class),
any(View.class),
anyInt(),
anyInt());
}
@Test
public void onNestedScrollAccepted_callsParentsOnNestedScrollAcceptedWithCorrectParams() {
setupNestedScrollViewWithParentAndChild();
doReturn(true)
.when(mParent)
.onStartNestedScroll(any(View.class), any(View.class), anyInt(), anyInt());
mSwipeRefreshLayout.onNestedScrollAccepted(
mChild,
mChild,
ViewCompat.SCROLL_AXIS_VERTICAL,
ViewCompat.TYPE_TOUCH);
verify(mParent, times(1)).onNestedScrollAccepted(
mSwipeRefreshLayout,
mSwipeRefreshLayout,
ViewCompat.SCROLL_AXIS_VERTICAL,
ViewCompat.TYPE_TOUCH);
verify(mParent, times(1)).onNestedScrollAccepted(
any(View.class),
any(View.class),
anyInt(),
anyInt());
}
@Test
public void onNestedScrollAccepted_bothOrientations_pOnNestedScrollAcceptedCalledWithVert() {
setupNestedScrollViewWithParentAndChild();
doReturn(true)
.when(mParent)
.onStartNestedScroll(any(View.class), any(View.class), anyInt(), anyInt());
mSwipeRefreshLayout.onNestedScrollAccepted(
mChild,
mChild,
ViewCompat.SCROLL_AXIS_VERTICAL | ViewCompat.SCROLL_AXIS_HORIZONTAL,
ViewCompat.TYPE_TOUCH);
verify(mParent, times(1)).onNestedScrollAccepted(
any(View.class),
any(View.class),
eq(ViewCompat.SCROLL_AXIS_VERTICAL),
anyInt());
verify(mParent, times(1)).onNestedScrollAccepted(
any(View.class),
any(View.class),
anyInt(),
anyInt());
}
@Test
public void onNestedScrollAccepted_parentRejects_parentOnNestedScrollAcceptedNotCalled() {
setupNestedScrollViewWithParentAndChild();
doReturn(false)
.when(mParent)
.onStartNestedScroll(any(View.class), any(View.class), anyInt(), anyInt());
mSwipeRefreshLayout.onNestedScrollAccepted(
mChild,
mChild,
ViewCompat.SCROLL_AXIS_VERTICAL,
ViewCompat.TYPE_TOUCH);
verify(mParent, never()).onNestedScrollAccepted(
any(View.class),
any(View.class),
anyInt(),
anyInt());
}
@Test
public void onStopNestedScroll_parentOnStopNestedScrollCalledWithCorrectParams() {
setupNestedScrollViewWithParentAndChild();
doReturn(true)
.when(mParent)
.onStartNestedScroll(any(View.class), any(View.class), anyInt(), anyInt());
mSwipeRefreshLayout.onNestedScrollAccepted(mChild, mChild,
ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
mSwipeRefreshLayout.onStopNestedScroll(mChild, ViewCompat.TYPE_TOUCH);
verify(mParent, times(1)).onStopNestedScroll(mSwipeRefreshLayout,
ViewCompat.TYPE_TOUCH);
verify(mParent, times(1)).onStopNestedScroll(any(View.class), anyInt());
}
@Test
public void onStopNestedScroll_parentRejects_parentOnStopNestedScrollNotCalled() {
setupNestedScrollViewWithParentAndChild();
doReturn(false)
.when(mParent)
.onStartNestedScroll(any(View.class), any(View.class), anyInt(), anyInt());
mSwipeRefreshLayout.onNestedScrollAccepted(mChild, mChild,
ViewCompat.SCROLL_AXIS_VERTICAL);
mSwipeRefreshLayout.onStopNestedScroll(mChild, ViewCompat.TYPE_TOUCH);
verify(mParent, never()).onStopNestedScroll(any(View.class), anyInt());
}
@Test
public void onNestedScroll_parentOnNestedScrollCalledWithCorrectParams() {
setupNestedScrollViewWithParentAndChild();
doReturn(true)
.when(mParent)
.onStartNestedScroll(any(View.class), any(View.class), anyInt(), anyInt());
mSwipeRefreshLayout.onNestedScrollAccepted(mChild, mChild,
ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
mSwipeRefreshLayout.onNestedScroll(mChild, 1, 2, 3, 4, ViewCompat.TYPE_TOUCH);
verify(mParent, times(1)).onNestedScroll(
mSwipeRefreshLayout,
1,
2,
3,
4,
ViewCompat.TYPE_TOUCH);
verify(mParent, times(1)).onNestedScroll(
any(View.class),
anyInt(),
anyInt(),
anyInt(),
anyInt(),
anyInt());
}
@Test
public void onNestedScroll_parentRejects_parentOnNestedScrollNotCalled() {
setupNestedScrollViewWithParentAndChild();
doReturn(false)
.when(mParent)
.onStartNestedScroll(any(View.class), any(View.class), anyInt(), anyInt());
mSwipeRefreshLayout.onNestedScrollAccepted(mChild, mChild,
ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
mSwipeRefreshLayout.onNestedScroll(mChild, 1, 2, 3, 4, ViewCompat.TYPE_TOUCH);
verify(mParent, never()).onNestedScroll(any(View.class), anyInt(), anyInt(),
anyInt(), anyInt(), anyInt());
}
@Test
public void onNestedPreScroll_parentOnNestedPreScrollCalledWithCorrectParams() {
setupNestedScrollViewWithParentAndChild();
doReturn(true)
.when(mParent)
.onStartNestedScroll(any(View.class), any(View.class), anyInt(), anyInt());
mSwipeRefreshLayout.onNestedScrollAccepted(mChild, mChild,
ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
mSwipeRefreshLayout.onNestedPreScroll(mChild, 1, 2, new int[]{0, 0},
ViewCompat.TYPE_TOUCH);
verify(mParent, times(1)).onNestedPreScroll(eq(mSwipeRefreshLayout), eq(1), eq(2),
eq(new int[]{0, 0}), eq(ViewCompat.TYPE_TOUCH));
verify(mParent, times(1)).onNestedPreScroll(any(View.class), anyInt(), anyInt(),
any(int[].class), anyInt());
}
@Test
public void onNestedPreScroll_parentRejects_parentOnNestedPreScrollNotCalled() {
setupNestedScrollViewWithParentAndChild();
doReturn(false)
.when(mParent)
.onStartNestedScroll(any(View.class), any(View.class), anyInt(), anyInt());
mSwipeRefreshLayout.onNestedScrollAccepted(mChild, mChild,
ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
mSwipeRefreshLayout.onNestedPreScroll(mChild, 1, 2, new int[2],
ViewCompat.TYPE_TOUCH);
verify(mParent, never()).onNestedPreScroll(any(View.class), anyInt(), anyInt(),
any(int[].class), anyInt());
}
// onNestedPreScroll, srl dragged down, parent on NestedPreScroll called with remainder
@Test
public void onNestedPreScroll_mSwipeRefreshPulledPartWay_parentReceivesRemainder() {
setupNestedScrollViewWithParentAndChild();
doReturn(true)
.when(mParent)
.onStartNestedScroll(any(View.class), any(View.class), anyInt(), anyInt());
mSwipeRefreshLayout.onNestedScrollAccepted(mChild, mChild,
ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
// Make sure the distance to trigger is greater than our pull so we don't accidentally
// fire refresh.
mSwipeRefreshLayout.setDistanceToTriggerSync(50);
// Pull refresh down part way first so when we scroll up, we are testing that we consumed
// the same portion that we scrolled down and passed remainder to parent.
mSwipeRefreshLayout.onNestedScroll(mChild, 0, 0, 0, -20, ViewCompat.TYPE_TOUCH);
mSwipeRefreshLayout.onNestedPreScroll(mChild, 0, 50, new int[2], ViewCompat.TYPE_TOUCH);
verify(mParent, times(1)).onNestedPreScroll(eq(mSwipeRefreshLayout), eq(0), eq(30),
eq(new int[]{0, 0}), eq(ViewCompat.TYPE_TOUCH));
verify(mParent, times(1)).onNestedPreScroll(any(View.class), anyInt(), anyInt(),
any(int[].class), anyInt());
}
@Test
public void onNestedPreScroll_mSwipeRefreshPulledPartWay_mutatesConsumedCorrectAmount() {
// Arrange
setupNestedScrollViewWithParentAndChild();
doReturn(true)
.when(mParent)
.onStartNestedScroll(any(View.class), any(View.class), anyInt(), anyInt());
// Mutate consumed in call to onNestedPreScroll.
doAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) {
int[] consumed = (int[]) invocation.getArguments()[3];
consumed[1] += 5;
return null;
}}).when(mParent)
.onNestedPreScroll(any(View.class), anyInt(), anyInt(), any(int[].class), anyInt());
mSwipeRefreshLayout.onNestedScrollAccepted(mChild, mChild, ViewCompat.SCROLL_AXIS_VERTICAL,
ViewCompat.TYPE_TOUCH);
// Make sure the distance to trigger is greater than our pull so we don't accidentally
// fire refresh.
mSwipeRefreshLayout.setDistanceToTriggerSync(50);
// Pull refresh down part way first so when we scroll up, we are testing that we consumed
// the same portion that we scrolled down and passed remainder to parent.
mSwipeRefreshLayout.onNestedScroll(mChild, 0, 0, 0, -20, ViewCompat.TYPE_TOUCH);
int[] consumed = new int[2];
// Act
mSwipeRefreshLayout.onNestedPreScroll(mChild, 0, 50, consumed, ViewCompat.TYPE_TOUCH);
// Assert
assertThat(consumed, is(new int[]{0, 25}));
}
private void onStartNestedScroll(int iScrollAxis, int type, boolean oRetValue) {
boolean retVal = mSwipeRefreshLayout.onStartNestedScroll(mChild, mChild, iScrollAxis,
type);
assertThat(retVal, is(oRetValue));
}
private void setupNestedScrollViewWithParentAndChild() {
mSwipeRefreshLayout.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 100));
mSwipeRefreshLayout.setMinimumHeight(100);
mChild.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 100));
mChild.setMinimumHeight(100);
mSwipeRefreshLayout.addView(mChild);
mParent.addView(mSwipeRefreshLayout);
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY);
mParent.measure(widthMeasureSpec, heightMeasureSpec);
mParent.layout(0, 0, 100, 100);
}
public class NestedScrollingSpyView extends FrameLayout implements NestedScrollingParent2 {
public NestedScrollingSpyView(Context context) {
super(context);
}
@Override
public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes,
int type) {
return false;
}
@Override
public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes,
int type) {
}
@Override
public void onStopNestedScroll(@NonNull View target, int type) {
}
@Override
public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed, int type) {
}
@Override
public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed,
int type) {
}
@Override
public boolean onStartNestedScroll(View child, View target, int axes) {
return false;
}
@Override
public void onNestedScrollAccepted(View child, View target, int axes) {
}
@Override
public void onStopNestedScroll(View target) {
}
@Override
public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
int dyUnconsumed) {
}
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
}
@Override
public boolean onNestedFling(View target, float velocityX, float velocityY,
boolean consumed) {
return false;
}
@Override
public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
return false;
}
@Override
public int getNestedScrollAxes() {
return 0;
}
}
}