Home | History | Annotate | Download | only in widget
      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 
     17 package androidx.recyclerview.widget;
     18 
     19 import static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_APPEAR;
     20 import static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_DISAPPEARED;
     21 import static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_POST;
     22 import static androidx.recyclerview.widget.ViewInfoStore.InfoRecord.FLAG_PRE;
     23 
     24 import static org.junit.Assert.assertEquals;
     25 import static org.junit.Assert.assertFalse;
     26 import static org.junit.Assert.assertNotNull;
     27 import static org.junit.Assert.assertNull;
     28 import static org.junit.Assert.assertSame;
     29 import static org.junit.Assert.assertTrue;
     30 
     31 import android.support.test.filters.SmallTest;
     32 import android.view.View;
     33 
     34 import androidx.annotation.NonNull;
     35 import androidx.annotation.Nullable;
     36 import androidx.core.util.Pair;
     37 import androidx.recyclerview.widget.RecyclerView.ItemAnimator.ItemHolderInfo;
     38 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
     39 
     40 import org.junit.Before;
     41 import org.junit.Test;
     42 import org.junit.runner.RunWith;
     43 import org.junit.runners.JUnit4;
     44 
     45 import java.util.ArrayList;
     46 import java.util.HashMap;
     47 import java.util.List;
     48 import java.util.Map;
     49 
     50 @SuppressWarnings("ConstantConditions")
     51 @RunWith(JUnit4.class)
     52 @SmallTest
     53 public class ViewInfoStoreTest {
     54     ViewInfoStore mStore;
     55     LoggingProcessCallback mCallback;
     56     @Before
     57     public void prepare() {
     58         mStore = new ViewInfoStore();
     59         mCallback = new LoggingProcessCallback();
     60     }
     61 
     62     @Test
     63     public void addOverridePre() {
     64         RecyclerView.ViewHolder vh = new MockViewHolder();
     65         MockInfo info = new MockInfo();
     66         mStore.addToPreLayout(vh, info);
     67         MockInfo info2 = new MockInfo();
     68         mStore.addToPreLayout(vh, info2);
     69         assertSame(info2, find(vh, FLAG_PRE));
     70     }
     71 
     72     @Test
     73     public void addOverridePost() {
     74         RecyclerView.ViewHolder vh = new MockViewHolder();
     75         MockInfo info = new MockInfo();
     76         mStore.addToPostLayout(vh, info);
     77         MockInfo info2 = new MockInfo();
     78         mStore.addToPostLayout(vh, info2);
     79         assertSame(info2, find(vh, FLAG_POST));
     80     }
     81 
     82     @Test
     83     public void addRemoveAndReAdd() {
     84         RecyclerView.ViewHolder vh = new MockViewHolder();
     85         MockInfo pre = new MockInfo();
     86         mStore.addToPreLayout(vh, pre);
     87         MockInfo post1 = new MockInfo();
     88         mStore.addToPostLayout(vh, post1);
     89         mStore.onViewDetached(vh);
     90         mStore.addToDisappearedInLayout(vh);
     91     }
     92 
     93     @Test
     94     public void addToPreLayout() {
     95         RecyclerView.ViewHolder vh = new MockViewHolder();
     96         MockInfo info = new MockInfo();
     97         mStore.addToPreLayout(vh, info);
     98         assertSame(info, find(vh, FLAG_PRE));
     99         assertTrue(mStore.isInPreLayout(vh));
    100         mStore.removeViewHolder(vh);
    101         assertFalse(mStore.isInPreLayout(vh));
    102     }
    103 
    104     @Test
    105     public void addToPostLayout() {
    106         RecyclerView.ViewHolder vh = new MockViewHolder();
    107         MockInfo info = new MockInfo();
    108         mStore.addToPostLayout(vh, info);
    109         assertSame(info, find(vh, FLAG_POST));
    110         mStore.removeViewHolder(vh);
    111         assertNull(find(vh, FLAG_POST));
    112     }
    113 
    114     @Test
    115     public void popFromPreLayout() {
    116         assertEquals(0, sizeOf(FLAG_PRE));
    117         RecyclerView.ViewHolder vh = new MockViewHolder();
    118         MockInfo info = new MockInfo();
    119         mStore.addToPreLayout(vh, info);
    120         assertSame(info, mStore.popFromPreLayout(vh));
    121         assertNull(mStore.popFromPreLayout(vh));
    122     }
    123 
    124     @Test
    125     public void addToOldChangeHolders() {
    126         RecyclerView.ViewHolder vh = new MockViewHolder();
    127         mStore.addToOldChangeHolders(1, vh);
    128         assertSame(vh, mStore.getFromOldChangeHolders(1));
    129         mStore.removeViewHolder(vh);
    130         assertNull(mStore.getFromOldChangeHolders(1));
    131     }
    132 
    133     @Test
    134     public void appearListTests() {
    135         RecyclerView.ViewHolder vh = new MockViewHolder();
    136         RecyclerView.ItemAnimator.ItemHolderInfo info = new MockInfo();
    137         mStore.addToAppearedInPreLayoutHolders(vh, info);
    138         assertEquals(1, sizeOf(FLAG_APPEAR));
    139         RecyclerView.ViewHolder vh2 = new MockViewHolder();
    140         mStore.addToAppearedInPreLayoutHolders(vh2, info);
    141         assertEquals(2, sizeOf(FLAG_APPEAR));
    142         mStore.removeViewHolder(vh2);
    143         assertEquals(1, sizeOf(FLAG_APPEAR));
    144     }
    145 
    146     @Test
    147     public void disappearListTest() {
    148         RecyclerView.ViewHolder vh = new MockViewHolder();
    149         mStore.addToDisappearedInLayout(vh);
    150         assertEquals(1, sizeOf(FLAG_DISAPPEARED));
    151         mStore.addToDisappearedInLayout(vh);
    152         assertEquals(1, sizeOf(FLAG_DISAPPEARED));
    153         RecyclerView.ViewHolder vh2 = new MockViewHolder();
    154         mStore.addToDisappearedInLayout(vh2);
    155         assertEquals(2, sizeOf(FLAG_DISAPPEARED));
    156         mStore.removeViewHolder(vh2);
    157         assertEquals(1, sizeOf(FLAG_DISAPPEARED));
    158         mStore.removeFromDisappearedInLayout(vh);
    159         assertEquals(0, sizeOf(FLAG_DISAPPEARED));
    160     }
    161 
    162     @Test
    163     public void processAppear() {
    164         ViewHolder vh = new MockViewHolder();
    165         MockInfo info = new MockInfo();
    166         mStore.addToPostLayout(vh, info);
    167         mStore.process(mCallback);
    168         assertEquals(new Pair<>(null, info), mCallback.appeared.get(vh));
    169         assertTrue(mCallback.disappeared.isEmpty());
    170         assertTrue(mCallback.unused.isEmpty());
    171         assertTrue(mCallback.persistent.isEmpty());
    172     }
    173 
    174     @Test
    175     public void processDisappearNormal() {
    176         ViewHolder vh = new MockViewHolder();
    177         MockInfo info = new MockInfo();
    178         mStore.addToPreLayout(vh, info);
    179         mStore.process(mCallback);
    180         assertEquals(new Pair<>(info, null), mCallback.disappeared.get(vh));
    181         assertTrue(mCallback.appeared.isEmpty());
    182         assertTrue(mCallback.unused.isEmpty());
    183         assertTrue(mCallback.persistent.isEmpty());
    184     }
    185 
    186     @Test
    187     public void processDisappearMissingLayout() {
    188         ViewHolder vh = new MockViewHolder();
    189         MockInfo info = new MockInfo();
    190         mStore.addToPreLayout(vh, info);
    191         mStore.addToDisappearedInLayout(vh);
    192         mStore.process(mCallback);
    193         assertEquals(new Pair<>(info, null), mCallback.disappeared.get(vh));
    194         assertTrue(mCallback.appeared.isEmpty());
    195         assertTrue(mCallback.unused.isEmpty());
    196         assertTrue(mCallback.persistent.isEmpty());
    197     }
    198 
    199     @Test
    200     public void processDisappearMoveOut() {
    201         ViewHolder vh = new MockViewHolder();
    202         MockInfo pre = new MockInfo();
    203         MockInfo post = new MockInfo();
    204         mStore.addToPreLayout(vh, pre);
    205         mStore.addToDisappearedInLayout(vh);
    206         mStore.addToPostLayout(vh, post);
    207         mStore.process(mCallback);
    208         assertEquals(new Pair<>(pre, post), mCallback.disappeared.get(vh));
    209         assertTrue(mCallback.appeared.isEmpty());
    210         assertTrue(mCallback.unused.isEmpty());
    211         assertTrue(mCallback.persistent.isEmpty());
    212     }
    213 
    214     @Test
    215     public void processDisappearAppear() {
    216         ViewHolder vh = new MockViewHolder();
    217         MockInfo pre = new MockInfo();
    218         MockInfo post = new MockInfo();
    219         mStore.addToPreLayout(vh, pre);
    220         mStore.addToDisappearedInLayout(vh);
    221         mStore.addToPostLayout(vh, post);
    222         mStore.removeFromDisappearedInLayout(vh);
    223         mStore.process(mCallback);
    224         assertTrue(mCallback.disappeared.isEmpty());
    225         assertTrue(mCallback.appeared.isEmpty());
    226         assertTrue(mCallback.unused.isEmpty());
    227         assertEquals(mCallback.persistent.get(vh), new Pair<>(pre, post));
    228     }
    229 
    230     @Test
    231     public void processAppearAndDisappearInPostLayout() {
    232         ViewHolder vh = new MockViewHolder();
    233         MockInfo info1 = new MockInfo();
    234         mStore.addToPostLayout(vh, info1);
    235         mStore.addToDisappearedInLayout(vh);
    236         mStore.process(mCallback);
    237         assertTrue(mCallback.disappeared.isEmpty());
    238         assertTrue(mCallback.appeared.isEmpty());
    239         assertTrue(mCallback.persistent.isEmpty());
    240         assertSame(mCallback.unused.get(0), vh);
    241     }
    242 
    243     static class MockViewHolder extends RecyclerView.ViewHolder {
    244         public MockViewHolder() {
    245             super(new View(null));
    246         }
    247     }
    248 
    249     static class MockInfo extends RecyclerView.ItemAnimator.ItemHolderInfo {
    250 
    251     }
    252 
    253     private int sizeOf(int flags) {
    254         int cnt = 0;
    255         final int size = mStore.mLayoutHolderMap.size();
    256         for (int i = 0; i < size; i ++) {
    257             ViewInfoStore.InfoRecord record = mStore.mLayoutHolderMap.valueAt(i);
    258             if ((record.flags & flags) != 0) {
    259                 cnt ++;
    260             }
    261         }
    262         return cnt;
    263     }
    264 
    265     private RecyclerView.ItemAnimator.ItemHolderInfo find(RecyclerView.ViewHolder viewHolder,
    266             int flags) {
    267         final int size = mStore.mLayoutHolderMap.size();
    268         for (int i = 0; i < size; i ++) {
    269             ViewInfoStore.InfoRecord record = mStore.mLayoutHolderMap.valueAt(i);
    270             RecyclerView.ViewHolder holder = mStore.mLayoutHolderMap.keyAt(i);
    271             if ((record.flags & flags) != 0 && holder == viewHolder) {
    272                 if (flags == FLAG_PRE || flags == FLAG_APPEAR) {
    273                     return record.preInfo;
    274                 } else if (flags == FLAG_POST) {
    275                     return record.postInfo;
    276                 }
    277                 throw new UnsupportedOperationException("don't know this flag");
    278             }
    279         }
    280         return null;
    281     }
    282 
    283     private static class LoggingProcessCallback implements ViewInfoStore.ProcessCallback {
    284         final Map<ViewHolder, Pair<ItemHolderInfo, ItemHolderInfo>> disappeared = new HashMap<>();
    285         final Map<ViewHolder, Pair<ItemHolderInfo, ItemHolderInfo>> appeared = new HashMap<>();
    286         final Map<ViewHolder, Pair<ItemHolderInfo, ItemHolderInfo>> persistent = new HashMap<>();
    287         final List<ViewHolder> unused = new ArrayList<>();
    288         @Override
    289         public void processDisappeared(ViewHolder viewHolder,
    290                 ItemHolderInfo preInfo,
    291                 @Nullable ItemHolderInfo postInfo) {
    292             assertNotNull(preInfo);
    293             assertFalse(disappeared.containsKey(viewHolder));
    294             disappeared.put(viewHolder, new Pair<>(preInfo, postInfo));
    295         }
    296 
    297         @Override
    298         public void processAppeared(ViewHolder viewHolder,
    299                 @Nullable ItemHolderInfo preInfo, @NonNull ItemHolderInfo info) {
    300             assertNotNull(info);
    301             assertFalse(appeared.containsKey(viewHolder));
    302             appeared.put(viewHolder, new Pair<>(preInfo, info));
    303         }
    304 
    305         @Override
    306         public void processPersistent(ViewHolder viewHolder,
    307                 @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
    308             assertFalse(persistent.containsKey(viewHolder));
    309             assertNotNull(preInfo);
    310             assertNotNull(postInfo);
    311             persistent.put(viewHolder, new Pair<>(preInfo, postInfo));
    312         }
    313 
    314         @Override
    315         public void unused(ViewHolder holder) {
    316             unused.add(holder);
    317         }
    318     }
    319 
    320 }
    321