1/*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.leanback.widget;
18
19import static org.junit.Assert.assertTrue;
20import static org.mockito.Matchers.any;
21import static org.mockito.Mockito.never;
22import static org.mockito.Mockito.spy;
23import static org.mockito.Mockito.times;
24
25import android.content.Context;
26import android.support.test.InstrumentationRegistry;
27import android.support.test.filters.SmallTest;
28import android.view.View;
29import android.view.ViewGroup;
30
31import androidx.recyclerview.widget.LinearLayoutManager;
32import androidx.recyclerview.widget.RecyclerView;
33
34import org.junit.Before;
35import org.junit.Test;
36import org.junit.runner.RunWith;
37import org.junit.runners.JUnit4;
38import org.mockito.Mockito;
39
40import java.util.ArrayList;
41import java.util.List;
42
43@SmallTest
44@RunWith(JUnit4.class)
45public class ItemBridgeAdapterTest {
46    private static final int sViewWidth = 100;
47    private static final int sViewHeight = 100;
48
49    public static class BasePresenter extends Presenter {
50
51        @Override
52        public ViewHolder onCreateViewHolder(ViewGroup parent) {
53            View view = new View(parent.getContext());
54            view.setLayoutParams(new ViewGroup.LayoutParams(sViewWidth, sViewHeight));
55            return new ViewHolder(view);
56        }
57
58        @Override
59        public void onBindViewHolder(ViewHolder viewHolder, Object item) {
60        }
61
62        @Override
63        public void onUnbindViewHolder(ViewHolder viewHolder) {
64
65        }
66    }
67
68    private ItemBridgeAdapter mItemBridgeAdapter;
69
70    private ArrayObjectAdapter mAdapter;
71
72    private RecyclerView mRecyclerView;
73
74    private Presenter mPresenter;
75
76    @Before
77    public void setup() {
78        mPresenter = spy(BasePresenter.class);
79
80        mItemBridgeAdapter = new ItemBridgeAdapter();
81        mAdapter = new ArrayObjectAdapter(mPresenter);
82        mAdapter.setItems(populateData(), null);
83        mItemBridgeAdapter.setAdapter(mAdapter);
84
85        Context context = InstrumentationRegistry.getTargetContext();
86        mRecyclerView = new RecyclerView(context);
87        mRecyclerView.setItemViewCacheSize(0);
88        mRecyclerView.setLayoutManager(new LinearLayoutManager(context,
89                LinearLayoutManager.VERTICAL, false));
90        mRecyclerView.setHasFixedSize(false); // force layout items in layout pass
91        mRecyclerView.setAdapter(mItemBridgeAdapter);
92        measureAndLayoutRecycleView(mRecyclerView);
93    }
94
95    List populateData() {
96        List data = new ArrayList();
97        for (int i = 0; i < 10000; i++) {
98            data.add(i);
99        }
100        return data;
101    }
102
103    static void measureAndLayoutRecycleView(RecyclerView recyclerView) {
104        measureAndLayout(recyclerView, 1920, 1080);
105    }
106
107    static void measureAndLayout(View view, int expectedWidth, int expectedHeight) {
108        view.measure(View.MeasureSpec.makeMeasureSpec(expectedWidth, View.MeasureSpec.EXACTLY),
109                View.MeasureSpec.makeMeasureSpec(expectedHeight, View.MeasureSpec.EXACTLY));
110        view.layout(0, 0, expectedWidth, expectedHeight);
111    }
112
113    @Test
114    public void onCreateAndOnBind() {
115        int childCount = mRecyclerView.getChildCount();
116        assertTrue(childCount > 0);
117        // Assert number of invokes of onCreateViewHolder and onBindViewHolder.
118        Mockito.verify(mPresenter, times(childCount)).onCreateViewHolder(any(ViewGroup.class));
119        Mockito.verify(mPresenter, times(childCount))
120                .onBindViewHolder(any(Presenter.ViewHolder.class), any());
121    }
122    @Test
123    public void onUnbind() {
124        assertTrue(mRecyclerView.getChildCount() > 0);
125        // When scroll one item offscreen, assert onUnbindViewHolder called once.
126        mRecyclerView.scrollBy(0, mRecyclerView.getChildAt(0).getHeight());
127        Mockito.verify(mPresenter, times(1))
128                .onUnbindViewHolder(any(Presenter.ViewHolder.class));
129    }
130
131    @Test
132    public void onUnbindWithTransientState() {
133        assertTrue(mRecyclerView.getChildCount() > 0);
134        // Set TransientState to true to simulate the view is running custom ViewPropertyAnimation.
135        mRecyclerView.getChildAt(0).setHasTransientState(true);
136        // When scroll one item with custom animation offscreen, assert onUnbindViewHolder
137        // was called once when ViewHolder is still having transient state.
138        mRecyclerView.scrollBy(0, mRecyclerView.getChildAt(0).getHeight());
139        Mockito.verify(mPresenter, times(1))
140                .onUnbindViewHolder(any(Presenter.ViewHolder.class));
141    }
142}
143