/* * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.litho.testing.viewtree; import java.util.List; import java.util.regex.Pattern; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import com.facebook.litho.ComponentHost; import com.facebook.litho.MatrixDrawable; import com.google.common.base.Predicate; /** * Utility APIs to query the state of components. */ class ComponentQueries { /** * Checks whether the given {@link ComponentHost} has the given text as it's text. It does not * look at the host's children. * @param host the component host * @param predicate the predicate to test * @return true if the host has the given text. */ static boolean hasTextMatchingPredicate(final ComponentHost host, final Predicate<String> predicate) { for (final CharSequence foundText : host.getTextContent().getTextItems()) { if (predicate.apply(foundText.toString())) { return true; } } return false; } /** * Checks whether the given {@link ComponentHost} matches the given pattern. It does not look at * the host's children. * @param host the component host * @param pattern the pattern to match * @return true if the host has text matching the pattern. */ static boolean matchesPattern(final ComponentHost host, final Pattern pattern) { for (final CharSequence foundText : host.getTextContent().getTextItems()) { if (pattern.matcher(foundText).find()) { return true; } } return false; } /** * Checks whether the given {@link ComponentHost} contains the given drawable. It does not look * at the host's children. * @param host the component host * @param drawable the drawable to look for * @return true if the given host contains the drawable. */ static boolean hasDrawable(final ComponentHost host, final Drawable drawable) { return satisfiesPredicate( host, new Predicate<Drawable>() { @Override public boolean apply(final Drawable input) { return hasDrawable(input, drawable); } }); } private static boolean hasDrawable(Drawable containingDrawable, Drawable drawable) { while (containingDrawable instanceof MatrixDrawable) { containingDrawable = ((MatrixDrawable) containingDrawable).getMountedDrawable(); } // Workaround a bug in Robolectric's BitmapDrawable implementation. if (drawable instanceof BitmapDrawable && !(containingDrawable instanceof BitmapDrawable)) { return false; } final String drawnDrawableDescription = ViewPredicates.getDrawnDrawableDescription(drawable); if (!drawnDrawableDescription.isEmpty()) { return ViewPredicates.getDrawnDrawableDescription(containingDrawable) .contains(drawnDrawableDescription); } // Some Drawables do not have a description. However they can be compared directly. // The drawable being compared is a Robolectric shadow drawable whose bounds are not set. // However, the containingDrawable in its mounted state has its bounds set in the components // testing environment. Therefore, the bounds must be set to 0 for comparison. containingDrawable = containingDrawable.mutate(); containingDrawable.setBounds(0, 0, 0, 0); if (drawable.equals(containingDrawable)) { return true; } // The drawable being compared might have pre-set bounds. drawable = drawable.mutate(); drawable.setBounds(0, 0, 0, 0); return drawable.equals(containingDrawable); } /** * Checks whether the given {@link ComponentHost} satisfy the given Predicate. * @param host the component host * @param predicate the predicate * @return true if the ComponentHost satisfies the condition. */ private static boolean satisfiesPredicate(final ComponentHost host, final Predicate<Drawable> predicate) { final List<Drawable> drawables = host.getDrawables(); for (final Drawable drawable : drawables) { if (predicate.apply(drawable)) { return true; } } for (final Drawable drawable : host.getImageContent().getImageItems()) { if (predicate.apply(drawable)) { return true; } } return false; } }