1/*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.recyclerview.widget;
18
19import static android.view.View.MeasureSpec.AT_MOST;
20import static android.view.View.MeasureSpec.EXACTLY;
21import static android.view.View.MeasureSpec.UNSPECIFIED;
22import static android.view.View.MeasureSpec.makeMeasureSpec;
23
24import android.graphics.Rect;
25import android.support.test.InstrumentationRegistry;
26import android.support.test.filters.SmallTest;
27import android.view.View;
28
29import org.hamcrest.CoreMatchers;
30import org.hamcrest.MatcherAssert;
31import org.junit.Before;
32import org.junit.Test;
33import org.junit.runner.RunWith;
34import org.junit.runners.Parameterized;
35
36import java.util.Arrays;
37import java.util.List;
38
39@RunWith(Parameterized.class)
40@SmallTest
41public class DefaultMeasureSpecTest {
42    private final int mWSpec;
43    private final int mHSpec;
44    private int mExpectedW;
45    private int mExpectedH;
46    private final Rect mPadding;
47    RecyclerView mRecyclerView;
48
49    @Before
50    public void setUp() throws Exception {
51        mRecyclerView = new RecyclerView(InstrumentationRegistry.getContext());
52        if (mPadding != null) {
53            mRecyclerView.setPadding(mPadding.left, mPadding.top, mPadding.right, mPadding.bottom);
54        }
55    }
56
57    @Parameterized.Parameters(name = "{0}")
58    public static List<Object[]> param() {
59        List<Object[]> params = Arrays.asList(
60                new Object[]{null, makeMeasureSpec(10, EXACTLY), makeMeasureSpec(20, EXACTLY), 10,
61                        20, new Rect(0, 0, 0, 0)},
62                new Object[]{null, makeMeasureSpec(10, EXACTLY), makeMeasureSpec(100, AT_MOST), 10,
63                        0, new Rect(0, 0, 0, 0)},
64                new Object[]{null, makeMeasureSpec(10, AT_MOST), makeMeasureSpec(100, EXACTLY), 0,
65                        100, new Rect(0, 0, 0, 0)},
66                new Object[]{null, makeMeasureSpec(10, EXACTLY), makeMeasureSpec(100, UNSPECIFIED),
67                        10, 0, new Rect(0, 0, 0, 0)},
68                new Object[]{null, makeMeasureSpec(10, UNSPECIFIED), makeMeasureSpec(100, EXACTLY),
69                        0, 100, new Rect(0, 0, 0, 0)},
70                new Object[]{null, makeMeasureSpec(10, EXACTLY), makeMeasureSpec(20, EXACTLY), 10,
71                        20, new Rect(39, 50, 34, 23)},
72                new Object[]{null, makeMeasureSpec(10, EXACTLY), makeMeasureSpec(100, AT_MOST), 10,
73                        50, new Rect(3, 35, 3, 15)},
74                new Object[]{null, makeMeasureSpec(10, EXACTLY), makeMeasureSpec(100, AT_MOST), 10,
75                        100, new Rect(3, 350, 3, 15)},
76
77                new Object[]{null, makeMeasureSpec(10, AT_MOST), makeMeasureSpec(100, EXACTLY), 10,
78                        100, new Rect(15, 500, 5, 30)},
79                new Object[]{null, makeMeasureSpec(10, AT_MOST), makeMeasureSpec(100, EXACTLY), 5,
80                        100, new Rect(3, 500, 2, 30)},
81                new Object[]{null, makeMeasureSpec(10, EXACTLY), makeMeasureSpec(100, UNSPECIFIED),
82                        10, 20, new Rect(500, 15, 30, 5)},
83                new Object[]{null, makeMeasureSpec(10, UNSPECIFIED), makeMeasureSpec(100, EXACTLY),
84                        45, 100, new Rect(15, 400, 30, 5)}
85        );
86        for (Object[] param : params) {
87            param[0] = "width: " + log((Integer) param[1]) + ", height:" + log((Integer) param[2]);
88            param[0] = param[0] + ", padding:" + param[5];
89        }
90        return params;
91    }
92
93    public DefaultMeasureSpecTest(@SuppressWarnings("UnusedParameters") String ignored,
94            int wSpec, int hSpec, int expectedW, int expectedH, Rect padding) {
95        mWSpec = wSpec;
96        mHSpec = hSpec;
97        mExpectedW = expectedW;
98        mExpectedH = expectedH;
99        this.mPadding = padding;
100    }
101
102    @Test
103    public void testWithSmallerMinWidth() {
104        mRecyclerView.setMinimumWidth(Math.max(0, mExpectedW - 5));
105        runTest();
106    }
107
108    @Test
109    public void testWithSmallerMinHeight() {
110        mRecyclerView.setMinimumHeight(Math.max(0, mExpectedH - 5));
111        runTest();
112    }
113
114    @Test
115    public void testWithLargerMinHeight() {
116        mRecyclerView.setMinimumHeight(mExpectedH + 5);
117        int mode = View.MeasureSpec.getMode(mHSpec);
118        switch (mode) {
119            case UNSPECIFIED:
120                mExpectedH += 5;
121                break;
122            case AT_MOST:
123                mExpectedH = Math.min(View.MeasureSpec.getSize(mHSpec), mExpectedH + 5);
124                break;
125        }
126        runTest();
127    }
128
129    @Test
130    public void testWithLargerMinWidth() {
131        mRecyclerView.setMinimumWidth(mExpectedW + 5);
132        int mode = View.MeasureSpec.getMode(mWSpec);
133        switch (mode) {
134            case UNSPECIFIED:
135                mExpectedW += 5;
136                break;
137            case AT_MOST:
138                mExpectedW = Math.min(View.MeasureSpec.getSize(mWSpec), mExpectedW + 5);
139                break;
140        }
141        runTest();
142    }
143
144    @Test
145    public void runTest() {
146        mRecyclerView.defaultOnMeasure(mWSpec, mHSpec);
147        MatcherAssert.assertThat("measured width", mRecyclerView.getMeasuredWidth(),
148                CoreMatchers.is(mExpectedW));
149        MatcherAssert.assertThat("measured height", mRecyclerView.getMeasuredHeight(),
150                CoreMatchers.is(mExpectedH));
151    }
152
153    private static String log(int spec) {
154        final int size = View.MeasureSpec.getSize(spec);
155        int mode = View.MeasureSpec.getMode(spec);
156        if (mode == View.MeasureSpec.AT_MOST) {
157            return "at most " + size;
158        }
159        if (mode == View.MeasureSpec.UNSPECIFIED) {
160            return "unspecified " + size;
161        }
162        if (mode == View.MeasureSpec.EXACTLY) {
163            return "exactly " + size;
164        }
165        return "?? " + size;
166    }
167}
168