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

// Copyright 2024 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.chromium.chrome.browser.tasks.tab_management.ColorPickerItemViewBinder.SELECTION_LAYER;

import android.app.Activity;
import android.graphics.drawable.LayerDrawable;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;

import androidx.test.filters.MediumTest;

import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.BaseActivityTestRule;
import org.chromium.base.test.params.ParameterAnnotations;
import org.chromium.base.test.params.ParameterSet;
import org.chromium.base.test.params.ParameterizedRunner;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.Criteria;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.tasks.tab_management.ColorPickerCoordinator.ColorPickerLayoutType;
import org.chromium.chrome.test.ChromeJUnit4RunnerDelegate;
import org.chromium.chrome.test.R;
import org.chromium.chrome.test.util.ChromeRenderTestRule;
import org.chromium.ui.base.ViewUtils;
import org.chromium.ui.test.util.BlankUiTestActivity;
import org.chromium.ui.test.util.NightModeTestUtils;
import org.chromium.ui.test.util.RenderTestRule;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/** Integration and render tests for the ColorPicker feature. */
@RunWith(ParameterizedRunner.class)
@ParameterAnnotations.UseRunnerDelegate(ChromeJUnit4RunnerDelegate.class)
@Batch(Batch.PER_CLASS)
public class TabGroupColorPickerTest {
    @ParameterAnnotations.ClassParameter
    public static List<ParameterSet> sClassParams =
            new NightModeTestUtils.NightModeParams().getParameters();

    @Rule
    public final ChromeRenderTestRule mRenderTestRule =
            ChromeRenderTestRule.Builder.withPublicCorpus()
                    .setBugComponent(RenderTestRule.Component.UI_BROWSER_MOBILE_TAB_GROUPS)
                    .setRevision(2)
                    .build();

    @Rule
    public BaseActivityTestRule<BlankUiTestActivity> mActivityTestRule =
            new BaseActivityTestRule<>(BlankUiTestActivity.class);

    private ColorPickerCoordinator mCoordinator;
    private ColorPickerContainer mContainerView;
    private FrameLayout mRootView;
    private List<Integer> mColorList;

    public TabGroupColorPickerTest(boolean nightModeEnabled) {
        NightModeTestUtils.setUpNightModeForBlankUiTestActivity(nightModeEnabled);
        mRenderTestRule.setNightModeEnabled(nightModeEnabled);
    }

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mActivityTestRule.launchActivity(null);

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    int selectedIndex = 1;
                    int totalColorCount = 9;
                    Activity activity = mActivityTestRule.getActivity();

                    List<Integer> colors = new ArrayList<>();
                    for (int i = 0; i < totalColorCount; i++) {
                        colors.add(i);
                    }

                    mCoordinator =
                            new ColorPickerCoordinator(
                                    mActivityTestRule.getActivity(),
                                    colors,
                                    LayoutInflater.from(mActivityTestRule.getActivity())
                                            .inflate(
                                                    R.layout.tab_group_color_picker_container,
                                                    /* root= */ null),
                                    ColorPickerType.TAB_GROUP,
                                    false,
                                    ColorPickerLayoutType.DYNAMIC,
                                    null);
                    mCoordinator.setSelectedColorItem(colors.get(selectedIndex));
                    mContainerView = (ColorPickerContainer) mCoordinator.getContainerView();
                    mColorList = colors;

                    mRootView = new FrameLayout(activity);
                    activity.setContentView(mRootView);
                });
    }

    @Test
    @MediumTest
    public void testColorPicker_forceSingleRow() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mContainerView.setColorPickerLayoutType(ColorPickerLayoutType.SINGLE_ROW);
                    mRootView.addView(mContainerView);
                });

        // Change the width of the parent view to restrict for a double row
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);
                    int containerWidthPx =
                            firstRow.getChildAt(0).getMeasuredWidth() * (mColorList.size() - 1);

                    ViewGroup.LayoutParams params = mRootView.getLayoutParams();
                    params.width = containerWidthPx;
                    mRootView.setLayoutParams(params);
                });

        // Enforce that the change was made to the parent view of the container
        CriteriaHelper.pollUiThread(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);
                    int containerWidthPx =
                            firstRow.getChildAt(0).getMeasuredWidth() * (mColorList.size() - 1);

                    // Refresh the layout and re-measure the widths
                    ViewUtils.requestLayout(
                            mContainerView, "TabGroupColorPicker.TestForceSingleRow");
                    Criteria.checkThat(
                            "Width was not set properly",
                            mRootView.getMeasuredWidth(),
                            Matchers.is(containerWidthPx));
                });

        // Validate that a row split was not performed
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);

                    Assert.assertEquals(mColorList.size(), firstRow.getChildCount());
                });

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mContainerView.setColorPickerLayoutType(ColorPickerLayoutType.DYNAMIC);
                });
    }

    @Test
    @MediumTest
    public void testColorPicker_forceDoubleRow() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mContainerView.setColorPickerLayoutType(ColorPickerLayoutType.DOUBLE_ROW);
                    mRootView.addView(mContainerView);
                });

        // Change the width of the parent view to allow for a single row
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);
                    int containerWidthPx =
                            firstRow.getChildAt(0).getMeasuredWidth() * mColorList.size();

                    ViewGroup.LayoutParams params = mRootView.getLayoutParams();
                    params.width = containerWidthPx;
                    mRootView.setLayoutParams(params);
                });

        // Enforce that the change was made to the parent view of the container
        CriteriaHelper.pollUiThread(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);
                    int containerWidthPx =
                            firstRow.getChildAt(0).getMeasuredWidth() * mColorList.size();

                    // Refresh the layout and re-measure the widths
                    ViewUtils.requestLayout(
                            mContainerView, "TabGroupColorPicker.TestForceDoubleRow");
                    Criteria.checkThat(
                            "Width was not set properly",
                            mRootView.getMeasuredWidth(),
                            Matchers.is(containerWidthPx));
                });

        // Validate that a row split was performed
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);
                    LinearLayout secondRow =
                            mContainerView.findViewById(R.id.color_picker_second_row);

                    Assert.assertEquals((mColorList.size() + 1) / 2, firstRow.getChildCount());
                    Assert.assertEquals(mColorList.size() / 2, secondRow.getChildCount());
                });

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mContainerView.setColorPickerLayoutType(ColorPickerLayoutType.DYNAMIC);
                });
    }

    @Test
    @MediumTest
    public void testColorPicker_dynamicSingleRow() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mRootView.addView(mContainerView);
                });

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    int selectedIndex = 1;
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);
                    Assert.assertEquals(mColorList.size(), firstRow.getChildCount());

                    for (int color : mColorList) {
                        FrameLayout colorView = (FrameLayout) firstRow.getChildAt(color);
                        ImageView imageView = colorView.findViewById(R.id.color_picker_icon);
                        LayerDrawable layerDrawable = (LayerDrawable) imageView.getBackground();

                        // Check that the default item's selection layer indicates a selection.
                        if (color == mColorList.get(selectedIndex)) {
                            Assert.assertEquals(
                                    0xFF, layerDrawable.getDrawable(SELECTION_LAYER).getAlpha());
                        } else {
                            Assert.assertEquals(
                                    0, layerDrawable.getDrawable(SELECTION_LAYER).getAlpha());
                        }
                    }
                });
    }

    @Test
    @MediumTest
    public void testColorPicker_dynamicAlternateSelection() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mRootView.addView(mContainerView);
                });

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    int selectedIndex = 0;
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);

                    // Mock a click on a new color item.
                    mCoordinator.setSelectedColorItem(mColorList.get(selectedIndex));

                    for (int color : mColorList) {
                        FrameLayout colorView = (FrameLayout) firstRow.getChildAt(color);
                        ImageView imageView = colorView.findViewById(R.id.color_picker_icon);
                        LayerDrawable layerDrawable = (LayerDrawable) imageView.getBackground();

                        // Check that the default item's selection layer indicates a selection.
                        if (color == mColorList.get(selectedIndex)) {
                            Assert.assertEquals(
                                    0xFF, layerDrawable.getDrawable(SELECTION_LAYER).getAlpha());
                        } else {
                            Assert.assertEquals(
                                    0, layerDrawable.getDrawable(SELECTION_LAYER).getAlpha());
                        }
                    }
                });
    }

    @Test
    @MediumTest
    public void testColorPicker_dynamicDoubleRow() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mRootView.addView(mContainerView);
                });

        // Change the width of the parent view to enact a row split on the colors
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);
                    int containerWidthPx =
                            firstRow.getChildAt(0).getMeasuredWidth() * (mColorList.size() - 1);

                    ViewGroup.LayoutParams params = mRootView.getLayoutParams();
                    params.width = containerWidthPx;
                    mRootView.setLayoutParams(params);
                });

        // Enforce that the change was made to the parent view of the container
        CriteriaHelper.pollUiThread(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);
                    int containerWidthPx =
                            firstRow.getChildAt(0).getMeasuredWidth() * (mColorList.size() - 1);

                    // Refresh the layout and re-measure the widths
                    ViewUtils.requestLayout(mContainerView, "TabGroupColorPicker.TestDoubleRow");
                    Criteria.checkThat(
                            "Width was not set properly",
                            mRootView.getMeasuredWidth(),
                            Matchers.is(containerWidthPx));
                });

        // Validate that a row split was performed
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);
                    LinearLayout secondRow =
                            mContainerView.findViewById(R.id.color_picker_second_row);

                    Assert.assertEquals((mColorList.size() + 1) / 2, firstRow.getChildCount());
                    Assert.assertEquals(mColorList.size() / 2, secondRow.getChildCount());
                });

        // Change the width of the parent view to enact a single row on the colors
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);
                    int containerWidthPx =
                            firstRow.getChildAt(0).getMeasuredWidth() * mColorList.size();

                    ViewGroup.LayoutParams params = mRootView.getLayoutParams();
                    params.width = containerWidthPx;
                    mRootView.setLayoutParams(params);
                });

        // Enforce that the change was made to the parent view of the container
        CriteriaHelper.pollUiThread(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);
                    int containerWidthPx =
                            firstRow.getChildAt(0).getMeasuredWidth() * mColorList.size();

                    // Refresh the layout and re-measure the widths
                    ViewUtils.requestLayout(mContainerView, "TabGroupColorPicker.TestDoubleRow");
                    Criteria.checkThat(
                            "Width was not set properly",
                            mRootView.getMeasuredWidth(),
                            Matchers.is(containerWidthPx));
                });

        // Validate that a single row was returned to
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);

                    Assert.assertEquals(mColorList.size(), firstRow.getChildCount());
                });
    }

    @Test
    @MediumTest
    @Feature("RenderTest")
    public void testColorPicker_singleRowRender() throws IOException {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mRootView.addView(mContainerView);
                });

        mRenderTestRule.render(mRootView, "tab_group_color_picker_single_row");
    }

    @Test
    @MediumTest
    @Feature("RenderTest")
    public void testColorPicker_doubleRowRender() throws IOException {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mRootView.addView(mContainerView);
                });

        // Change the width of the parent view to enact a row split on the colors
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);
                    int containerWidthPx =
                            firstRow.getChildAt(0).getMeasuredWidth() * (mColorList.size() - 1);

                    ViewGroup.LayoutParams params = mRootView.getLayoutParams();
                    params.width = containerWidthPx;
                    mRootView.setLayoutParams(params);
                });

        // Enforce that the change was made to the parent view of the container
        CriteriaHelper.pollUiThread(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);
                    int containerWidthPx =
                            firstRow.getChildAt(0).getMeasuredWidth() * (mColorList.size() - 1);

                    // Refresh the layout and re-measure the widths
                    ViewUtils.requestLayout(mContainerView, "TabGroupColorPicker.TestDoubleRow");
                    Criteria.checkThat(
                            "Width was not set properly",
                            mRootView.getMeasuredWidth(),
                            Matchers.is(containerWidthPx));
                });

        mRenderTestRule.render(mRootView, "tab_group_color_picker_double_row");

        // Change the width of the parent view to enact a single row on the colors
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);
                    int containerWidthPx =
                            firstRow.getChildAt(0).getMeasuredWidth() * mColorList.size();

                    ViewGroup.LayoutParams params = mRootView.getLayoutParams();
                    params.width = containerWidthPx;
                    mRootView.setLayoutParams(params);
                });

        // Enforce that the change was made to the parent view of the container
        CriteriaHelper.pollUiThread(
                () -> {
                    LinearLayout firstRow =
                            mContainerView.findViewById(R.id.color_picker_first_row);
                    int containerWidthPx =
                            firstRow.getChildAt(0).getMeasuredWidth() * mColorList.size();

                    // Refresh the layout and re-measure the widths
                    ViewUtils.requestLayout(mContainerView, "TabGroupColorPicker.TestDoubleRow");
                    Criteria.checkThat(
                            "Width was not set properly",
                            mRootView.getMeasuredWidth(),
                            Matchers.is(containerWidthPx));
                });
    }
}