chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RoundedCornerOutlineProviderUnitTest.java

// Copyright 2021 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.components.browser_ui.widget;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.graphics.Outline;
import android.graphics.Rect;
import android.view.View;

import androidx.test.filters.SmallTest;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;

import org.chromium.base.test.BaseRobolectricTestRunner;

/** Tests for {@link RoundedCornerOutlineProvider}. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class RoundedCornerOutlineProviderUnitTest {
    private static final int VIEW_WIDTH = 120;
    private static final int VIEW_HEIGHT = 50;
    private static final int RADIUS = 10;

    View mView;

    private Outline mOutline;
    private Context mContext;
    private RoundedCornerOutlineProvider mProvider;
    private Rect mRect;

    @Before
    public void setUp() {
        mContext = RuntimeEnvironment.application;

        // Permit RTL tests.
        // Note that without this, all LayoutDirection changes will be ignored Views
        // and Robolectric - by default - does not support RTL mode (needs to be
        // explicitly enabled).
        mContext.getApplicationInfo().flags |= ApplicationInfo.FLAG_SUPPORTS_RTL;

        mView = new View(mContext);
        mView.layout(0, 0, VIEW_WIDTH, VIEW_HEIGHT);
        mProvider = new RoundedCornerOutlineProvider(RADIUS);
        mRect = new Rect();
        mOutline = new Outline();
    }

    @Test
    @SmallTest
    public void verifyCanClipWithRounding() {
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(0, 0, VIEW_WIDTH, VIEW_HEIGHT), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());
    }

    @Test
    @SmallTest
    public void verifyCanClipWithNoRounding() {
        mProvider = new RoundedCornerOutlineProvider();
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(0, 0, VIEW_WIDTH, VIEW_HEIGHT), mRect);
        Assert.assertEquals(0, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());
    }

    @Test
    @SmallTest
    public void verifyRespectsRoundingUpdates() {
        mProvider.setRadius(RADIUS * 3);
        mProvider.getOutline(mView, mOutline);
        Assert.assertEquals(RADIUS * 3, mOutline.getRadius(), 0.001);
        mProvider.setRadius(RADIUS * 2);
        mProvider.getOutline(mView, mOutline);
        Assert.assertEquals(RADIUS * 2, mOutline.getRadius(), 0.001);
        mProvider.setRadius(RADIUS * 5);
        mProvider.getOutline(mView, mOutline);
        Assert.assertEquals(RADIUS * 5, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());
    }

    @Test
    @SmallTest
    public void verifyRespectsPaddings() {
        mView.setPaddingRelative(15, 10, 25, 20);
        mProvider.getOutline(mView, mOutline);

        // Default: no clipping padded area.
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(0, 0, VIEW_WIDTH, VIEW_HEIGHT), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());

        // Variant: clip padded area.
        mProvider.setClipPaddedArea(true);
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(15, 10, VIEW_WIDTH - 25, VIEW_HEIGHT - 20), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());
    }

    @Test
    @SmallTest
    public void verifyRespectsPaddingsInRTLMode() {
        mView.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
        Assert.assertEquals(
                "layout direction not supported",
                View.LAYOUT_DIRECTION_RTL,
                mView.getLayoutDirection());

        mView.setPaddingRelative(15, 10, 25, 20);

        // Default: no clipping padded area.
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(0, 0, VIEW_WIDTH, VIEW_HEIGHT), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());

        // Variant: clip padded area.
        mProvider.setClipPaddedArea(true);
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(25, 10, VIEW_WIDTH - 15, VIEW_HEIGHT - 20), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());
    }

    @Test
    @SmallTest
    public void verifyViewOriginDoesNotImpactOutline() {
        mView.layout(10, 15, 10 + VIEW_WIDTH, 15 + VIEW_HEIGHT);
        mView.setPaddingRelative(15, 10, 25, 20);

        // Default: no clipping padded area.
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(0, 0, VIEW_WIDTH, VIEW_HEIGHT), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());

        // Variant: clip padded area.
        mProvider.setClipPaddedArea(true);
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(15, 10, VIEW_WIDTH - 25, VIEW_HEIGHT - 20), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());
    }

    @Test
    @SmallTest
    public void verifyLeftEdgeExclusion() {
        mView.layout(10, 15, 10 + VIEW_WIDTH, 15 + VIEW_HEIGHT);
        mView.setPaddingRelative(15, 10, 25, 20);
        mProvider.setRoundingEdges(
                /* left= */ false, /* top= */ true, /* right= */ true, /* bottom= */ true);

        // Default: no clipping padded area.
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(-RADIUS, 0, VIEW_WIDTH, VIEW_HEIGHT), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());

        // Variant: clip padded area.
        mProvider.setClipPaddedArea(true);
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(15 - RADIUS, 10, VIEW_WIDTH - 25, VIEW_HEIGHT - 20), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());
    }

    @Test
    @SmallTest
    public void verifyTopEdgeExclusion() {
        mView.layout(10, 15, 10 + VIEW_WIDTH, 15 + VIEW_HEIGHT);
        mView.setPaddingRelative(15, 10, 25, 20);
        mProvider.setRoundingEdges(
                /* left= */ true, /* top= */ false, /* right= */ true, /* bottom= */ true);

        // Default: no clipping padded area.
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(0, -RADIUS, VIEW_WIDTH, VIEW_HEIGHT), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());

        // Variant: clip padded area.
        mProvider.setClipPaddedArea(true);
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(15, 10 - RADIUS, VIEW_WIDTH - 25, VIEW_HEIGHT - 20), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());
    }

    @Test
    @SmallTest
    public void verifyRightEdgeExclusion() {
        mView.layout(10, 15, 10 + VIEW_WIDTH, 15 + VIEW_HEIGHT);
        mView.setPaddingRelative(15, 10, 25, 20);
        mProvider.setRoundingEdges(
                /* left= */ true, /* top= */ true, /* right= */ false, /* bottom= */ true);

        // Default: no clipping padded area.
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(0, 0, VIEW_WIDTH + RADIUS, VIEW_HEIGHT), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());

        // Variant: clip padded area.
        mProvider.setClipPaddedArea(true);
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(15, 10, VIEW_WIDTH - 25 + RADIUS, VIEW_HEIGHT - 20), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());
    }

    @Test
    @SmallTest
    public void verifyBottomEdgeExclusion() {
        mView.layout(10, 15, 10 + VIEW_WIDTH, 15 + VIEW_HEIGHT);
        mView.setPaddingRelative(15, 10, 25, 20);
        mProvider.setRoundingEdges(
                /* left= */ true, /* top= */ true, /* right= */ true, /* bottom= */ false);

        // Default: no clipping padded area.
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(0, 0, VIEW_WIDTH, VIEW_HEIGHT + RADIUS), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());

        // Variant: clip padded area.
        mProvider.setClipPaddedArea(true);
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(15, 10, VIEW_WIDTH - 25, VIEW_HEIGHT - 20 + RADIUS), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());
    }

    @Test
    @SmallTest
    public void verifyRightBottomEdgeExclusion() {
        mView.layout(10, 15, 10 + VIEW_WIDTH, 15 + VIEW_HEIGHT);
        mView.setPaddingRelative(15, 10, 25, 20);
        // Disable rounding near the right and bottom edges.
        // The effect is that only top-left edge is rounded.
        mProvider.setRoundingEdges(
                /* left= */ true, /* top= */ true, /* right= */ false, /* bottom= */ false);

        // Default: no clipping padded area.
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(0, 0, VIEW_WIDTH + RADIUS, VIEW_HEIGHT + RADIUS), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());

        // Variant: clip padded area.
        mProvider.setClipPaddedArea(true);
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(
                new Rect(15, 10, VIEW_WIDTH - 25 + RADIUS, VIEW_HEIGHT - 20 + RADIUS), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());
    }

    @Test
    @SmallTest
    public void verifyTopBottomEdgeExclusion() {
        mView.layout(10, 15, 10 + VIEW_WIDTH, 15 + VIEW_HEIGHT);
        mView.setPaddingRelative(15, 10, 25, 20);
        // Disable rounding near the top and bottom edges.
        // The effect is that rounding is effectively disabled.
        mProvider.setRoundingEdges(
                /* left= */ true, /* top= */ false, /* right= */ true, /* bottom= */ false);

        // Default: no clipping padded area.
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(new Rect(0, -RADIUS, VIEW_WIDTH, VIEW_HEIGHT + RADIUS), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());

        // Variant: clip padded area.
        mProvider.setClipPaddedArea(true);
        mProvider.getOutline(mView, mOutline);
        mOutline.getRect(mRect);
        Assert.assertEquals(
                new Rect(15, 10 - RADIUS, VIEW_WIDTH - 25, VIEW_HEIGHT - 20 + RADIUS), mRect);
        Assert.assertEquals(RADIUS, mOutline.getRadius(), 0.001);
        Assert.assertTrue(mOutline.canClip());
    }
}