blob: 265a6006839289e7bddf1dd305d4ecbd65fd8be8 [file] [log] [blame]
// Copyright 2019 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.chrome.browser.tasks.tab_management;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.chromium.base.GarbageCollectionTestUtils.canBeGarbageCollected;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.MediumTest;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.Callback;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
import org.chromium.chrome.tab_ui.R;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ui.DummyUiActivityTestCase;
import org.chromium.content_public.browser.test.util.CriteriaHelper;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
import org.chromium.ui.widget.ButtonCompat;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Tests for the {@link android.support.v7.widget.RecyclerView.ViewHolder} classes for {@link
* TabListCoordinator}.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
public class TabListViewHolderTest extends DummyUiActivityTestCase {
private static final int TAB1_ID = 456;
private static final int TAB2_ID = 789;
private ViewGroup mTabGridView;
private PropertyModel mGridModel;
private PropertyModelChangeProcessor mGridMCP;
private ViewGroup mTabStripView;
private PropertyModel mStripModel;
private PropertyModelChangeProcessor mStripMCP;
private ViewGroup mSelectableTabGridView;
private PropertyModel mSelectableModel;
private PropertyModelChangeProcessor mSelectableMCP;
private SelectionDelegate<Integer> mSelectionDelegate;
private int mSelectedTabBackgroundDrawableId = R.drawable.selected_tab_background;
private TabListMediator.ThumbnailFetcher mMockThumbnailProvider =
new TabListMediator.ThumbnailFetcher(new TabListMediator.ThumbnailProvider() {
@Override
public void getTabThumbnailWithCallback(Tab tab, Callback<Bitmap> callback,
boolean forceUpdate, boolean writeToCache) {
Bitmap bitmap = mShouldReturnBitmap
? Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
: null;
callback.onResult(bitmap);
mThumbnailFetchedCount.incrementAndGet();
}
}, null, false, false);
private AtomicInteger mThumbnailFetchedCount = new AtomicInteger();
private TabListMediator.TabActionListener mMockCloseListener =
new TabListMediator.TabActionListener() {
@Override
public void run(int tabId) {
mCloseClicked.set(true);
mCloseTabId.set(tabId);
}
};
private AtomicBoolean mCloseClicked = new AtomicBoolean();
private AtomicInteger mCloseTabId = new AtomicInteger();
private TabListMediator.TabActionListener mMockSelectedListener =
new TabListMediator.TabActionListener() {
@Override
public void run(int tabId) {
mSelectClicked.set(true);
mSelectTabId.set(tabId);
}
};
private AtomicBoolean mSelectClicked = new AtomicBoolean();
private AtomicInteger mSelectTabId = new AtomicInteger();
private TabListMediator.TabActionListener mMockCreateGroupButtonListener =
new TabListMediator.TabActionListener() {
@Override
public void run(int tabId) {
mCreateGroupButtonClicked.set(true);
mCreateGroupTabId.set(tabId);
}
};
private AtomicBoolean mCreateGroupButtonClicked = new AtomicBoolean();
private AtomicInteger mCreateGroupTabId = new AtomicInteger();
private boolean mShouldReturnBitmap;
@Override
public void setUpTest() throws Exception {
super.setUpTest();
ViewGroup view = new LinearLayout(getActivity());
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
TestThreadUtils.runOnUiThreadBlocking(() -> {
getActivity().setContentView(view, params);
mTabGridView = (ViewGroup) getActivity().getLayoutInflater().inflate(
R.layout.closable_tab_grid_card_item, null);
mTabStripView = (ViewGroup) getActivity().getLayoutInflater().inflate(
R.layout.tab_strip_item, null);
mSelectableTabGridView = (ViewGroup) getActivity().getLayoutInflater().inflate(
R.layout.selectable_tab_grid_card_item, null);
view.addView(mTabGridView);
view.addView(mTabStripView);
view.addView(mSelectableTabGridView);
});
mSelectionDelegate = new SelectionDelegate<>();
mGridModel = new PropertyModel.Builder(TabProperties.ALL_KEYS_TAB_GRID)
.with(TabProperties.TAB_ID, TAB1_ID)
.with(TabProperties.TAB_SELECTED_LISTENER, mMockSelectedListener)
.with(TabProperties.TAB_CLOSED_LISTENER, mMockCloseListener)
.with(TabProperties.SELECTED_TAB_BACKGROUND_DRAWABLE_ID,
mSelectedTabBackgroundDrawableId)
.build();
mStripModel = new PropertyModel.Builder(TabProperties.ALL_KEYS_TAB_STRIP)
.with(TabProperties.TAB_SELECTED_LISTENER, mMockSelectedListener)
.with(TabProperties.TAB_CLOSED_LISTENER, mMockCloseListener)
.build();
mSelectableModel =
new PropertyModel.Builder(TabProperties.ALL_KEYS_TAB_GRID)
.with(TabProperties.SELECTABLE_TAB_CLICKED_LISTENER, mMockSelectedListener)
.with(TabProperties.TAB_SELECTION_DELEGATE, mSelectionDelegate)
.with(TabProperties.SELECTED_TAB_BACKGROUND_DRAWABLE_ID,
mSelectedTabBackgroundDrawableId)
.build();
TestThreadUtils.runOnUiThreadBlocking(() -> {
mGridMCP = PropertyModelChangeProcessor.create(
mGridModel, mTabGridView, TabGridViewBinder::bindClosableTab);
mStripMCP = PropertyModelChangeProcessor.create(
mStripModel, mTabStripView, TabStripViewBinder::bind);
mSelectableMCP = PropertyModelChangeProcessor.create(
mSelectableModel, mSelectableTabGridView, TabGridViewBinder::bindSelectableTab);
});
}
private void testGridSelected(ViewGroup holder, PropertyModel model) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
model.set(TabProperties.IS_SELECTED, true);
Assert.assertTrue(holder.getForeground() != null);
model.set(TabProperties.IS_SELECTED, false);
Assert.assertFalse(holder.getForeground() != null);
} else {
model.set(TabProperties.IS_SELECTED, true);
View selectedView = holder.findViewById(R.id.selected_view_below_lollipop);
Assert.assertTrue(selectedView.getVisibility() == View.VISIBLE);
model.set(TabProperties.IS_SELECTED, false);
Assert.assertTrue(selectedView.getVisibility() == View.GONE);
}
mStripModel.set(TabProperties.IS_SELECTED, true);
Assert.assertTrue(((FrameLayout) mTabStripView).getForeground() != null);
mStripModel.set(TabProperties.IS_SELECTED, false);
Assert.assertFalse(((FrameLayout) mTabStripView).getForeground() != null);
}
@Test
@MediumTest
@UiThreadTest
public void testSelected() {
testGridSelected(mTabGridView, mGridModel);
mStripModel.set(TabProperties.IS_SELECTED, true);
Assert.assertTrue(((FrameLayout) mTabStripView).getForeground() != null);
mStripModel.set(TabProperties.IS_SELECTED, false);
Assert.assertFalse(((FrameLayout) mTabStripView).getForeground() != null);
testGridSelected(mSelectableTabGridView, mSelectableModel);
mSelectableModel.set(TabProperties.IS_SELECTED, true);
ImageView actionButton = mSelectableTabGridView.findViewById(R.id.action_button);
Assert.assertTrue(actionButton.getBackground().getLevel() == 1);
Assert.assertTrue(actionButton.getDrawable() != null);
Assert.assertEquals(255, actionButton.getDrawable().getAlpha());
mSelectableModel.set(TabProperties.IS_SELECTED, false);
Assert.assertTrue(actionButton.getBackground().getLevel() == 0);
Assert.assertEquals(0, actionButton.getDrawable().getAlpha());
}
@Test
@MediumTest
public void testAnimationRestored() {
View backgroundView = mTabGridView.findViewById(R.id.background_view);
TestThreadUtils.runOnUiThreadBlocking(() -> {
mGridModel.set(TabProperties.IS_SELECTED, true);
mGridModel.set(TabProperties.CARD_ANIMATION_STATUS,
ClosableTabGridView.AnimationStatus.CARD_RESTORE);
});
CriteriaHelper.pollUiThread(
() -> !((ClosableTabGridView) mTabGridView).getIsAnimatingForTesting());
Assert.assertTrue(backgroundView.getVisibility() == View.GONE);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
View selectedView = mTabGridView.findViewById(R.id.selected_view_below_lollipop);
Assert.assertTrue(selectedView.getVisibility() == View.VISIBLE);
} else {
Drawable selectedDrawable = mTabGridView.getForeground();
Assert.assertNotNull(selectedDrawable);
}
TestThreadUtils.runOnUiThreadBlocking(() -> {
mGridModel.set(TabProperties.IS_SELECTED, false);
mGridModel.set(TabProperties.CARD_ANIMATION_STATUS,
ClosableTabGridView.AnimationStatus.CARD_RESTORE);
});
CriteriaHelper.pollUiThread(
() -> !((ClosableTabGridView) mTabGridView).getIsAnimatingForTesting());
Assert.assertTrue(backgroundView.getVisibility() == View.GONE);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
View selectedView = mTabGridView.findViewById(R.id.selected_view_below_lollipop);
Assert.assertTrue(selectedView.getVisibility() == View.GONE);
} else {
Drawable selectedDrawable = mTabGridView.getForeground();
Assert.assertNull(selectedDrawable);
}
}
@Test
@MediumTest
@UiThreadTest
public void testTitle() {
final String title = "Surf the cool webz";
mGridModel.set(TabProperties.TITLE, title);
TextView textView = mTabGridView.findViewById(R.id.tab_title);
Assert.assertEquals(textView.getText(), title);
mSelectableModel.set(TabProperties.TITLE, title);
textView = mSelectableTabGridView.findViewById(R.id.tab_title);
Assert.assertEquals(textView.getText(), title);
}
@Test
@MediumTest
@UiThreadTest
public void testThumbnail() {
mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
ImageView thumbnail = mTabGridView.findViewById(R.id.tab_thumbnail);
Assert.assertNull(thumbnail.getDrawable());
mGridModel.set(TabProperties.THUMBNAIL_FETCHER, null);
mShouldReturnBitmap = true;
mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
assertThat(thumbnail.getDrawable(), instanceOf(BitmapDrawable.class));
Assert.assertEquals(2, mThumbnailFetchedCount.get());
}
@Test
@MediumTest
@UiThreadTest
public void testThumbnailGCAfterNullBitmap() {
ImageView thumbnail = mTabGridView.findViewById(R.id.tab_thumbnail);
mShouldReturnBitmap = true;
mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
assertThat(thumbnail.getDrawable(), instanceOf(BitmapDrawable.class));
Bitmap bitmap = ((BitmapDrawable) thumbnail.getDrawable()).getBitmap();
WeakReference<Bitmap> ref = new WeakReference<>(bitmap);
bitmap = null;
Assert.assertFalse(canBeGarbageCollected(ref));
mShouldReturnBitmap = false;
mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
Assert.assertTrue(canBeGarbageCollected(ref));
Assert.assertEquals(2, mThumbnailFetchedCount.get());
}
@Test
@MediumTest
@UiThreadTest
public void testThumbnailGCAfterNewBitmap() {
ImageView thumbnail = mTabGridView.findViewById(R.id.tab_thumbnail);
mShouldReturnBitmap = true;
mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
assertThat(thumbnail.getDrawable(), instanceOf(BitmapDrawable.class));
Bitmap bitmap = ((BitmapDrawable) thumbnail.getDrawable()).getBitmap();
WeakReference<Bitmap> ref = new WeakReference<>(bitmap);
bitmap = null;
Assert.assertFalse(canBeGarbageCollected(ref));
mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
Assert.assertTrue(canBeGarbageCollected(ref));
Assert.assertEquals(2, mThumbnailFetchedCount.get());
}
@Test
@MediumTest
@UiThreadTest
public void testResetThumbnailGC() {
ImageView thumbnail = mTabGridView.findViewById(R.id.tab_thumbnail);
mShouldReturnBitmap = true;
mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
assertThat(thumbnail.getDrawable(), instanceOf(BitmapDrawable.class));
Bitmap bitmap = ((BitmapDrawable) thumbnail.getDrawable()).getBitmap();
WeakReference<Bitmap> ref = new WeakReference<>(bitmap);
bitmap = null;
Assert.assertFalse(canBeGarbageCollected(ref));
mGridModel.set(TabProperties.THUMBNAIL_FETCHER, null);
Assert.assertTrue(canBeGarbageCollected(ref));
}
@Test
@MediumTest
@UiThreadTest
public void testHiddenGC() {
ImageView thumbnail = mTabGridView.findViewById(R.id.tab_thumbnail);
mShouldReturnBitmap = true;
mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
assertThat(thumbnail.getDrawable(), instanceOf(BitmapDrawable.class));
Bitmap bitmap = ((BitmapDrawable) thumbnail.getDrawable()).getBitmap();
WeakReference<Bitmap> ref = new WeakReference<>(bitmap);
bitmap = null;
Assert.assertFalse(canBeGarbageCollected(ref));
mGridModel.set(TabProperties.THUMBNAIL_FETCHER, null);
Assert.assertTrue(canBeGarbageCollected(ref));
Assert.assertNull(thumbnail.getDrawable());
Assert.assertEquals(1, mThumbnailFetchedCount.get());
}
@Test
@MediumTest
@UiThreadTest
public void testHiddenThenShow() {
ImageView thumbnail = mTabGridView.findViewById(R.id.tab_thumbnail);
mShouldReturnBitmap = true;
mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
assertThat(thumbnail.getDrawable(), instanceOf(BitmapDrawable.class));
Assert.assertEquals(1, mThumbnailFetchedCount.get());
mGridModel.set(TabProperties.THUMBNAIL_FETCHER, null);
Assert.assertNull(thumbnail.getDrawable());
Assert.assertEquals(1, mThumbnailFetchedCount.get());
mGridModel.set(TabProperties.THUMBNAIL_FETCHER, mMockThumbnailProvider);
assertThat(thumbnail.getDrawable(), instanceOf(BitmapDrawable.class));
Assert.assertEquals(2, mThumbnailFetchedCount.get());
}
@Test
@MediumTest
@UiThreadTest
public void testClickToSelect() {
Assert.assertFalse(mSelectClicked.get());
mTabGridView.performClick();
Assert.assertTrue(mSelectClicked.get());
mSelectClicked.set(false);
int firstSelectId = mSelectTabId.get();
Assert.assertEquals(TAB1_ID, firstSelectId);
mGridModel.set(TabProperties.TAB_ID, TAB2_ID);
mTabGridView.performClick();
Assert.assertTrue(mSelectClicked.get());
mSelectClicked.set(false);
int secondSelectId = mSelectTabId.get();
// When TAB_ID in PropertyModel is updated, binder should select tab with updated tab ID.
Assert.assertEquals(TAB2_ID, secondSelectId);
Assert.assertNotEquals(firstSelectId, secondSelectId);
ImageButton button = mTabStripView.findViewById(R.id.tab_strip_item_button);
mStripModel.set(TabProperties.IS_SELECTED, false);
button.performClick();
Assert.assertTrue(mSelectClicked.get());
mSelectClicked.set(false);
mStripModel.set(TabProperties.IS_SELECTED, true);
button.performClick();
Assert.assertFalse(mSelectClicked.get());
mSelectClicked.set(false);
mSelectableModel.set(TabProperties.IS_SELECTED, false);
mSelectableTabGridView.performClick();
Assert.assertTrue(mSelectClicked.get());
mSelectClicked.set(false);
mSelectableModel.set(TabProperties.IS_SELECTED, true);
mSelectableTabGridView.performClick();
Assert.assertTrue(mSelectClicked.get());
}
@Test
@MediumTest
@UiThreadTest
public void testClickToClose() {
ImageView actionButton = mTabGridView.findViewById(R.id.action_button);
ImageButton button = mTabStripView.findViewById(R.id.tab_strip_item_button);
Assert.assertFalse(mCloseClicked.get());
actionButton.performClick();
Assert.assertTrue(mCloseClicked.get());
mCloseClicked.set(false);
int firstCloseId = mCloseTabId.get();
Assert.assertEquals(TAB1_ID, firstCloseId);
mGridModel.set(TabProperties.TAB_ID, TAB2_ID);
actionButton.performClick();
Assert.assertTrue(mCloseClicked.get());
mCloseClicked.set(false);
int secondClosed = mCloseTabId.get();
// When TAB_ID in PropertyModel is updated, binder should close tab with updated tab ID.
Assert.assertEquals(TAB2_ID, secondClosed);
Assert.assertNotEquals(firstCloseId, secondClosed);
mStripModel.set(TabProperties.IS_SELECTED, true);
button.performClick();
Assert.assertTrue(mCloseClicked.get());
mCloseClicked.set(false);
mStripModel.set(TabProperties.IS_SELECTED, false);
button.performClick();
Assert.assertFalse(mCloseClicked.get());
}
@Test
@MediumTest
@UiThreadTest
public void testSetCreateGroupListener() {
ButtonCompat actionButton = mTabGridView.findViewById(R.id.create_group_button);
// By default, the create group button is invisible.
Assert.assertEquals(View.GONE, actionButton.getVisibility());
// When setup with actual listener, the button should be visible.
mGridModel.set(TabProperties.CREATE_GROUP_LISTENER, mMockCreateGroupButtonListener);
Assert.assertEquals(View.VISIBLE, actionButton.getVisibility());
Assert.assertFalse(mCreateGroupButtonClicked.get());
actionButton.performClick();
Assert.assertTrue(mCreateGroupButtonClicked.get());
mCreateGroupButtonClicked.set(false);
int firstCreateGroupId = mCreateGroupTabId.get();
Assert.assertEquals(TAB1_ID, firstCreateGroupId);
mGridModel.set(TabProperties.TAB_ID, TAB2_ID);
actionButton.performClick();
Assert.assertTrue(mCreateGroupButtonClicked.get());
mCreateGroupButtonClicked.set(false);
int secondCreateGroupId = mCreateGroupTabId.get();
// When TAB_ID in PropertyModel is updated, binder should create group with updated tab ID.
Assert.assertEquals(TAB2_ID, secondCreateGroupId);
Assert.assertNotEquals(firstCreateGroupId, secondCreateGroupId);
mGridModel.set(TabProperties.CREATE_GROUP_LISTENER, null);
actionButton.performClick();
Assert.assertFalse(mCreateGroupButtonClicked.get());
// When CREATE_GROUP_LISTENER is set to null, the button should be invisible.
Assert.assertEquals(View.GONE, actionButton.getVisibility());
}
@Override
public void tearDownTest() throws Exception {
mStripMCP.destroy();
mGridMCP.destroy();
mSelectableMCP.destroy();
super.tearDownTest();
}
}