chromium/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabListContainerViewBinderTest.java

// Copyright 2019 The Chromium Authors
// 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.Matchers.allOf;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.hamcrest.MockitoHamcrest.intThat;

import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.MediumTest;

import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Spy;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.Batch;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.R;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
import org.chromium.ui.test.util.BlankUiTestActivity;
import org.chromium.ui.test.util.BlankUiTestActivityTestCase;

/** Tests for {@link TabListRecyclerView} and {@link TabListContainerViewBinder} */
@RunWith(ChromeJUnit4ClassRunner.class)
@Batch(Batch.PER_CLASS)
public class TabListContainerViewBinderTest extends BlankUiTestActivityTestCase {
    private PropertyModel mContainerModel;
    private PropertyModelChangeProcessor mMCP;
    private TabListRecyclerView mRecyclerView;
    @Spy private GridLayoutManager mGridLayoutManager;
    @Spy private LinearLayoutManager mLinearLayoutManager;

    @BeforeClass
    public static void setUpBeforeActivityLaunched() {
        BlankUiTestActivity.setTestLayout(R.layout.tab_list_recycler_view_layout);
    }

    @Override
    public void setUpTest() throws Exception {
        super.setUpTest();

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mRecyclerView = getActivity().findViewById(R.id.tab_list_recycler_view);
                });

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mContainerModel = new PropertyModel(TabListContainerProperties.ALL_KEYS);

                    mMCP =
                            PropertyModelChangeProcessor.create(
                                    mContainerModel,
                                    mRecyclerView,
                                    TabListContainerViewBinder::bind);
                });
    }

    @Override
    public void tearDownTest() throws Exception {
        ThreadUtils.runOnUiThreadBlocking(mMCP::destroy);
        super.tearDownTest();
    }

    private void setUpGridLayoutManager() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mGridLayoutManager = spy(new GridLayoutManager(getActivity(), 2));
                    mRecyclerView.setLayoutManager(mGridLayoutManager);
                });
    }

    private void setUpLinearLayoutManager() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mLinearLayoutManager = spy(new LinearLayoutManager(getActivity()));
                    mRecyclerView.setLayoutManager(mLinearLayoutManager);
                });
    }

    @Test
    @MediumTest
    @UiThreadTest
    public void testSetBottomPadding() {
        int oldLeft = mRecyclerView.getPaddingLeft();
        int oldTop = mRecyclerView.getPaddingTop();
        int oldRight = mRecyclerView.getPaddingRight();
        int oldBottom = mRecyclerView.getPaddingBottom();

        int customBottom = 37;
        mContainerModel.set(TabListContainerProperties.BOTTOM_PADDING, customBottom);

        int left = mRecyclerView.getPaddingLeft();
        int top = mRecyclerView.getPaddingTop();
        int right = mRecyclerView.getPaddingRight();
        int bottom = mRecyclerView.getPaddingBottom();

        assertEquals(oldLeft, left);
        assertEquals(oldTop, top);
        assertEquals(oldRight, right);
        assertNotEquals(oldBottom, customBottom);
        assertEquals(bottom, customBottom);
    }

    @Test
    @MediumTest
    @UiThreadTest
    public void testSetInitialScrollIndex_Grid() {
        setUpGridLayoutManager();
        mRecyclerView.layout(0, 0, 100, 500);

        mContainerModel.set(TabListContainerProperties.MODE, TabListCoordinator.TabListMode.GRID);
        mContainerModel.set(TabListContainerProperties.INITIAL_SCROLL_INDEX, 5);

        // Offset will be view height (500) / 2 - tab card height calculated from TabUtils / 2
        verify(mGridLayoutManager, times(1))
                .scrollToPositionWithOffset(
                        eq(5),
                        intThat(allOf(lessThan(mRecyclerView.getHeight() / 2), greaterThan(0))));
    }

    @Test
    @MediumTest
    @UiThreadTest
    public void testSetInitialScrollIndex_List_NoTabs() {
        setUpLinearLayoutManager();
        mRecyclerView.layout(0, 0, 100, 500);

        mContainerModel.set(TabListContainerProperties.MODE, TabListCoordinator.TabListMode.LIST);
        mContainerModel.set(TabListContainerProperties.INITIAL_SCROLL_INDEX, 7);

        // Offset will be 0 to avoid divide by 0 with no tabs.
        verify(mLinearLayoutManager, times(1)).scrollToPositionWithOffset(eq(7), eq(0));
    }

    @Test
    @MediumTest
    @UiThreadTest
    public void testSetInitialScrollIndex_List_WithTabs() {
        setUpLinearLayoutManager();
        mRecyclerView.layout(0, 0, 100, 500);

        doReturn(9).when(mLinearLayoutManager).getItemCount();
        int range = mRecyclerView.computeVerticalScrollRange();

        mContainerModel.set(TabListContainerProperties.MODE, TabListCoordinator.TabListMode.LIST);
        mContainerModel.set(TabListContainerProperties.INITIAL_SCROLL_INDEX, 5);

        // 9 Tabs at 900 scroll extent = 100 per tab. With view height of 500 the offset is
        // 500 / 2 - range / 9 / 2 = result.
        verify(mLinearLayoutManager, times(1))
                .scrollToPositionWithOffset(eq(5), eq(250 - range / 9 / 2));
    }
}