diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2021-06-19 03:08:58 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2021-06-19 03:08:58 +0000 |
commit | 92ee1c4e91c8c1a9880af75f903b438d196fc9b6 (patch) | |
tree | cd60438a00efd01eec6198b56291371ee58e9d68 | |
parent | 68d88c86e84c0bfeca8c9b3a99795e05572b9454 (diff) | |
parent | cece3e92e86869bc477ea704176e534e76a2f46c (diff) | |
download | Dialer-92ee1c4e91c8c1a9880af75f903b438d196fc9b6.tar.gz |
Snap for 7473808 from cece3e92e86869bc477ea704176e534e76a2f46c to sc-release
Change-Id: Ib9eb7b05e9129e24b89c79e0a67e69240481364a
5 files changed, 269 insertions, 174 deletions
diff --git a/src/com/android/car/dialer/ui/contact/ContactDetailsFragment.java b/src/com/android/car/dialer/ui/contact/ContactDetailsFragment.java index bd306792..86dae334 100644 --- a/src/com/android/car/dialer/ui/contact/ContactDetailsFragment.java +++ b/src/com/android/car/dialer/ui/contact/ContactDetailsFragment.java @@ -86,7 +86,7 @@ public class ContactDetailsFragment extends Hilt_ContactDetailsFragment implemen public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mContact = getArguments().getParcelable(KEY_CONTACT_ENTITY); + mContact = getArguments() == null ? null : getArguments().getParcelable(KEY_CONTACT_ENTITY); if (mContact == null && savedInstanceState != null) { mContact = savedInstanceState.getParcelable(KEY_CONTACT_ENTITY); } @@ -118,7 +118,9 @@ public class ContactDetailsFragment extends Hilt_ContactDetailsFragment implemen } private void onContactChanged(Contact contact) { - getArguments().clear(); + if (getArguments() != null) { + getArguments().clear(); + } ToolbarController toolbar = CarUi.getToolbar(getActivity()); // Null check to have unit tests to pass. if (toolbar == null) { diff --git a/testing/src/com/android/car/dialer/testing/TestViewMatchers.java b/testing/src/com/android/car/dialer/testing/TestViewMatchers.java new file mode 100644 index 00000000..770b9c5f --- /dev/null +++ b/testing/src/com/android/car/dialer/testing/TestViewMatchers.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2021 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 com.android.car.dialer.testing; + +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import androidx.test.espresso.matcher.BoundedMatcher; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +/** View matchers created for Android instrumented tests. */ +public final class TestViewMatchers { + + /** Creates a matcher that checks the {@link View#isActivated()} state. */ + public static Matcher<View> isActivated(boolean isActivated) { + return new TypeSafeMatcher<View>() { + @Override + protected boolean matchesSafely(View item) { + return item.isActivated() == isActivated; + } + + @Override + public void describeTo(Description description) { + description.appendText("View is " + (isActivated ? "activated" : "not activated")); + } + }; + } + + /** Creates a matcher for the item view at the given position of a {@link RecyclerView}. */ + public static Matcher<View> atPosition(int position, @NonNull Matcher<View> itemMatcher) { + return new BoundedMatcher<View, RecyclerView>(RecyclerView.class) { + @Override + public void describeTo(Description description) { + description.appendText("has item at position " + position + ": "); + itemMatcher.describeTo(description); + } + + @Override + protected boolean matchesSafely(final RecyclerView view) { + RecyclerView.ViewHolder viewHolder = view.findViewHolderForAdapterPosition( + position); + if (viewHolder == null) { + return false; + } + return itemMatcher.matches(viewHolder.itemView); + } + }; + } +} diff --git a/tests/robotests/src/com/android/car/dialer/ui/contact/ContactDetailsFragmentTest.java b/tests/robotests/src/com/android/car/dialer/ui/contact/ContactDetailsFragmentTest.java deleted file mode 100644 index 9a9bca4c..00000000 --- a/tests/robotests/src/com/android/car/dialer/ui/contact/ContactDetailsFragmentTest.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2019 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 com.android.car.dialer.ui.contact; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.view.View; -import android.widget.TextView; - -import androidx.lifecycle.MutableLiveData; - -import com.android.car.arch.common.FutureData; -import com.android.car.dialer.CarDialerRobolectricTestRunner; -import com.android.car.dialer.FragmentTestActivity; -import com.android.car.dialer.R; -import com.android.car.dialer.telecom.UiCallManager; -import com.android.car.dialer.testutils.ShadowAndroidViewModelFactory; -import com.android.car.telephony.common.Contact; -import com.android.car.telephony.common.InMemoryPhoneBook; -import com.android.car.telephony.common.PhoneNumber; -import com.android.car.ui.recyclerview.CarUiRecyclerView; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.robolectric.Robolectric; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; - -import java.util.Arrays; - -@Config(shadows = {ShadowAndroidViewModelFactory.class}, qualifiers = "h610dp") -@RunWith(CarDialerRobolectricTestRunner.class) -public class ContactDetailsFragmentTest { - private static final String DISPLAY_NAME = "NAME"; - private static final String[] RAW_NUMBERS = {"6505550000", "6502370000"}; - - private ContactDetailsFragment mContactDetailsFragment; - private FragmentTestActivity mFragmentTestActivity; - private CarUiRecyclerView mListView; - @Mock - private ContactDetailsViewModel mMockContactDetailsViewModel; - @Mock - private Contact mMockContact; - @Mock - private PhoneNumber mMockPhoneNumber1; - @Mock - private PhoneNumber mMockPhoneNumber2; - @Mock - private UiCallManager mMockUiCallManager; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - InMemoryPhoneBook.init(RuntimeEnvironment.application); - - when(mMockContact.getDisplayName()).thenReturn(DISPLAY_NAME); - when(mMockPhoneNumber1.getRawNumber()).thenReturn(RAW_NUMBERS[0]); - when(mMockPhoneNumber2.getRawNumber()).thenReturn(RAW_NUMBERS[1]); - when(mMockContact.getNumbers()).thenReturn( - Arrays.asList(mMockPhoneNumber1, mMockPhoneNumber2)); - - MutableLiveData<FutureData<Contact>> contactDetails = new MutableLiveData<>(); - contactDetails.setValue(new FutureData<>(false, mMockContact)); - ShadowAndroidViewModelFactory.add(ContactDetailsViewModel.class, - mMockContactDetailsViewModel); - when(mMockContactDetailsViewModel.getContactDetails(mMockContact)).thenReturn( - contactDetails); - } - - @After - public void tearDown() { - InMemoryPhoneBook.tearDown(); - } - - @Test - public void testCreateWithContact() { - mContactDetailsFragment = ContactDetailsFragment.newInstance(mMockContact); - - setUpFragment(); - - verifyHeader(); - verifyPhoneNumber(1); - verifyPhoneNumber(2); - } - - private void setUpFragment() { - mFragmentTestActivity = Robolectric.buildActivity( - FragmentTestActivity.class).create().resume().get(); - - ContactDetailsViewHolder.Factory viewHolderFactory = - (v, phoneNumberPresenter) -> new ContactDetailsViewHolder(v, phoneNumberPresenter, - mMockUiCallManager); - mContactDetailsFragment.mContactDetailsAdapterFactory = - (contact, phoneNumberPresenter) -> new ContactDetailsAdapter( - mFragmentTestActivity, viewHolderFactory, contact, phoneNumberPresenter); - mFragmentTestActivity.setFragment(mContactDetailsFragment); - - mListView = mContactDetailsFragment.getView().findViewById(R.id.list_view); - // Set up layout for recyclerView - mListView.layout(0, 0, 100, 1000); - } - - /** - * Verify the title of the Contact - */ - private void verifyHeader() { - View firstChild = mListView.findViewHolderForLayoutPosition(0).itemView; - assertThat(((TextView) firstChild.findViewById(R.id.title)).getText().toString()).isEqualTo( - DISPLAY_NAME); - assertThat(firstChild.hasOnClickListeners()).isFalse(); - } - - /** - * Verify the phone numbers for the Contact - */ - private void verifyPhoneNumber(int position) { - View child = mListView.findViewHolderForLayoutPosition(position).itemView; - View callButton = child.findViewById(R.id.call_action_id); - - assertThat(((TextView) child.findViewById(R.id.title)).getText().toString()).isEqualTo( - RAW_NUMBERS[position - 1]); - assertThat(callButton.hasOnClickListeners()).isTrue(); - - int invocations = Mockito.mockingDetails(mMockUiCallManager).getInvocations().size(); - - callButton.performClick(); - - verify(mMockUiCallManager, times(invocations + 1)).placeCall(Mockito.any()); - } -} diff --git a/tests/unittests/src/com/android/car/dialer/ui/activecall/OnGoingCallControllerBarFragmentTest.java b/tests/unittests/src/com/android/car/dialer/ui/activecall/OnGoingCallControllerBarFragmentTest.java index f40ef7db..65dbd2c9 100644 --- a/tests/unittests/src/com/android/car/dialer/ui/activecall/OnGoingCallControllerBarFragmentTest.java +++ b/tests/unittests/src/com/android/car/dialer/ui/activecall/OnGoingCallControllerBarFragmentTest.java @@ -27,6 +27,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibilit import static androidx.test.espresso.matcher.ViewMatchers.withId; import static com.android.car.dialer.testing.TestViewActions.selfClick; +import static com.android.car.dialer.testing.TestViewMatchers.isActivated; import static com.google.common.truth.Truth.assertThat; @@ -37,7 +38,6 @@ import static org.mockito.Mockito.when; import android.telecom.Call; import android.telecom.CallAudioState; -import android.view.View; import androidx.core.util.Pair; import androidx.lifecycle.LiveData; @@ -52,9 +52,6 @@ import com.android.car.dialer.R; import com.android.car.dialer.testing.TestActivity; import com.android.car.telephony.common.CallDetail; -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.hamcrest.TypeSafeMatcher; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -235,18 +232,4 @@ public class OnGoingCallControllerBarFragmentTest { new OnGoingCallControllerBarFragment()).commit(); }); } - - private static Matcher<View> isActivated(boolean isActivated) { - return new TypeSafeMatcher<View>() { - @Override - protected boolean matchesSafely(View item) { - return item.isActivated() == isActivated; - } - - @Override - public void describeTo(Description description) { - description.appendText("View is " + (isActivated ? "activated" : "not activated")); - } - }; - } } diff --git a/tests/unittests/src/com/android/car/dialer/ui/contact/ContactDetailsFragmentTest.java b/tests/unittests/src/com/android/car/dialer/ui/contact/ContactDetailsFragmentTest.java new file mode 100644 index 00000000..a2cdde72 --- /dev/null +++ b/tests/unittests/src/com/android/car/dialer/ui/contact/ContactDetailsFragmentTest.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2021 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 com.android.car.dialer.ui.contact; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.assertion.ViewAssertions.matches; +import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant; +import static androidx.test.espresso.matcher.ViewMatchers.hasSibling; +import static androidx.test.espresso.matcher.ViewMatchers.isClickable; +import static androidx.test.espresso.matcher.ViewMatchers.isNotClickable; +import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withText; + +import static com.android.car.dialer.testing.TestViewActions.selfClick; +import static com.android.car.dialer.testing.TestViewMatchers.atPosition; +import static com.android.car.dialer.testing.TestViewMatchers.isActivated; + +import static org.hamcrest.Matchers.allOf; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.Bundle; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; +import androidx.test.core.app.ActivityScenario; +import androidx.test.espresso.contrib.RecyclerViewActions; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.car.arch.common.FutureData; +import com.android.car.dialer.R; +import com.android.car.dialer.telecom.UiCallManager; +import com.android.car.dialer.testing.TestActivity; +import com.android.car.telephony.common.Contact; +import com.android.car.telephony.common.PhoneNumber; + +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; + +@RunWith(AndroidJUnit4.class) +public class ContactDetailsFragmentTest { + private static final String DISPLAY_NAME = "NAME"; + private static final String[] RAW_NUMBERS = {"6505550000", "6502370000"}; + + @Mock + private Contact mMockContact; + @Mock + private PhoneNumber mMockPhoneNumber1; + @Mock + private PhoneNumber mMockPhoneNumber2; + @Mock + private UiCallManager mMockUiCallManager; + + // This is initialized in the TestContactDetailsFragment#getDefaultViewModelProviderFactory(); + private ContactDetailsViewModel mViewModel; + + private final FragmentManager.FragmentLifecycleCallbacks mCallbacks = + new FragmentManager.FragmentLifecycleCallbacks() { + @Override + public void onFragmentAttached(@NonNull FragmentManager fm, @NonNull Fragment f, + @NonNull Context context) { + // Set up the mock ContactDetailsViewModel. + mViewModel = new ViewModelProvider(f).get(ContactDetailsViewModel.class); + LiveData<FutureData<Contact>> contactDetail = new MutableLiveData<>( + FutureData.newLoadedData(mMockContact)); + when(mViewModel.getContactDetails(any())).thenReturn(contactDetail); + } + + @Override + public void onFragmentCreated(@NonNull FragmentManager fm, @NonNull Fragment f, + @Nullable Bundle savedInstanceState) { + if (f instanceof ContactDetailsFragment) { + // Set up adapter with the mock UiCallManager. + ContactDetailsViewHolder.Factory viewHolderFactory = + (v, phoneNumberPresenter) -> new ContactDetailsViewHolder(v, + phoneNumberPresenter, mMockUiCallManager); + ((ContactDetailsFragment) f).mContactDetailsAdapterFactory = + (contact, phoneNumberPresenter) -> new ContactDetailsAdapter( + f.getContext(), viewHolderFactory, contact, + phoneNumberPresenter); + fm.unregisterFragmentLifecycleCallbacks(mCallbacks); + } + } + }; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mMockContact.getDisplayName()).thenReturn(DISPLAY_NAME); + when(mMockPhoneNumber1.getRawNumber()).thenReturn(RAW_NUMBERS[0]); + when(mMockPhoneNumber2.getRawNumber()).thenReturn(RAW_NUMBERS[1]); + when(mMockContact.getNumbers()).thenReturn( + Arrays.asList(mMockPhoneNumber1, mMockPhoneNumber2)); + + } + + @Test + public void testCreateWithContact() { + setUpFragment(); + + onView(withId(R.id.list_view)) + .perform(RecyclerViewActions.scrollToPosition(0)) + .check(matches(atPosition(0, hasDescendant( + allOf(withId(R.id.title), withText(DISPLAY_NAME)))))) + .check(matches(atPosition(0, isNotClickable()))); + + verifyPhoneNumber(1); + verifyPhoneNumber(2); + } + + private void setUpFragment() { + ActivityScenario<TestActivity> activityScenario = ActivityScenario.launch( + TestActivity.class); + activityScenario.onActivity(activity -> { + ContactDetailsFragment fragment = new TestContactDetailsFragment(); + + activity.getSupportFragmentManager().registerFragmentLifecycleCallbacks( + mCallbacks, /* recursive= */false); + + activity.getSupportFragmentManager().beginTransaction().add( + R.id.test_fragment_container, fragment).commit(); + }); + } + + /** + * Verify the phone numbers for the Contact + */ + private void verifyPhoneNumber(int position) { + onView(withId(R.id.list_view)) + .perform(RecyclerViewActions.scrollToPosition(position)) + .check(matches(atPosition(position, hasDescendant( + allOf(withId(R.id.title), withText(RAW_NUMBERS[position - 1])))))) + .check(matches(atPosition(position, hasDescendant( + allOf(withId(R.id.call_action_id), isClickable()))))); + + Matcher<View> callActionViewMatcher = allOf( + withId(R.id.call_action_id), + hasDescendant(withText(RAW_NUMBERS[position - 1]))); + onView(callActionViewMatcher).perform(selfClick()); + verify(mMockUiCallManager).placeCall(RAW_NUMBERS[position - 1]); + + onView(allOf( + withId(R.id.contact_details_favorite_button), hasSibling(callActionViewMatcher))) + .check(matches(isActivated(false))) + .perform(selfClick()) + .check(matches(isActivated(true))); + verify(mViewModel).addToFavorite(eq(mMockContact), + eq(position == 1 ? mMockPhoneNumber1 : mMockPhoneNumber2)); + } + + /** A test override that creates mock {@link ViewModel}s. */ + public static class TestContactDetailsFragment extends ContactDetailsFragment { + + @Override + public ViewModelProvider.Factory getDefaultViewModelProviderFactory() { + return new ViewModelProvider.Factory() { + @NonNull + @Override + public <T extends ViewModel> T create(@NonNull Class<T> aClass) { + return mock(aClass); + } + }; + } + } +} |