Commit 002bc1bd authored by Tomasz Wiszkowski's avatar Tomasz Wiszkowski Committed by Commit Bot

Fix SimpleRecyclerViewAdapter ModelList event propagation.

This CL addresses the SimpleRecyclerViewAdapter propagating incomplete
events to the parent RecyclerViewAdapter, resulting in partial UI
updates.

The CL also adds a few tests that confirm the behavior of the SRVA.

Change-Id: I4c35b54e925853e91d686fde5cc476f3d2efe6f9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2146063
Commit-Queue: Ender <ender@google.com>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#758528}
parent 06a437db
......@@ -399,6 +399,7 @@ junit_binary("ui_junit_tests") {
"junit/src/org/chromium/ui/modelutil/PropertyListModelTest.java",
"junit/src/org/chromium/ui/modelutil/PropertyModelTest.java",
"junit/src/org/chromium/ui/modelutil/SimpleListObservableTest.java",
"junit/src/org/chromium/ui/modelutil/SimpleRecyclerViewAdapterTest.java",
"junit/src/org/chromium/ui/resources/dynamics/BitmapDynamicResourceTest.java",
"junit/src/org/chromium/ui/resources/dynamics/ViewResourceAdapterTest.java",
"junit/src/org/chromium/ui/shadows/ShadowAnimatedStateListDrawable.java",
......
......@@ -73,7 +73,7 @@ public class SimpleRecyclerViewAdapter
mListObserver = new ListObserver<Void>() {
@Override
public void onItemRangeInserted(ListObservable source, int index, int count) {
notifyItemInserted(index);
notifyItemRangeInserted(index, count);
}
@Override
......@@ -84,7 +84,7 @@ public class SimpleRecyclerViewAdapter
@Override
public void onItemRangeChanged(
ListObservable<Void> source, int index, int count, @Nullable Void payload) {
notifyItemChanged(index);
notifyItemRangeChanged(index, count);
}
@Override
......
// Copyright 2020 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.
package org.chromium.ui.modelutil;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.view.View;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
import java.util.ArrayList;
import java.util.List;
/**
* Tests to ensure/validate SimpleRecyclerViewAdapter behavior.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class SimpleRecyclerViewAdapterTest {
private static final PropertyModel.WritableIntPropertyKey INT_PROPERTY =
new PropertyModel.WritableIntPropertyKey();
private static final Integer VIEW_TYPE_1 = 1;
private static final Integer VIEW_TYPE_2 = 2;
private static final Integer VIEW_TYPE_3 = 3;
private ModelList mModelList;
private PropertyModel mModel;
// Mockito Spies allow us to intercept calls to parent class.
private SimpleRecyclerViewAdapter mSpyAdapter;
@Mock
View mMockView;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mModel = new PropertyModel(INT_PROPERTY);
mModelList = new ModelList();
// Note: this behaves both like a mock and a real object.
// Methods calls can be mocked or tracked to validate class behavior.
mSpyAdapter = Mockito.mock(SimpleRecyclerViewAdapter.class,
Mockito.withSettings()
.useConstructor(mModelList)
.defaultAnswer(Mockito.CALLS_REAL_METHODS));
mSpyAdapter.registerType(VIEW_TYPE_1, parent -> mMockView, (m, v, p) -> {});
mSpyAdapter.registerType(VIEW_TYPE_2, parent -> mMockView, (m, v, p) -> {});
mSpyAdapter.registerType(VIEW_TYPE_3, parent -> mMockView, (m, v, p) -> {});
}
@Test
public void testObserver_listModelItemsAdded() {
mModelList.add(new ModelListAdapter.ListItem(VIEW_TYPE_1, mModel));
verify(mSpyAdapter, times(1)).notifyItemRangeInserted(0, 1);
mModelList.add(new ModelListAdapter.ListItem(VIEW_TYPE_2, mModel));
verify(mSpyAdapter, times(1)).notifyItemRangeInserted(1, 1);
}
@Test
public void testObserver_listModelItemsAddedInBatch() {
mModelList.add(new ModelListAdapter.ListItem(VIEW_TYPE_1, mModel));
verify(mSpyAdapter, times(1)).notifyItemRangeInserted(0, 1);
List<ModelListAdapter.ListItem> items = new ArrayList<>();
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_1, mModel));
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_2, mModel));
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_3, mModel));
mModelList.addAll(items);
verify(mSpyAdapter, times(1)).notifyItemRangeInserted(1, 3);
}
@Test
public void testObserver_listModelItemsSet() {
List<ModelListAdapter.ListItem> items = new ArrayList<>();
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_1, mModel));
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_2, mModel));
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_3, mModel));
mModelList.set(items);
verify(mSpyAdapter, times(1)).notifyItemRangeInserted(0, 3);
}
@Test
public void testObserver_listModelItemsRemove() {
List<ModelListAdapter.ListItem> items = new ArrayList<>();
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_1, mModel));
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_2, mModel));
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_3, mModel));
mModelList.set(items);
verify(mSpyAdapter, times(1)).notifyItemRangeInserted(0, 3);
mModelList.removeRange(0, 2);
verify(mSpyAdapter, times(1)).notifyItemRangeRemoved(0, 2);
}
@Test
public void testObserver_listModelItemsClear() {
List<ModelListAdapter.ListItem> items = new ArrayList<>();
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_1, mModel));
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_2, mModel));
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_3, mModel));
mModelList.set(items);
verify(mSpyAdapter, times(1)).notifyItemRangeInserted(0, 3);
mModelList.clear();
verify(mSpyAdapter, times(1)).notifyItemRangeRemoved(0, 3);
}
@Test
public void testObserver_listModelItemMoved() {
List<ModelListAdapter.ListItem> items = new ArrayList<>();
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_1, mModel));
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_2, mModel));
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_3, mModel));
mModelList.set(items);
verify(mSpyAdapter, times(1)).notifyItemRangeInserted(0, 3);
mModelList.move(1, 2);
verify(mSpyAdapter, times(1)).notifyItemMoved(1, 2);
}
@Test
public void testObserver_listModelItemUpdated() {
List<ModelListAdapter.ListItem> items = new ArrayList<>();
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_1, mModel));
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_2, mModel));
items.add(new ModelListAdapter.ListItem(VIEW_TYPE_3, mModel));
mModelList.set(items);
verify(mSpyAdapter, times(1)).notifyItemRangeInserted(0, 3);
mModelList.update(1, new ModelListAdapter.ListItem(VIEW_TYPE_2, mModel));
verify(mSpyAdapter, times(1)).notifyItemRangeChanged(1, 1);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment