/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.support.v4.testutils; import java.lang.String; import java.util.List; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.support.annotation.ColorInt; import android.support.test.espresso.matcher.BoundedMatcher; import android.support.v4.testutils.TestUtils; import android.support.v4.view.ViewCompat; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import junit.framework.Assert; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; public class TestUtilsMatchers { /** * Returns a matcher that matches views which have specific background color. */ public static Matcher backgroundColor(@ColorInt final int backgroundColor) { return new BoundedMatcher(View.class) { private String failedComparisonDescription; @Override public void describeTo(final Description description) { description.appendText("with background color: "); description.appendText(failedComparisonDescription); } @Override public boolean matchesSafely(final View view) { Drawable actualBackgroundDrawable = view.getBackground(); if (actualBackgroundDrawable == null) { return false; } // One option is to check if we have a ColorDrawable and then call getColor // but that API is v11+. Instead, we call our helper method that checks whether // all pixels in a Drawable are of the same specified color. Here we pass // hard-coded dimensions of 40x40 since we can't rely on the intrinsic dimensions // being set on our drawable. try { TestUtils.assertAllPixelsOfColor("", actualBackgroundDrawable, 40, 40, backgroundColor, true); // If we are here, the color comparison has passed. failedComparisonDescription = null; return true; } catch (Throwable t) { // If we are here, the color comparison has failed. failedComparisonDescription = t.getMessage(); return false; } } }; } /** * Returns a matcher that matches Views which are an instance of the provided class. */ public static Matcher isOfClass(final Class clazz) { if (clazz == null) { Assert.fail("Passed null Class instance"); } return new TypeSafeMatcher() { @Override public void describeTo(Description description) { description.appendText("is identical to class: " + clazz); } @Override public boolean matchesSafely(View view) { return clazz.equals(view.getClass()); } }; } /** * Returns a matcher that matches Views that are aligned to the left / start edge of * their parent. */ public static Matcher startAlignedToParent() { return new BoundedMatcher(View.class) { private String failedCheckDescription; @Override public void describeTo(final Description description) { description.appendText(failedCheckDescription); } @Override public boolean matchesSafely(final View view) { final ViewParent parent = view.getParent(); if (!(parent instanceof ViewGroup)) { return false; } final ViewGroup parentGroup = (ViewGroup) parent; final int parentLayoutDirection = ViewCompat.getLayoutDirection(parentGroup); if (parentLayoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) { if (view.getLeft() == 0) { return true; } else { failedCheckDescription = "not aligned to start (left) edge of parent : left=" + view.getLeft(); return false; } } else { if (view.getRight() == parentGroup.getWidth()) { return true; } else { failedCheckDescription = "not aligned to start (right) edge of parent : right=" + view.getRight() + ", parent width=" + parentGroup.getWidth(); return false; } } } }; } /** * Returns a matcher that matches Views that are aligned to the right / end edge of * their parent. */ public static Matcher endAlignedToParent() { return new BoundedMatcher(View.class) { private String failedCheckDescription; @Override public void describeTo(final Description description) { description.appendText(failedCheckDescription); } @Override public boolean matchesSafely(final View view) { final ViewParent parent = view.getParent(); if (!(parent instanceof ViewGroup)) { return false; } final ViewGroup parentGroup = (ViewGroup) parent; final int parentLayoutDirection = ViewCompat.getLayoutDirection(parentGroup); if (parentLayoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) { if (view.getRight() == parentGroup.getWidth()) { return true; } else { failedCheckDescription = "not aligned to end (right) edge of parent : right=" + view.getRight() + ", parent width=" + parentGroup.getWidth(); return false; } } else { if (view.getLeft() == 0) { return true; } else { failedCheckDescription = "not aligned to end (left) edge of parent : left=" + view.getLeft(); return false; } } } }; } /** * Returns a matcher that matches Views that are centered horizontally in their parent. */ public static Matcher centerAlignedInParent() { return new BoundedMatcher(View.class) { private String failedCheckDescription; @Override public void describeTo(final Description description) { description.appendText(failedCheckDescription); } @Override public boolean matchesSafely(final View view) { final ViewParent parent = view.getParent(); if (!(parent instanceof ViewGroup)) { return false; } final ViewGroup parentGroup = (ViewGroup) parent; final int viewLeft = view.getLeft(); final int viewRight = view.getRight(); final int parentWidth = parentGroup.getWidth(); final int viewMiddle = (viewLeft + viewRight) / 2; final int parentMiddle = parentWidth / 2; // Check that the view is centered in its parent, accounting for off-by-one // pixel difference in case one is even and the other is odd. if (Math.abs(viewMiddle - parentMiddle) > 1) { failedCheckDescription = "not aligned to center of parent : own span=[" + viewLeft + "-" + viewRight + "], parent width=" + parentWidth; return false; } return true; } }; } /** * Returns a matcher that matches lists of integer values that match the specified sequence * of values. */ public static Matcher> matches(final int ... expectedValues) { return new TypeSafeMatcher>() { private String mFailedDescription; @Override public void describeTo(Description description) { description.appendText(mFailedDescription); } @Override protected boolean matchesSafely(List item) { int actualCount = item.size(); int expectedCount = expectedValues.length; if (actualCount != expectedCount) { mFailedDescription = "Expected " + expectedCount + " values, but got " + actualCount; return false; } for (int i = 0; i < expectedCount; i++) { int curr = item.get(i); if (curr != expectedValues[i]) { mFailedDescription = "At #" + i + " got " + curr + " but should be " + expectedValues[i]; return false; } } return true; } }; } }