[go: nahoru, domu]

1/*
2 * Copyright (C) 2015 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 android.support.design.testutils;
18
19import android.graphics.Color;
20import android.graphics.Rect;
21import android.graphics.drawable.Drawable;
22import android.support.annotation.ColorInt;
23import android.support.design.widget.FloatingActionButton;
24import android.support.test.espresso.matcher.BoundedMatcher;
25import android.support.v4.view.ViewCompat;
26import android.view.View;
27import android.view.ViewParent;
28import android.widget.TextView;
29
30import org.hamcrest.Description;
31import org.hamcrest.Matcher;
32import org.hamcrest.TypeSafeMatcher;
33
34public class TestUtilsMatchers {
35    /**
36     * Returns a matcher that matches Views that are not narrower than specified width in pixels.
37     */
38    public static Matcher<View> isNotNarrowerThan(final int minWidth) {
39        return new BoundedMatcher<View, View>(View.class) {
40            private String failedCheckDescription;
41
42            @Override
43            public void describeTo(final Description description) {
44                description.appendText(failedCheckDescription);
45            }
46
47            @Override
48            public boolean matchesSafely(final View view) {
49                final int viewWidth = view.getWidth();
50                if (viewWidth < minWidth) {
51                    failedCheckDescription =
52                            "width " + viewWidth + " is less than minimum " + minWidth;
53                    return false;
54                }
55                return true;
56            }
57        };
58    }
59
60    /**
61     * Returns a matcher that matches Views that are not wider than specified width in pixels.
62     */
63    public static Matcher<View> isNotWiderThan(final int maxWidth) {
64        return new BoundedMatcher<View, View>(View.class) {
65            private String failedCheckDescription;
66
67            @Override
68            public void describeTo(final Description description) {
69                description.appendText(failedCheckDescription);
70            }
71
72            @Override
73            public boolean matchesSafely(final View view) {
74                final int viewWidth = view.getWidth();
75                if (viewWidth > maxWidth) {
76                    failedCheckDescription =
77                            "width " + viewWidth + " is more than maximum " + maxWidth;
78                    return false;
79                }
80                return true;
81            }
82        };
83    }
84
85    /**
86     * Returns a matcher that matches TextViews with the specified text size.
87     */
88    public static Matcher withTextSize(final float textSize) {
89        return new BoundedMatcher<View, TextView>(TextView.class) {
90            private String failedCheckDescription;
91
92            @Override
93            public void describeTo(final Description description) {
94                description.appendText(failedCheckDescription);
95            }
96
97            @Override
98            public boolean matchesSafely(final TextView view) {
99                final float ourTextSize = view.getTextSize();
100                if (Math.abs(textSize - ourTextSize) > 1.0f) {
101                    failedCheckDescription =
102                            "text size " + ourTextSize + " is different than expected " + textSize;
103                    return false;
104                }
105                return true;
106            }
107        };
108    }
109
110    /**
111     * Returns a matcher that matches TextViews with the specified text color.
112     */
113    public static Matcher withTextColor(final @ColorInt int textColor) {
114        return new BoundedMatcher<View, TextView>(TextView.class) {
115            private String failedCheckDescription;
116
117            @Override
118            public void describeTo(final Description description) {
119                description.appendText(failedCheckDescription);
120            }
121
122            @Override
123            public boolean matchesSafely(final TextView view) {
124                final @ColorInt int ourTextColor = view.getCurrentTextColor();
125                if (ourTextColor != textColor) {
126                    int ourAlpha = Color.alpha(ourTextColor);
127                    int ourRed = Color.red(ourTextColor);
128                    int ourGreen = Color.green(ourTextColor);
129                    int ourBlue = Color.blue(ourTextColor);
130
131                    int expectedAlpha = Color.alpha(textColor);
132                    int expectedRed = Color.red(textColor);
133                    int expectedGreen = Color.green(textColor);
134                    int expectedBlue = Color.blue(textColor);
135
136                    failedCheckDescription =
137                            "expected color to be ["
138                                    + expectedAlpha + "," + expectedRed + ","
139                                    + expectedGreen + "," + expectedBlue
140                                    + "] but found ["
141                                    + ourAlpha + "," + ourRed + ","
142                                    + ourGreen + "," + ourBlue + "]";
143                    return false;
144                }
145                return true;
146            }
147        };
148    }
149
150    /**
151     * Returns a matcher that matches TextViews whose start drawable is filled with the specified
152     * fill color.
153     */
154    public static Matcher withStartDrawableFilledWith(final @ColorInt int fillColor,
155            final int allowedComponentVariance) {
156        return new BoundedMatcher<View, TextView>(TextView.class) {
157            private String failedCheckDescription;
158
159            @Override
160            public void describeTo(final Description description) {
161                description.appendText(failedCheckDescription);
162            }
163
164            @Override
165            public boolean matchesSafely(final TextView view) {
166                final Drawable[] compoundDrawables = view.getCompoundDrawables();
167                final boolean isRtl =
168                        (ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL);
169                final Drawable startDrawable = isRtl ? compoundDrawables[2] : compoundDrawables[0];
170                if (startDrawable == null) {
171                    failedCheckDescription = "no start drawable";
172                    return false;
173                }
174                try {
175                    final Rect bounds = startDrawable.getBounds();
176                    TestUtils.assertAllPixelsOfColor("",
177                            startDrawable, bounds.width(), bounds.height(), true,
178                            fillColor, allowedComponentVariance, true);
179                } catch (Throwable t) {
180                    failedCheckDescription = t.getMessage();
181                    return false;
182                }
183                return true;
184            }
185        };
186    }
187
188    /**
189     * Returns a matcher that matches Views with the specified background fill color.
190     */
191    public static Matcher withBackgroundFill(final @ColorInt int fillColor) {
192        return new BoundedMatcher<View, View>(View.class) {
193            private String failedCheckDescription;
194
195            @Override
196            public void describeTo(final Description description) {
197                description.appendText(failedCheckDescription);
198            }
199
200            @Override
201            public boolean matchesSafely(final View view) {
202                Drawable background = view.getBackground();
203                try {
204                    TestUtils.assertAllPixelsOfColor("",
205                            background, view.getWidth(), view.getHeight(), true,
206                            fillColor, 0, true);
207                } catch (Throwable t) {
208                    failedCheckDescription = t.getMessage();
209                    return false;
210                }
211                return true;
212            }
213        };
214    }
215
216    /**
217     * Returns a matcher that matches FloatingActionButtons with the specified background
218     * fill color.
219     */
220    public static Matcher withFabBackgroundFill(final @ColorInt int fillColor) {
221        return new BoundedMatcher<View, View>(View.class) {
222            private String failedCheckDescription;
223
224            @Override
225            public void describeTo(final Description description) {
226                description.appendText(failedCheckDescription);
227            }
228
229            @Override
230            public boolean matchesSafely(final View view) {
231                if (!(view instanceof FloatingActionButton)) {
232                    return false;
233                }
234
235                final FloatingActionButton fab = (FloatingActionButton) view;
236
237                // Since the FAB background is round, and may contain the shadow, we'll look at
238                // just the center half rect of the content area
239                final Rect area = new Rect();
240                fab.getContentRect(area);
241
242                final int rectHeightQuarter = area.height() / 4;
243                final int rectWidthQuarter = area.width() / 4;
244                area.left += rectWidthQuarter;
245                area.top += rectHeightQuarter;
246                area.right -= rectWidthQuarter;
247                area.bottom -= rectHeightQuarter;
248
249                try {
250                    TestUtils.assertAllPixelsOfColor("",
251                            fab.getBackground(), view.getWidth(), view.getHeight(), false,
252                            fillColor, area, 0, true);
253                } catch (Throwable t) {
254                    failedCheckDescription = t.getMessage();
255                    return false;
256                }
257                return true;
258            }
259        };
260    }
261
262    /**
263     * Returns a matcher that matches {@link View}s based on the given parent type.
264     *
265     * @param parentMatcher the type of the parent to match on
266     */
267    public static Matcher<View> isChildOfA(final Matcher<View> parentMatcher) {
268        return new TypeSafeMatcher<View>() {
269            @Override
270            public void describeTo(Description description) {
271                description.appendText("is child of a: ");
272                parentMatcher.describeTo(description);
273            }
274
275            @Override
276            public boolean matchesSafely(View view) {
277                final ViewParent viewParent = view.getParent();
278                if (!(viewParent instanceof View)) {
279                    return false;
280                }
281                if (parentMatcher.matches(viewParent)) {
282                    return true;
283                }
284                return false;
285            }
286        };
287    }
288
289    /**
290     * Returns a matcher that matches FloatingActionButtons with the specified content height
291     */
292    public static Matcher withFabContentHeight(final int size) {
293        return new BoundedMatcher<View, View>(View.class) {
294            private String failedCheckDescription;
295
296            @Override
297            public void describeTo(final Description description) {
298                description.appendText(failedCheckDescription);
299            }
300
301            @Override
302            public boolean matchesSafely(final View view) {
303                if (!(view instanceof FloatingActionButton)) {
304                    return false;
305                }
306
307                final FloatingActionButton fab = (FloatingActionButton) view;
308                final Rect area = new Rect();
309                fab.getContentRect(area);
310
311                return area.height() == size;
312            }
313        };
314    }
315
316}
317