Run toolbar tests with and without shared library
Bug: 182208858
Test: atest CarUiLibUnitTests
Change-Id: I1c593c644ec3a1a53f47e2080b39d3ae0d4fd8b2
diff --git a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/matchers/DrawableMatcher.java b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/matchers/DrawableMatcher.java
index 4c0d9bb..2030e0f 100644
--- a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/matchers/DrawableMatcher.java
+++ b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/matchers/DrawableMatcher.java
@@ -16,21 +16,28 @@
package com.android.car.ui.matchers;
+import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
/* package */ class DrawableMatcher extends TypeSafeMatcher<View> {
- private final int mDrawableId;
+ private final Bitmap mBitmap;
- DrawableMatcher(int drawableId) {
- mDrawableId = drawableId;
+ DrawableMatcher(@NonNull Context context, @DrawableRes int drawableId) {
+ this(context.getDrawable(drawableId));
+ }
+ DrawableMatcher(Drawable drawable) {
+ mBitmap = drawableToBitmap(drawable);
}
@Override
@@ -42,7 +49,7 @@
ImageView imageView = (ImageView) item;
Bitmap bitmap = drawableToBitmap(imageView.getDrawable());
- Bitmap otherBitmap = drawableToBitmap(imageView.getContext().getDrawable(mDrawableId));
+ Bitmap otherBitmap = mBitmap;
if (bitmap == null && otherBitmap == null) {
return true;
@@ -68,6 +75,6 @@
@Override
public void describeTo(Description description) {
- description.appendText("has drawable with id " + mDrawableId);
+ description.appendText("has a certain drawable");
}
}
diff --git a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/matchers/ViewMatchers.java b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/matchers/ViewMatchers.java
index be397b1..9f4e238 100644
--- a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/matchers/ViewMatchers.java
+++ b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/matchers/ViewMatchers.java
@@ -16,15 +16,26 @@
package com.android.car.ui.matchers;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+
+import static org.hamcrest.Matchers.not;
+
+import android.content.Context;
import android.view.View;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.test.espresso.ViewAssertion;
+
import com.android.car.ui.matchers.PaddingMatcher.Side;
import org.hamcrest.Matcher;
public class ViewMatchers {
- public static Matcher<View> withDrawable(int drawableId) {
- return new DrawableMatcher(drawableId);
+ public static Matcher<View> withDrawable(
+ @NonNull Context context, @DrawableRes int drawableId) {
+ return new DrawableMatcher(context, drawableId);
}
public static Matcher<View> nthChildOfView(Matcher<View> parentMatcher, int n) {
@@ -46,4 +57,12 @@
public static Matcher<View> isActivated() {
return new IsActivatedMatcher();
}
+
+ public static ViewAssertion doesNotExistOrIsNotDisplayed() {
+ return (view, noViewFoundException) -> {
+ if (view != null) {
+ matches(not(isDisplayed())).check(view, noViewFoundException);
+ }
+ };
+ }
}
diff --git a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/preference/PreferenceTest.java b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/preference/PreferenceTest.java
index b116706..eb576b3 100644
--- a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/preference/PreferenceTest.java
+++ b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/preference/PreferenceTest.java
@@ -22,6 +22,7 @@
import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isNotChecked;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
@@ -101,7 +102,7 @@
.check(matches(isNotChecked()));
// Press back to save selection.
- onView(withId(R.id.car_ui_toolbar_nav_icon)).perform(click());
+ onView(withContentDescription("Back")).perform(click());
// Verify preference value was updated.
verify(mockListener, times(1)).onPreferenceChange(any(), eq(mEntriesValues[0]));
@@ -126,7 +127,7 @@
.check(matches(isNotChecked()));
// Press back to save selection.
- onView(withId(R.id.car_ui_toolbar_nav_icon)).perform(click());
+ onView(withContentDescription("Back")).perform(click());
// Verify preference value was updated.
verify(mockListener, times(1)).onPreferenceChange(any(), eq(mEntriesValues[1]));
// Return to list preference screen.
@@ -175,7 +176,7 @@
.check(matches(isChecked()));
// Press back to save selection.
- onView(withId(R.id.car_ui_toolbar_nav_icon)).perform(click());
+ onView(withContentDescription("Back")).perform(click());
Set<String> expectedUpdate = new HashSet<>();
expectedUpdate.add(mEntriesValues[0]);
expectedUpdate.add(mEntriesValues[2]);
@@ -318,7 +319,7 @@
.check(matches(isChecked()));
// Press back to save selection.
- onView(withId(R.id.car_ui_toolbar_nav_icon)).perform(click());
+ onView(withContentDescription("Back")).perform(click());
// Verify preference value was updated.
verify(mockListener, times(1)).onPreferenceChange(any(), eq(mEntriesValues[2]));
diff --git a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/toolbar/ToolbarTest.java b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/toolbar/ToolbarTest.java
index eafbb30..b9e475f 100644
--- a/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/toolbar/ToolbarTest.java
+++ b/car-ui-lib/car-ui-lib/src/androidTest/java/com/android/car/ui/toolbar/ToolbarTest.java
@@ -18,16 +18,16 @@
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.ViewMatchers.hasChildCount;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withHint;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static com.android.car.ui.actions.ViewActions.waitForView;
-import static com.android.car.ui.matchers.ViewMatchers.nthChildOfView;
+import static com.android.car.ui.matchers.ViewMatchers.doesNotExistOrIsNotDisplayed;
import static com.android.car.ui.matchers.ViewMatchers.withDrawable;
import static com.google.common.truth.Truth.assertThat;
@@ -38,28 +38,45 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import android.content.Context;
import android.view.View;
-import androidx.test.rule.ActivityTestRule;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.car.ui.core.CarUi;
+import com.android.car.ui.sharedlibrarysupport.SharedLibraryFactorySingleton;
import com.android.car.ui.test.R;
-import org.hamcrest.Matcher;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.Collections;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
/** Unit test for {@link ToolbarController}. */
@SuppressWarnings("AndroidJdkLibsChecker")
+@RunWith(Parameterized.class)
public class ToolbarTest {
+ @Parameterized.Parameters(name = "With shared library? {0}")
+ public static Object[] data() {
+ // It's important to do no shared library first, so that the shared library will
+ // still be enabled when this test finishes
+ return new Object[] { false, true };
+ }
+
+ public ToolbarTest(boolean sharedLibEnabled) {
+ SharedLibraryFactorySingleton.setSharedLibEnabled(sharedLibEnabled);
+ }
+
@Rule
- public ActivityTestRule<ToolbarTestActivity> mActivityRule =
- new ActivityTestRule<>(ToolbarTestActivity.class);
+ public final ActivityScenarioRule<ToolbarTestActivity> mScenarioRule =
+ new ActivityScenarioRule<>(ToolbarTestActivity.class);
@Test
public void test_setTitle_displaysTitle() throws Throwable {
@@ -135,157 +152,187 @@
public void test_setLogo_displaysLogo() throws Throwable {
runWithToolbar((toolbar) -> toolbar.setLogo(R.drawable.ic_launcher));
- onView(withDrawable(R.drawable.ic_launcher)).check(matches(isDisplayed()));
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ onView(withDrawable(context, R.drawable.ic_launcher)).check(matches(isDisplayed()));
}
@Test
public void pressBack_withoutListener_callsActivityOnBack() throws Throwable {
- runWithToolbar((toolbar) -> toolbar.setState(Toolbar.State.SUBPAGE));
+ ToolbarTestActivity[] savedActivity = new ToolbarTestActivity[] { null };
+ runWithActivityAndToolbar((activity, toolbar) -> {
+ toolbar.setState(Toolbar.State.SUBPAGE);
+ savedActivity[0] = activity;
+ });
- onView(withId(R.id.car_ui_toolbar_nav_icon_container)).perform(click());
+ onView(withContentDescription("Back")).perform(click());
- assertEquals(1, mActivityRule.getActivity().getTimesOnBackPressed());
+ assertEquals(1, savedActivity[0].getTimesOnBackPressed());
}
@Test
public void pressBack_withListenerThatReturnsFalse_callsActivityOnBack() throws Throwable {
- runWithToolbar((toolbar) -> {
+ ToolbarTestActivity[] savedActivity = new ToolbarTestActivity[] { null };
+ runWithActivityAndToolbar((activity, toolbar) -> {
toolbar.setState(Toolbar.State.SUBPAGE);
toolbar.registerOnBackListener(() -> false);
+ savedActivity[0] = activity;
});
- onView(withId(R.id.car_ui_toolbar_nav_icon_container)).perform(click());
+ onView(withContentDescription("Back")).perform(click());
- assertEquals(1, mActivityRule.getActivity().getTimesOnBackPressed());
+ assertEquals(1, savedActivity[0].getTimesOnBackPressed());
}
@Test
public void pressBack_withListenerThatReturnsTrue_doesntCallActivityOnBack() throws Throwable {
- runWithToolbar((toolbar) -> {
+ ToolbarTestActivity[] savedActivity = new ToolbarTestActivity[] { null };
+ runWithActivityAndToolbar((activity, toolbar) -> {
toolbar.setState(Toolbar.State.SUBPAGE);
toolbar.registerOnBackListener(() -> true);
+ savedActivity[0] = activity;
});
- onView(withId(R.id.car_ui_toolbar_nav_icon_container)).perform(click());
+ onView(withContentDescription("Back")).perform(click());
- assertEquals(0, mActivityRule.getActivity().getTimesOnBackPressed());
+ assertEquals(0, savedActivity[0].getTimesOnBackPressed());
}
@Test
public void pressBack_withUnregisteredListener_doesntCallActivityOnBack() throws Throwable {
- runWithToolbar((toolbar) -> {
+ ToolbarTestActivity[] savedActivity = new ToolbarTestActivity[] { null };
+ runWithActivityAndToolbar((activity, toolbar) -> {
toolbar.setState(Toolbar.State.SUBPAGE);
Toolbar.OnBackListener listener = () -> true;
toolbar.registerOnBackListener(listener);
toolbar.registerOnBackListener(listener);
toolbar.unregisterOnBackListener(listener);
+ savedActivity[0] = activity;
});
- onView(withId(R.id.car_ui_toolbar_nav_icon_container)).perform(click());
+ onView(withContentDescription("Back")).perform(click());
- assertEquals(1, mActivityRule.getActivity().getTimesOnBackPressed());
+ assertEquals(1, savedActivity[0].getTimesOnBackPressed());
}
@Test
- public void menuItems_setId_shouldWork() {
- MenuItem item = MenuItem.builder(mActivityRule.getActivity()).build();
+ public void menuItems_setId_shouldWork() throws Throwable {
+ runWithActivityAndToolbar((activity, toolbar) -> {
+ MenuItem item = MenuItem.builder(activity).build();
- assertThat(item.getId()).isEqualTo(View.NO_ID);
+ assertThat(item.getId()).isEqualTo(View.NO_ID);
- item.setId(7);
+ item.setId(7);
- assertThat(item.getId()).isEqualTo(7);
+ assertThat(item.getId()).isEqualTo(7);
+ });
}
@Test
public void menuItems_whenClicked_shouldCallListener() throws Throwable {
MenuItem.OnClickListener callback = mock(MenuItem.OnClickListener.class);
- MenuItem menuItem = MenuItem.builder(mActivityRule.getActivity())
- .setTitle("Button!")
- .setOnClickListener(callback)
- .build();
- runWithToolbar((toolbar) -> toolbar.setMenuItems(Collections.singletonList(menuItem)));
+ MenuItem[] menuItem = new MenuItem[] { null };
+ runWithActivityAndToolbar((activity, toolbar) -> {
+ menuItem[0] = MenuItem.builder(activity)
+ .setTitle("Button!")
+ .setOnClickListener(callback)
+ .build();
+ toolbar.setMenuItems(Collections.singletonList(menuItem[0]));
+ });
- waitForMenuItems();
+ waitForViewWithText("Button!");
- onView(firstMenuItem()).perform(click());
+ onView(withText("Button!")).perform(click());
- verify(callback).onClick(menuItem);
+ verify(callback).onClick(menuItem[0]);
}
@Test
public void menuItems_null_shouldRemoveExistingMenuItems() throws Throwable {
- runWithToolbar((toolbar) ->
+ runWithActivityAndToolbar((activity, toolbar) ->
toolbar.setMenuItems(Arrays.asList(
- MenuItem.builder(mActivityRule.getActivity())
+ MenuItem.builder(activity)
.setTitle("Button!")
.build(),
- MenuItem.builder(mActivityRule.getActivity())
+ MenuItem.builder(activity)
.setTitle("Button2!")
.build()
)));
- waitForMenuItems();
+ waitForViewWithText("Button!");
+ waitForViewWithText("Button2!");
- onView(withId(R.id.car_ui_toolbar_menu_items_container)).check(matches(hasChildCount(2)));
+ onView(withText("Button!")).check(matches(isDisplayed()));
+ onView(withText("Button2!")).check(matches(isDisplayed()));
runWithToolbar((toolbar) -> toolbar.setMenuItems(null));
- onView(withId(R.id.car_ui_toolbar_menu_items_container)).check(matches(hasChildCount(0)));
+ onView(withText("Button!")).check(doesNotExist());
+ onView(withText("Button2!")).check(doesNotExist());
}
@Test
public void menuItems_setVisibility_shouldHide() throws Throwable {
- MenuItem menuItem = MenuItem.builder(mActivityRule.getActivity())
- .setTitle("Button!")
- .build();
- runWithToolbar((toolbar) -> toolbar.setMenuItems(Collections.singletonList(menuItem)));
- waitForMenuItems();
+ MenuItem[] menuItem = new MenuItem[] { null };
+ runWithActivityAndToolbar((activity, toolbar) -> {
+ menuItem[0] = MenuItem.builder(activity)
+ .setTitle("Button!")
+ .build();
+ toolbar.setMenuItems(Collections.singletonList(menuItem[0]));
+ });
+ waitForViewWithText("Button!");
onView(withText("Button!")).check(matches(isDisplayed()));
- runWithToolbar((toolbar) -> menuItem.setVisible(false));
+ runWithToolbar((toolbar) -> menuItem[0].setVisible(false));
onView(withText("Button!")).check(matches(not(isDisplayed())));
}
@Test
public void menuItems_searchScreen_shouldHideMenuItems() throws Throwable {
- runWithToolbar((toolbar) -> {
+ runWithActivityAndToolbar((activity, toolbar) -> {
toolbar.setMenuItems(Arrays.asList(
- MenuItem.builder(mActivityRule.getActivity())
+ MenuItem.builder(activity)
.setToSearch()
.build(),
- MenuItem.builder(mActivityRule.getActivity())
+ MenuItem.builder(activity)
.setTitle("Button!")
.build()));
- toolbar.setShowMenuItemsWhileSearching(false);
+ toolbar.setShowMenuItemsWhileSearching(true);
toolbar.setState(Toolbar.State.SEARCH);
});
- waitForMenuItems();
- // All menuitems should be hidden if we're hiding menuitems while searching
- onView(withText("Button!")).check(matches(not(isDisplayed())));
- onView(firstMenuItem()).check(matches(not(isDisplayed())));
-
- runWithToolbar((toolbar) -> toolbar.setShowMenuItemsWhileSearching(true));
+ waitForViewWithText("Button!");
// Even if not hiding MenuItems while searching, the search MenuItem should still be hidden
onView(withText("Button!")).check(matches(isDisplayed()));
- onView(firstMenuItem()).check(matches(not(isDisplayed())));
+ onView(withContentDescription(R.string.car_ui_toolbar_menu_item_search_title))
+ .check(doesNotExistOrIsNotDisplayed());
+
+ runWithToolbar((toolbar) -> toolbar.setShowMenuItemsWhileSearching(false));
+
+ // All menuitems should be hidden if we're hiding menuitems while searching
+ onView(withText("Button!")).check(doesNotExistOrIsNotDisplayed());
+ onView(withContentDescription(R.string.car_ui_toolbar_menu_item_search_title))
+ .check(doesNotExistOrIsNotDisplayed());
+
}
private void runWithToolbar(Consumer<ToolbarController> toRun) throws Throwable {
- mActivityRule.runOnUiThread(() -> {
- ToolbarController toolbar = CarUi.requireToolbar(mActivityRule.getActivity());
+ mScenarioRule.getScenario().onActivity(activity -> {
+ ToolbarController toolbar = CarUi.requireToolbar(activity);
toRun.accept(toolbar);
});
}
- private Matcher<View> firstMenuItem() {
- return nthChildOfView(withId(R.id.car_ui_toolbar_menu_items_container), 0);
+ private void runWithActivityAndToolbar(BiConsumer<ToolbarTestActivity, ToolbarController> toRun)
+ throws Throwable {
+ mScenarioRule.getScenario().onActivity(activity -> {
+ ToolbarController toolbar = CarUi.requireToolbar(activity);
+ toRun.accept(activity, toolbar);
+ });
}
- private void waitForMenuItems() {
- onView(isRoot()).perform(waitForView(firstMenuItem(), 500));
+ private void waitForViewWithText(String text) {
+ onView(isRoot()).perform(waitForView(withText(text), 500));
}
}
diff --git a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/sharedlibrarysupport/SharedLibraryFactorySingleton.java b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/sharedlibrarysupport/SharedLibraryFactorySingleton.java
index 3284aa0..69676f5 100644
--- a/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/sharedlibrarysupport/SharedLibraryFactorySingleton.java
+++ b/car-ui-lib/car-ui-lib/src/main/java/com/android/car/ui/sharedlibrarysupport/SharedLibraryFactorySingleton.java
@@ -27,6 +27,7 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import com.android.car.ui.R;
import com.android.car.ui.utils.CarUiUtils;
@@ -47,6 +48,7 @@
private static final String TAG = "carui";
private static SharedLibraryFactory sInstance;
+ private static boolean sSharedLibEnabled = true;
/**
* Get the {@link SharedLibraryFactory}.
@@ -62,6 +64,11 @@
context = context.getApplicationContext();
+ if (!sSharedLibEnabled) {
+ sInstance = new SharedLibraryFactoryStub(context);
+ return sInstance;
+ }
+
String sharedLibPackageName = CarUiUtils.getSystemProperty(context.getResources(),
R.string.car_ui_shared_library_package_system_property_name);
@@ -138,6 +145,23 @@
return sInstance;
}
+ /**
+ * This method globally enables/disables the shared library. It only applies upon the next
+ * call to {@link #get}, components that have already been created won't switch between
+ * the shared/static library implementations.
+ * <p>
+ * This method is @VisibleForTesting so that unit tests can run both with and without
+ * the shared library. Since it's tricky to use correctly, real apps shouldn't use it.
+ * Instead, apps should use {@link SharedLibraryConfigProvider} to control if their
+ * shared library is disabled.
+ */
+ @VisibleForTesting
+ public static void setSharedLibEnabled(boolean sharedLibEnabled) {
+ sSharedLibEnabled = sharedLibEnabled;
+ // Cause the next call to get() to reinitialize the shared library
+ sInstance = null;
+ }
+
private SharedLibraryFactorySingleton() {}
@NonNull