chromium/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/SimpleHorizontalLayoutViewTest.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.omnibox.suggestions.base;

import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.view.View.MeasureSpec;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;

import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.omnibox.suggestions.base.SimpleHorizontalLayoutView.LayoutParams;

/** Tests for {@link SimpleHorizontalLayoutView}. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class SimpleHorizontalLayoutViewTest {
    private static final int SMALL_VIEW_WIDTH = 50;
    private static final int LARGE_VIEW_WIDTH = 120;

    private SimpleHorizontalLayoutViewForTest mView;
    private Activity mActivity;

    private View mSmallView;
    private View mLargeView;
    private View mDynamicView;

    class SimpleHorizontalLayoutViewForTest extends SimpleHorizontalLayoutView {
        private int mCurrentDirection = View.LAYOUT_DIRECTION_LTR;

        SimpleHorizontalLayoutViewForTest(Context context) {
            super(context);
        }

        @Override
        public void setLayoutDirection(int newDirection) {
            mCurrentDirection = newDirection;
        }

        @Override
        public int getLayoutDirection() {
            return mCurrentDirection;
        }

        /** Test method to force layout update based on specified view dimensions. */
        void performLayoutForTest(int width) {
            onMeasure(
                    MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                    MeasureSpec.UNSPECIFIED);

            // Note: height is computed by onMeasure call.
            final int height = getMeasuredHeight();
            onLayout(true, 0, 0, width, height);
        }
    }

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

        mActivity = Robolectric.buildActivity(Activity.class).setup().get();
        mView = new SimpleHorizontalLayoutViewForTest(mActivity);

        mSmallView = new View(mActivity);
        mSmallView.setLayoutParams(new LayoutParams(SMALL_VIEW_WIDTH, LayoutParams.MATCH_PARENT));
        mLargeView = new View(mActivity);
        mLargeView.setLayoutParams(new LayoutParams(LARGE_VIEW_WIDTH, LayoutParams.MATCH_PARENT));
        mDynamicView = new View(mActivity);
        mDynamicView.setLayoutParams(LayoutParams.forDynamicView());
    }

    /**
     * Perform the measure and layout pass on the SimpleHorizontalLayoutView. This method sets up
     * the basic properties of the Suggestion container, specifies height of the content view and
     * executes the measure and layout pass.
     */
    private void executeLayoutTest(int containerWidth, int contentHeight, int layoutDirection) {
        mView.setLayoutDirection(layoutDirection);
        Assert.assertEquals(
                "layout direction not supported", layoutDirection, mView.getLayoutDirection());

        // Let ContentView drive the height of the Suggestion. The dummy view could shrink, so let's
        // prevent that from happening. We don't technically have any content, so we need to prevent
        // the view from shrinking, too.
        mDynamicView.setMinimumHeight(contentHeight);

        mView.performLayoutForTest(containerWidth);
    }

    /** Confirm that specified view is positioned at specific coordinates. */
    private void verifyViewLayout(View v, int left, int top, int right, int bottom) {
        Assert.assertEquals("left view edge", left, v.getLeft());
        Assert.assertEquals("top view edge", top, v.getTop());
        Assert.assertEquals("right view edge", right, v.getRight());
        Assert.assertEquals("bottom view edge", bottom, v.getBottom());
        Assert.assertEquals("view width", right - left, v.getMeasuredWidth());
        Assert.assertEquals("view height", bottom - top, v.getMeasuredHeight());
    }

    /**
     * LTR layout with dynamic view in the middle.
     *
     * <pre>
     * [   LARGE   | DYNAMIC         |SMALL]
     * </pre>
     */
    @Test
    public void layout_LtrWithDynamicInbetween() {
        final int useContentWidth = 123;
        final int giveSuggestionWidth = SMALL_VIEW_WIDTH + LARGE_VIEW_WIDTH + useContentWidth;
        final int giveContentHeight = 15;

        mView.addView(mLargeView);
        mView.addView(mDynamicView);
        mView.addView(mSmallView);

        final int expectedLargeCornerLeft = 0;
        final int expectedLargeCornerRight = LARGE_VIEW_WIDTH;
        final int expectedDynamicCornerLeft = expectedLargeCornerRight;
        final int expectedDynamicCornerRight = expectedDynamicCornerLeft + useContentWidth;
        final int expectedSmallCornerLeft = expectedDynamicCornerRight;
        final int expectedSmallCornerRight = expectedSmallCornerLeft + SMALL_VIEW_WIDTH;

        executeLayoutTest(giveSuggestionWidth, giveContentHeight, View.LAYOUT_DIRECTION_LTR);

        verifyViewLayout(
                mLargeView,
                expectedLargeCornerLeft,
                0,
                expectedLargeCornerRight,
                giveContentHeight);

        verifyViewLayout(
                mDynamicView,
                expectedDynamicCornerLeft,
                0,
                expectedDynamicCornerRight,
                giveContentHeight);

        verifyViewLayout(
                mSmallView,
                expectedSmallCornerLeft,
                0,
                expectedSmallCornerRight,
                giveContentHeight);
    }

    /**
     * RTL layout with dynamic view in the middle.
     *
     * <pre>
     * [SMALL| DYNAMIC         |   LARGE   ]
     * </pre>
     */
    @Test
    public void layout_RtlWithDynamicInbetween() {
        final int useContentWidth = 234;
        final int giveSuggestionWidth = SMALL_VIEW_WIDTH + LARGE_VIEW_WIDTH + useContentWidth;
        final int giveContentHeight = 18;

        mView.addView(mLargeView);
        mView.addView(mDynamicView);
        mView.addView(mSmallView);

        final int expectedSmallCornerLeft = 0;
        final int expectedSmallCornerRight = SMALL_VIEW_WIDTH;
        final int expectedDynamicCornerLeft = expectedSmallCornerRight;
        final int expectedDynamicCornerRight = expectedDynamicCornerLeft + useContentWidth;
        final int expectedLargeCornerLeft = expectedDynamicCornerRight;
        final int expectedLargeCornerRight = expectedLargeCornerLeft + LARGE_VIEW_WIDTH;

        executeLayoutTest(giveSuggestionWidth, giveContentHeight, View.LAYOUT_DIRECTION_RTL);

        verifyViewLayout(
                mLargeView,
                expectedLargeCornerLeft,
                0,
                expectedLargeCornerRight,
                giveContentHeight);

        verifyViewLayout(
                mDynamicView,
                expectedDynamicCornerLeft,
                0,
                expectedDynamicCornerRight,
                giveContentHeight);

        verifyViewLayout(
                mSmallView,
                expectedSmallCornerLeft,
                0,
                expectedSmallCornerRight,
                giveContentHeight);
    }

    /**
     * LTR layout with dynamic view in the middle, and first fixed-size view element hidden.
     *
     * <pre>
     * [DYNAMIC          |SMALL]
     * </pre>
     */
    @Test
    public void layout_LtrWithDynamicInbetween_FirstViewHidden() {
        final int useContentWidth = 123;
        final int giveSuggestionWidth = SMALL_VIEW_WIDTH + useContentWidth;
        final int giveContentHeight = 15;

        mLargeView.setVisibility(View.GONE);
        mView.addView(mLargeView);
        mView.addView(mDynamicView);
        mView.addView(mSmallView);

        final int expectedDynamicCornerLeft = 0;
        final int expectedDynamicCornerRight = expectedDynamicCornerLeft + useContentWidth;
        final int expectedSmallCornerLeft = expectedDynamicCornerRight;
        final int expectedSmallCornerRight = expectedSmallCornerLeft + SMALL_VIEW_WIDTH;

        executeLayoutTest(giveSuggestionWidth, giveContentHeight, View.LAYOUT_DIRECTION_LTR);

        verifyViewLayout(
                mDynamicView,
                expectedDynamicCornerLeft,
                0,
                expectedDynamicCornerRight,
                giveContentHeight);

        verifyViewLayout(
                mSmallView,
                expectedSmallCornerLeft,
                0,
                expectedSmallCornerRight,
                giveContentHeight);
    }

    /**
     * RTL layout with dynamic view in the middle, and last fixed-size view element hidden.
     *
     * <pre>
     * [DYNAMIC          |   LARGE   ]
     * </pre>
     */
    @Test
    public void layout_RtlWithDynamicInbetween_LastViewHidden() {
        final int useContentWidth = 234;
        final int giveSuggestionWidth = LARGE_VIEW_WIDTH + useContentWidth;
        final int giveContentHeight = 18;

        mSmallView.setVisibility(View.GONE);
        mView.addView(mLargeView);
        mView.addView(mDynamicView);
        mView.addView(mSmallView);

        final int expectedDynamicCornerLeft = 0;
        final int expectedDynamicCornerRight = expectedDynamicCornerLeft + useContentWidth;
        final int expectedLargeCornerLeft = expectedDynamicCornerRight;
        final int expectedLargeCornerRight = expectedLargeCornerLeft + LARGE_VIEW_WIDTH;

        executeLayoutTest(giveSuggestionWidth, giveContentHeight, View.LAYOUT_DIRECTION_RTL);

        verifyViewLayout(
                mLargeView,
                expectedLargeCornerLeft,
                0,
                expectedLargeCornerRight,
                giveContentHeight);

        verifyViewLayout(
                mDynamicView,
                expectedDynamicCornerLeft,
                0,
                expectedDynamicCornerRight,
                giveContentHeight);
    }

    /**
     * LTR layout with dynamic view positioned first.
     *
     * <pre>
     * [ DYNAMIC         |   LARGE   |SMALL]
     * </pre>
     */
    @Test
    public void layout_LtrWithDynamicFirst() {
        final int useContentWidth = 135;
        final int giveSuggestionWidth = SMALL_VIEW_WIDTH + LARGE_VIEW_WIDTH + useContentWidth;
        final int giveContentHeight = 25;

        mView.addView(mDynamicView);
        mView.addView(mLargeView);
        mView.addView(mSmallView);

        final int expectedDynamicCornerLeft = 0;
        final int expectedDynamicCornerRight = useContentWidth;
        final int expectedLargeCornerLeft = expectedDynamicCornerRight;
        final int expectedLargeCornerRight = expectedLargeCornerLeft + LARGE_VIEW_WIDTH;
        final int expectedSmallCornerLeft = expectedLargeCornerRight;
        final int expectedSmallCornerRight = expectedSmallCornerLeft + SMALL_VIEW_WIDTH;

        executeLayoutTest(giveSuggestionWidth, giveContentHeight, View.LAYOUT_DIRECTION_LTR);

        verifyViewLayout(
                mLargeView,
                expectedLargeCornerLeft,
                0,
                expectedLargeCornerRight,
                giveContentHeight);

        verifyViewLayout(
                mDynamicView,
                expectedDynamicCornerLeft,
                0,
                expectedDynamicCornerRight,
                giveContentHeight);

        verifyViewLayout(
                mSmallView,
                expectedSmallCornerLeft,
                0,
                expectedSmallCornerRight,
                giveContentHeight);
    }

    /**
     * RTL layout with dynamic view positioned first
     *
     * <pre>
     * [   LARGE   |SMALL| DYNAMIC         ]
     * </pre>
     */
    @Test
    public void layout_RtlWithDynamicFirst() {
        final int useContentWidth = 246;
        final int giveSuggestionWidth = SMALL_VIEW_WIDTH + LARGE_VIEW_WIDTH + useContentWidth;
        final int giveContentHeight = 28;

        mView.addView(mDynamicView);
        mView.addView(mSmallView);
        mView.addView(mLargeView);

        final int expectedLargeCornerLeft = 0;
        final int expectedLargeCornerRight = LARGE_VIEW_WIDTH;
        final int expectedSmallCornerLeft = expectedLargeCornerRight;
        final int expectedSmallCornerRight = expectedSmallCornerLeft + SMALL_VIEW_WIDTH;
        final int expectedDynamicCornerLeft = expectedSmallCornerRight;
        final int expectedDynamicCornerRight = expectedDynamicCornerLeft + useContentWidth;

        executeLayoutTest(giveSuggestionWidth, giveContentHeight, View.LAYOUT_DIRECTION_RTL);

        verifyViewLayout(
                mLargeView,
                expectedLargeCornerLeft,
                0,
                expectedLargeCornerRight,
                giveContentHeight);

        verifyViewLayout(
                mDynamicView,
                expectedDynamicCornerLeft,
                0,
                expectedDynamicCornerRight,
                giveContentHeight);

        verifyViewLayout(
                mSmallView,
                expectedSmallCornerLeft,
                0,
                expectedSmallCornerRight,
                giveContentHeight);
    }

    /**
     * LTR layout with dynamic view positioned last.
     *
     * <pre>
     * [   LARGE   |SMALL| DYNAMIC         ]
     * </pre>
     */
    @Test
    public void layout_LtrWithDynamicLast() {
        final int useContentWidth = 147;
        final int giveSuggestionWidth = SMALL_VIEW_WIDTH + LARGE_VIEW_WIDTH + useContentWidth;
        final int giveContentHeight = 17;

        mView.addView(mLargeView);
        mView.addView(mSmallView);
        mView.addView(mDynamicView);

        final int expectedLargeCornerLeft = 0;
        final int expectedLargeCornerRight = LARGE_VIEW_WIDTH;
        final int expectedSmallCornerLeft = expectedLargeCornerRight;
        final int expectedSmallCornerRight = expectedSmallCornerLeft + SMALL_VIEW_WIDTH;
        final int expectedDynamicCornerLeft = expectedSmallCornerRight;
        final int expectedDynamicCornerRight = expectedDynamicCornerLeft + useContentWidth;

        executeLayoutTest(giveSuggestionWidth, giveContentHeight, View.LAYOUT_DIRECTION_LTR);

        verifyViewLayout(
                mLargeView,
                expectedLargeCornerLeft,
                0,
                expectedLargeCornerRight,
                giveContentHeight);

        verifyViewLayout(
                mDynamicView,
                expectedDynamicCornerLeft,
                0,
                expectedDynamicCornerRight,
                giveContentHeight);

        verifyViewLayout(
                mSmallView,
                expectedSmallCornerLeft,
                0,
                expectedSmallCornerRight,
                giveContentHeight);
    }

    /**
     * RTL layout with dynamic view positioned last.
     *
     * <pre>
     * [ DYNAMIC         |   LARGE   |SMALL]
     * </pre>
     */
    @Test
    public void layout_RtlWithDynamicLast() {
        final int useContentWidth = 258;
        final int giveSuggestionWidth = SMALL_VIEW_WIDTH + LARGE_VIEW_WIDTH + useContentWidth;
        final int giveContentHeight = 27;

        mView.addView(mSmallView);
        mView.addView(mLargeView);
        mView.addView(mDynamicView);

        final int expectedDynamicCornerLeft = 0;
        final int expectedDynamicCornerRight = useContentWidth;
        final int expectedLargeCornerLeft = expectedDynamicCornerRight;
        final int expectedLargeCornerRight = expectedLargeCornerLeft + LARGE_VIEW_WIDTH;
        final int expectedSmallCornerLeft = expectedLargeCornerRight;
        final int expectedSmallCornerRight = expectedSmallCornerLeft + SMALL_VIEW_WIDTH;

        executeLayoutTest(giveSuggestionWidth, giveContentHeight, View.LAYOUT_DIRECTION_RTL);

        verifyViewLayout(
                mLargeView,
                expectedLargeCornerLeft,
                0,
                expectedLargeCornerRight,
                giveContentHeight);

        verifyViewLayout(
                mDynamicView,
                expectedDynamicCornerLeft,
                0,
                expectedDynamicCornerRight,
                giveContentHeight);

        verifyViewLayout(
                mSmallView,
                expectedSmallCornerLeft,
                0,
                expectedSmallCornerRight,
                giveContentHeight);
    }

    /** Verify that start padding is respected during layout. */
    @Test
    public void layout_LtrPaddingLeft() {
        final int contentWidth = 250;
        final int contentHeight = 48;
        final int paddingWidth = 18;
        final int suggestionWidth = paddingWidth + SMALL_VIEW_WIDTH + contentWidth;

        mView.setPaddingRelative(paddingWidth, 0, 0, 0);

        mView.addView(mSmallView);
        mView.addView(mDynamicView);

        final int expectedSmallCornerLeft = paddingWidth;
        final int expectedSmallCornerRight = expectedSmallCornerLeft + SMALL_VIEW_WIDTH;
        final int expectedDynamicCornerLeft = expectedSmallCornerRight;
        final int expectedDynamicCornerRight = expectedDynamicCornerLeft + contentWidth;

        executeLayoutTest(suggestionWidth, contentHeight, View.LAYOUT_DIRECTION_LTR);

        verifyViewLayout(
                mSmallView, expectedSmallCornerLeft, 0, expectedSmallCornerRight, contentHeight);
        verifyViewLayout(
                mDynamicView,
                expectedDynamicCornerLeft,
                0,
                expectedDynamicCornerRight,
                contentHeight);
    }

    /** Verify that end padding is respected during layout. */
    @Test
    public void layout_LtrPaddingEnd() {
        final int contentWidth = 250;
        final int contentHeight = 48;
        final int paddingWidth = 18;
        final int suggestionWidth = paddingWidth + SMALL_VIEW_WIDTH + contentWidth;

        mView.setPaddingRelative(0, 0, paddingWidth, 0);

        mView.addView(mSmallView);
        mView.addView(mDynamicView);

        final int expectedSmallCornerLeft = 0;
        final int expectedSmallCornerRight = SMALL_VIEW_WIDTH;
        final int expectedDynamicCornerLeft = expectedSmallCornerRight;
        final int expectedDynamicCornerRight = expectedDynamicCornerLeft + contentWidth;

        executeLayoutTest(suggestionWidth, contentHeight, View.LAYOUT_DIRECTION_LTR);

        verifyViewLayout(
                mSmallView, expectedSmallCornerLeft, 0, expectedSmallCornerRight, contentHeight);
        verifyViewLayout(
                mDynamicView,
                expectedDynamicCornerLeft,
                0,
                expectedDynamicCornerRight,
                contentHeight);
    }

    /** Verify that start padding is respected during layout / RTL. */
    @Test
    public void layout_RtlPaddingLeft() {
        final int contentWidth = 250;
        final int contentHeight = 48;
        final int paddingWidth = 18;
        final int suggestionWidth = paddingWidth + SMALL_VIEW_WIDTH + contentWidth;

        mView.addView(mSmallView);
        mView.addView(mDynamicView);

        final int expectedDynamicCornerLeft = 0;
        final int expectedDynamicCornerRight = contentWidth;
        final int expectedSmallCornerLeft = expectedDynamicCornerRight;
        final int expectedSmallCornerRight = expectedSmallCornerLeft + SMALL_VIEW_WIDTH;

        mView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
        mView.setPaddingRelative(paddingWidth, 0, 0, 0);
        executeLayoutTest(suggestionWidth, contentHeight, View.LAYOUT_DIRECTION_RTL);

        verifyViewLayout(
                mSmallView, expectedSmallCornerLeft, 0, expectedSmallCornerRight, contentHeight);
        verifyViewLayout(
                mDynamicView,
                expectedDynamicCornerLeft,
                0,
                expectedDynamicCornerRight,
                contentHeight);
    }

    /** Verify that end padding is respected during layout / RTL. */
    @Test
    public void layout_RtlPaddingEnd() {
        final int contentWidth = 250;
        final int contentHeight = 48;
        final int paddingWidth = 18;
        final int suggestionWidth = paddingWidth + SMALL_VIEW_WIDTH + contentWidth;

        mView.addView(mSmallView);
        mView.addView(mDynamicView);

        final int expectedDynamicCornerLeft = paddingWidth;
        final int expectedDynamicCornerRight = expectedDynamicCornerLeft + contentWidth;
        final int expectedSmallCornerLeft = expectedDynamicCornerRight;
        final int expectedSmallCornerRight = expectedSmallCornerLeft + SMALL_VIEW_WIDTH;

        mView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
        mView.setPaddingRelative(0, 0, paddingWidth, 0);
        executeLayoutTest(suggestionWidth, contentHeight, View.LAYOUT_DIRECTION_RTL);

        verifyViewLayout(
                mSmallView, expectedSmallCornerLeft, 0, expectedSmallCornerRight, contentHeight);
        verifyViewLayout(
                mDynamicView,
                expectedDynamicCornerLeft,
                0,
                expectedDynamicCornerRight,
                contentHeight);
    }

    /** Two dynamic views. Expect the layout mechanism to fail. */
    @Test(expected = AssertionError.class)
    public void layout_MultipleDynamicViews() {
        View dynamicView2 = new View(mActivity);
        dynamicView2.setLayoutParams(LayoutParams.forDynamicView());

        mView.addView(mDynamicView);
        mView.addView(dynamicView2);

        executeLayoutTest(100, 100, View.LAYOUT_DIRECTION_LTR);
    }

    /** No dynamic views. Expect the layout mechanism to fail. */
    @Test(expected = AssertionError.class)
    public void layout_NoDynamicViews() {
        View dynamicView2 = new View(mActivity);
        mView.addView(mLargeView);
        mView.addView(mSmallView);

        executeLayoutTest(100, 100, View.LAYOUT_DIRECTION_LTR);
    }
}