From 4017204e188858a41b50ae042c8b9963eb7acc1f Mon Sep 17 00:00:00 2001 From: Andrew Lee Date: Mon, 14 Sep 2015 18:32:37 -0700 Subject: Move CallLogFragment tests to CallLogAdapterTest. + Reduces flakiness of test. + Rewrote random stress test to be more deterministic; simpler code but still tests in general for the same scenario (rebinding viewholders for different presentation types). Bug: 23640774 Change-Id: I57adf16f9becc5cb7354563986f55f9023e4aabd --- .../android/dialer/calllog/CallLogAdapterTest.java | 447 +++++++++++++---- .../dialer/calllog/CallLogFragmentTest.java | 545 --------------------- 2 files changed, 353 insertions(+), 639 deletions(-) delete mode 100644 tests/src/com/android/dialer/calllog/CallLogFragmentTest.java (limited to 'tests/src') diff --git a/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java b/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java index de6f198e4..83d098fc5 100644 --- a/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java +++ b/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java @@ -22,14 +22,16 @@ import android.content.Intent; import android.database.MatrixCursor; import android.net.Uri; import android.provider.CallLog.Calls; +import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.VoicemailContract; import android.support.v7.widget.RecyclerView.ViewHolder; +import android.telephony.PhoneNumberUtils; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.LargeTest; import android.text.TextUtils; import android.view.View; -import android.widget.LinearLayout; import com.android.dialer.contactinfo.ContactInfoCache; import com.android.dialer.contactinfo.ContactInfoCache.OnContactInfoChangedListener; @@ -42,24 +44,32 @@ import java.util.Random; /** * Unit tests for {@link CallLogAdapter}. + * + * adb shell am instrument \ + * -e com.android.dialer.calllog.CallLogAdapterTest \ + * -w com.android.dialer.tests/android.test.InstrumentationTestRunner */ -@SmallTest public class CallLogAdapterTest extends AndroidTestCase { + private static final String EMPTY_STRING = ""; private static final int NO_VALUE_SET = -1; + private static final String TEST_CACHED_NAME = "name"; + private static final String TEST_CACHED_NUMBER_LABEL = "label"; + private static final int TEST_CACHED_NUMBER_TYPE = 1; + private static final String TEST_COUNTRY_ISO = "US"; + private static final String TEST_DEFAULT_CUSTOM_LABEL = "myLabel"; + private static final Uri TEST_LOOKUP_URI = Uri.parse("content://contacts/2"); + + private static final String TEST_NUMBER = "12125551000"; private static final String TEST_NUMBER_1 = "12345678"; private static final String TEST_NUMBER_2 = "87654321"; private static final String TEST_NUMBER_3 = "18273645"; - private static final String TEST_NAME = "name"; - private static final String TEST_NUMBER_LABEL = "label"; - private static final int TEST_NUMBER_TYPE = 1; - private static final String TEST_COUNTRY_ISO = "US"; + private static final String TEST_FORMATTED_NUMBER = "1 212-555-1000"; // The object under test. private TestCallLogAdapter mAdapter; private MatrixCursor mCursor; - private int mCursorSize; private View mView; private CallLogListItemViewHolder mViewHolder; @@ -88,117 +98,188 @@ public class CallLogAdapterTest extends AndroidTestCase { }; mAdapter = new TestCallLogAdapter(getContext(), fakeCallFetcher, fakeContactInfoHelper); + // The cursor used in the tests to store the entries to display. mCursor = new MatrixCursor(CallLogQuery._PROJECTION); mCursor.moveToFirst(); + // The views into which to store the data. - mView = new LinearLayout(getContext()); mViewHolder = CallLogListItemViewHolder.createForTest(getContext()); - mView.setTag(mViewHolder); } @Override protected void tearDown() throws Exception { mAdapter = null; mCursor = null; - mView = null; super.tearDown(); } @MediumTest public void testBindView_NumberOnlyNoCache() { createCallLogEntry(); - mAdapter.changeCursor(mCursor); + mAdapter.changeCursor(mCursor); mAdapter.onBindViewHolder(mViewHolder, 0); - assertNameIs(mViewHolder, TEST_NUMBER_1); + + assertNameIs(mViewHolder, TEST_NUMBER); } @MediumTest public void testBindView_PrivateCall() { createPrivateCallLogEntry(); - mAdapter.changeCursor(mCursor); + mAdapter.changeCursor(mCursor); mAdapter.onBindViewHolder(mViewHolder, 0); + assertEquals(Calls.PRESENTATION_RESTRICTED, mViewHolder.numberPresentation); + assertNull(mViewHolder.primaryActionButtonView.getTag()); + } + + @MediumTest + public void testBindView_UnknownCall() { + createUnknownCallLogEntry(); + + mAdapter.changeCursor(mCursor); + mAdapter.onBindViewHolder(mViewHolder, 0); + + assertEquals(Calls.PRESENTATION_UNKNOWN, mViewHolder.numberPresentation); + assertNull(mViewHolder.primaryActionButtonView.getTag()); } @MediumTest public void testBindView_WithoutQuickContactBadge() { createCallLogEntry(); - mAdapter.changeCursor(mCursor); + mAdapter.changeCursor(mCursor); mAdapter.onBindViewHolder(mViewHolder, 0); + assertFalse(mViewHolder.quickContactView.isEnabled()); } @MediumTest public void testBindView_CallButton() { createCallLogEntry(); - mAdapter.changeCursor(mCursor); + mAdapter.changeCursor(mCursor); mAdapter.onBindViewHolder(mViewHolder, 0); // The primaryActionView tag is set when the ViewHolder is binded. If it is possible // to place a call to the phone number, a call intent will have been created which // starts a phone call to the entry's number. - IntentProvider intentProvider = - (IntentProvider) mViewHolder.primaryActionButtonView.getTag(); - Intent intent = intentProvider.getIntent(getContext()); - assertEquals(TestConstants.CALL_INTENT_ACTION, intent.getAction()); - assertEquals(Uri.parse("tel:" + TEST_NUMBER_1), intent.getData()); + assertHasCallAction(mViewHolder); } @MediumTest public void testBindView_VoicemailUri() { createVoicemailCallLogEntry(); - mAdapter.changeCursor(mCursor); + mAdapter.changeCursor(mCursor); mAdapter.onBindViewHolder(mViewHolder, 0); + assertEquals(Uri.parse(mViewHolder.voicemailUri), ContentUris.withAppendedId(VoicemailContract.Voicemails.CONTENT_URI, 0)); + assertNull(mViewHolder.primaryActionButtonView.getTag()); } - public void testBindView_NoCallLogCacheNorMemoryCache_EnqueueRequest() { - createCallLogEntry(); + @MediumTest + public void testPresentationAfterRebindingViewHolders() { + final int increment = 10; + final int size = increment * 4; + + // Instantiate list of ViewHolders. + CallLogListItemViewHolder[] holders = new CallLogListItemViewHolder[size]; + for (int i = 0; i < size; i++) { + holders[i] = CallLogListItemViewHolder.createForTest(getContext()); + } + + // Add first set of entries to the cursor. + for (int i = 0; i < increment; i++) { + createCallLogEntry(); + createPrivateCallLogEntry(); + createCallLogEntry(); + createUnknownCallLogEntry(); + } - // Bind the views of a single row. mAdapter.changeCursor(mCursor); - mAdapter.onBindViewHolder(mViewHolder, 0); - // There is one request for contact details. - assertEquals(1, mAdapter.getContactInfoCache().requests.size()); + // Verify correct appearance for presentation. + for (int i = 0; i < size; i++) { + mAdapter.onBindViewHolder(holders[i], i); + if (holders[i].numberPresentation == Calls.PRESENTATION_ALLOWED) { + assertHasCallAction(holders[i]); + } else { + assertNull(holders[i].primaryActionButtonView.getTag()); + assertEquals(holders[i].number, EMPTY_STRING); + } + } - TestContactInfoCache.Request request = mAdapter.getContactInfoCache().requests.get(0); - // It is for the number we need to show. - assertEquals(TEST_NUMBER_1, request.number); - // It has the right country. - assertEquals(TEST_COUNTRY_ISO, request.countryIso); - // Since there is nothing in the cache, it is an immediate request. - assertTrue("should be immediate", request.immediate); + // Append the rest of the entries to the cursor. Keep the first set of ViewHolders + // so they are updated and not buitl from scratch. This checks for bugs which may + // be evident only after the call log is updated. + for (int i = 0; i < increment; i++) { + createPrivateCallLogEntry(); + createCallLogEntry(); + createUnknownCallLogEntry(); + createCallLogEntry(); + } + + mCursor.move(size); + + // Verify correct appearnce for presentation. + for (int i = 0; i < size; i++) { + mAdapter.onBindViewHolder(holders[i], i + size); + if (holders[i].numberPresentation == Calls.PRESENTATION_ALLOWED) { + assertHasCallAction(holders[i]); + } else { + assertNull(holders[i].primaryActionButtonView.getTag()); + assertEquals(holders[i].number, EMPTY_STRING); + } + } } - public void testBindView_CallLogCacheButNoMemoryCache_EnqueueRequest() { - mCursor.addRow(createCallLogEntryWithCachedValues()); + @MediumTest + public void testBindView_NoCallLogCacheNorMemoryCache_EnqueueRequest() { + createCallLogEntry(); - // Bind the views of a single row. - mAdapter.changeCursor(mCursor); - mAdapter.onBindViewHolder(mViewHolder, 0); + // Bind the views of a single row. + mAdapter.changeCursor(mCursor); + mAdapter.onBindViewHolder(mViewHolder, 0); + + // There is one request for contact details. + assertEquals(1, mAdapter.getContactInfoCache().requests.size()); + + TestContactInfoCache.Request request = mAdapter.getContactInfoCache().requests.get(0); + // It is for the number we need to show. + assertEquals(TEST_NUMBER, request.number); + // It has the right country. + assertEquals(TEST_COUNTRY_ISO, request.countryIso); + // Since there is nothing in the cache, it is an immediate request. + assertTrue("should be immediate", request.immediate); + } + + @MediumTest + public void testBindView_CallLogCacheButNoMemoryCache_EnqueueRequest() { + createCallLogEntryWithCachedValues(false); + + // Bind the views of a single row. + mAdapter.changeCursor(mCursor); + mAdapter.onBindViewHolder(mViewHolder, 0); // There is one request for contact details. assertEquals(1, mAdapter.getContactInfoCache().requests.size()); TestContactInfoCache.Request request = mAdapter.getContactInfoCache().requests.get(0); + // The values passed to the request, match the ones in the call log cache. - assertEquals(TEST_NAME, request.callLogInfo.name); - assertEquals(1, request.callLogInfo.type); - assertEquals(TEST_NUMBER_LABEL, request.callLogInfo.label); + assertEquals(TEST_CACHED_NAME, request.callLogInfo.name); + assertEquals(TEST_CACHED_NUMBER_TYPE, request.callLogInfo.type); + assertEquals(TEST_CACHED_NUMBER_LABEL, request.callLogInfo.label); } - + @MediumTest public void testBindView_NoCallLogButMemoryCache_EnqueueRequest() { createCallLogEntry(); - mAdapter.injectContactInfoForTest(TEST_NUMBER_1, TEST_COUNTRY_ISO, createContactInfo()); + mAdapter.injectContactInfoForTest(TEST_NUMBER, TEST_COUNTRY_ISO, createContactInfo()); // Bind the views of a single row. mAdapter.changeCursor(mCursor); @@ -212,9 +293,9 @@ public class CallLogAdapterTest extends AndroidTestCase { assertFalse("should not be immediate", request.immediate); } + @MediumTest public void testBindView_BothCallLogAndMemoryCache_NoEnqueueRequest() { - mCursor.addRow(createCallLogEntryWithCachedValues()); - mAdapter.injectContactInfoForTest(TEST_NUMBER_1, TEST_COUNTRY_ISO, createContactInfo()); + createCallLogEntryWithCachedValues(true); // Bind the views of a single row. mAdapter.changeCursor(mCursor); @@ -224,13 +305,14 @@ public class CallLogAdapterTest extends AndroidTestCase { assertEquals(0, mAdapter.getContactInfoCache().requests.size()); } - public void testBindView_MismatchBetwenCallLogAndMemoryCache_EnqueueRequest() { - mCursor.addRow(createCallLogEntryWithCachedValues()); + @MediumTest + public void testBindView_MismatchBetweenCallLogAndMemoryCache_EnqueueRequest() { + createCallLogEntryWithCachedValues(false); // Contact info contains a different name. ContactInfo info = createContactInfo(); info.name = "new name"; - mAdapter.injectContactInfoForTest(TEST_NUMBER_1, TEST_COUNTRY_ISO, info); + mAdapter.injectContactInfoForTest(TEST_NUMBER, TEST_COUNTRY_ISO, info); // Bind the views of a single row. mAdapter.changeCursor(mCursor); @@ -244,6 +326,98 @@ public class CallLogAdapterTest extends AndroidTestCase { assertFalse("should not be immediate", request.immediate); } + @MediumTest + public void testBindView_WithCachedName() { + createCallLogEntryWithCachedValues( + "John Doe", + Phone.TYPE_HOME, + TEST_CACHED_NUMBER_LABEL); + + mAdapter.changeCursor(mCursor); + mAdapter.onBindViewHolder(mViewHolder, 0); + + assertNameIs(mViewHolder, "John Doe"); + assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME)); + } + + @MediumTest + public void testBindView_UriNumber() { + createCallLogEntryWithCachedValues( + "sip:johndoe@gmail.com", + Calls.INCOMING_TYPE, + "John Doe", + Phone.TYPE_HOME, + TEST_DEFAULT_CUSTOM_LABEL, + EMPTY_STRING, + false /* inject */); + + mAdapter.changeCursor(mCursor); + mAdapter.onBindViewHolder(mViewHolder, 0); + + assertNameIs(mViewHolder, "John Doe"); + assertLabel(mViewHolder, "sip:johndoe@gmail.com", "sip:johndoe@gmail.com"); + } + + @MediumTest + public void testBindView_HomeLabel() { + createCallLogEntryWithCachedValues( + "John Doe", + Phone.TYPE_HOME, + TEST_CACHED_NUMBER_LABEL); + + mAdapter.changeCursor(mCursor); + mAdapter.onBindViewHolder(mViewHolder, 0); + + assertNameIs(mViewHolder, "John Doe"); + assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME)); + } + + @MediumTest + public void testBindView_WorkLabel() { + createCallLogEntryWithCachedValues( + "John Doe", + Phone.TYPE_WORK, + TEST_CACHED_NUMBER_LABEL); + + mAdapter.changeCursor(mCursor); + mAdapter.onBindViewHolder(mViewHolder, 0); + + assertNameIs(mViewHolder, "John Doe"); + assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_WORK)); + } + + @MediumTest + public void testBindView_CustomLabel() { + createCallLogEntryWithCachedValues( + "John Doe", + Phone.TYPE_CUSTOM, + TEST_DEFAULT_CUSTOM_LABEL); + + mAdapter.changeCursor(mCursor); + mAdapter.onBindViewHolder(mViewHolder, 0); + + assertNameIs(mViewHolder, "John Doe"); + assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, TEST_DEFAULT_CUSTOM_LABEL); + } + + @MediumTest + public void testBindView_NumberOnlyDbCachedFormattedNumber() { + createCallLogEntryWithCachedValues( + TEST_NUMBER, + Calls.INCOMING_TYPE, + EMPTY_STRING, + TEST_CACHED_NUMBER_TYPE, + TEST_CACHED_NUMBER_LABEL, + TEST_FORMATTED_NUMBER, + false /* inject */); + + mAdapter.changeCursor(mCursor); + mAdapter.onBindViewHolder(mViewHolder, 0); + + assertNameIs(mViewHolder, TEST_FORMATTED_NUMBER); + } + + @MediumTest public void testBindVoicemailPromoCard() { createCallLogEntry(TEST_NUMBER_1); createCallLogEntry(TEST_NUMBER_1); @@ -271,51 +445,106 @@ public class CallLogAdapterTest extends AndroidTestCase { assertEquals(TEST_NUMBER_3, mViewHolder.number); } - /** Returns a contact info with default values. */ - private ContactInfo createContactInfo() { - ContactInfo info = new ContactInfo(); - info.number = TEST_NUMBER_1; - info.name = TEST_NAME; - info.type = TEST_NUMBER_TYPE; - info.label = TEST_NUMBER_LABEL; - return info; - } - - /** Returns a call log entry without cached values. */ private void createCallLogEntry() { - createCallLogEntry(TEST_NUMBER_1); + createCallLogEntry(TEST_NUMBER); } - private void createCallLogEntry(String testNumber) { - createCallLogEntry(testNumber, NO_VALUE_SET, NO_VALUE_SET, NO_VALUE_SET, NO_VALUE_SET); + private void createCallLogEntry(String testNumber) { + createCallLogEntry(testNumber, NO_VALUE_SET, NO_VALUE_SET); } private void createPrivateCallLogEntry() { - createCallLogEntry("", Calls.PRESENTATION_RESTRICTED, NO_VALUE_SET, 0, Calls.INCOMING_TYPE); + createCallLogEntry(EMPTY_STRING, Calls.PRESENTATION_RESTRICTED, Calls.INCOMING_TYPE); + } + + private void createUnknownCallLogEntry() { + createCallLogEntry(EMPTY_STRING, Calls.PRESENTATION_UNKNOWN, Calls.INCOMING_TYPE); } private void createVoicemailCallLogEntry() { - createCallLogEntry(TEST_NUMBER_1, NO_VALUE_SET, NO_VALUE_SET, NO_VALUE_SET, - Calls.VOICEMAIL_TYPE, true /* isVoicemail */); + createCallLogEntry(TEST_NUMBER, NO_VALUE_SET, Calls.VOICEMAIL_TYPE); } - private void createCallLogEntry( - String number, int presentation, long date, int duration, int type) { - createCallLogEntry(number, presentation, date, duration, type, false /* isVoicemail */); + private void createCallLogEntry(String number, int presentation, int type) { + Object[] values = getValues(number, presentation, type); + mCursor.addRow(values); } - private void createCallLogEntry( + private void createCallLogEntryWithCachedValues(boolean inject) { + createCallLogEntryWithCachedValues( + TEST_NUMBER, + NO_VALUE_SET, + TEST_CACHED_NAME, + TEST_CACHED_NUMBER_TYPE, + TEST_CACHED_NUMBER_LABEL, + EMPTY_STRING, + inject); + } + + private void createCallLogEntryWithCachedValues( + String cachedName, int cachedNumberType, String cachedNumberLabel) { + createCallLogEntryWithCachedValues( + TEST_NUMBER, + NO_VALUE_SET, + cachedName, + cachedNumberType, + cachedNumberLabel, + EMPTY_STRING, + false /* inject */); + } + + /** + * Inserts a new call log entry + * + * It includes the values for the cached contact associated with the number. + * + * @param number The phone number. + * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE. + * @param cachedName The name of the contact with this number + * @param cachedNumberType The type of the number, from the contact with this number. + * @param cachedNumberLabel The label of the number, from the contact with this number. + * @param cachedFormattedNumber The formatted number, from the contact with this number. + * @param inject Whether to inject the contact info into the adapter's ContactInfoCache. + */ + private void createCallLogEntryWithCachedValues( String number, - int presentation, - long date, - int duration, int type, - boolean isVoicemail) { + String cachedName, + int cachedNumberType, + String cachedNumberLabel, + String cachedFormattedNumber, + boolean inject) { + Object[] values = getValues(number, NO_VALUE_SET, type); + values[CallLogQuery.CACHED_NAME] = cachedName; + values[CallLogQuery.CACHED_NUMBER_TYPE] = cachedNumberType; + values[CallLogQuery.CACHED_NUMBER_LABEL] = cachedNumberLabel; + values[CallLogQuery.CACHED_FORMATTED_NUMBER] = cachedFormattedNumber; + + mCursor.addRow(values); + + if (inject) { + ContactInfo contactInfo = + createContactInfo(cachedName, cachedNumberType, cachedNumberLabel); + mAdapter.injectContactInfoForTest(number, TEST_COUNTRY_ISO, contactInfo); + } + } + + /** + * @param number The phone number. + * @param presentation Number representing display rules for "allowed", + * "payphone", "restricted", or "unknown". + * @param date In millisec since epoch. Use NOW to use the current time. + */ + private Object[] getValues( + String number, + int presentation, + int type) { Object[] values = CallLogQueryTestUtils.createTestValues(); - values[CallLogQuery.ID] = mCursorSize; + values[CallLogQuery.ID] = mCursor.getCount(); values[CallLogQuery.COUNTRY_ISO] = TEST_COUNTRY_ISO; - values[CallLogQuery.DATE] = date != NO_VALUE_SET ? date : new Date().getTime(); + values[CallLogQuery.DATE] = new Date().getTime(); + values[CallLogQuery.DURATION] = mRandom.nextInt(10 * 60); if (!TextUtils.isEmpty(number)) { values[CallLogQuery.NUMBER] = number; @@ -323,38 +552,68 @@ public class CallLogAdapterTest extends AndroidTestCase { if (presentation != NO_VALUE_SET) { values[CallLogQuery.NUMBER_PRESENTATION] = presentation; } - if (duration != NO_VALUE_SET) { - values[CallLogQuery.DURATION] = (duration < 0) ? mRandom.nextInt(10 * 60) : duration; - } if (type != NO_VALUE_SET) { values[CallLogQuery.CALL_TYPE] = type; } - if (isVoicemail) { - values[CallLogQuery.VOICEMAIL_URI] = - ContentUris.withAppendedId(VoicemailContract.Voicemails.CONTENT_URI, mCursorSize); + if (type == Calls.VOICEMAIL_TYPE) { + values[CallLogQuery.VOICEMAIL_URI] = ContentUris.withAppendedId( + VoicemailContract.Voicemails.CONTENT_URI, mCursor.getCount()); } - mCursor.addRow(values); - mCursorSize++; + return values; } - // Returns a call log entry with a cached values. - private Object[] createCallLogEntryWithCachedValues() { - Object[] values = CallLogQueryTestUtils.createTestValues(); - values[CallLogQuery.NUMBER] = TEST_NUMBER_1; - values[CallLogQuery.COUNTRY_ISO] = TEST_COUNTRY_ISO; - values[CallLogQuery.CACHED_NAME] = TEST_NAME; - values[CallLogQuery.CACHED_NUMBER_TYPE] = TEST_NUMBER_TYPE; - values[CallLogQuery.CACHED_NUMBER_LABEL] = TEST_NUMBER_LABEL; - return values; + private ContactInfo createContactInfo() { + return createContactInfo( + TEST_CACHED_NAME, + TEST_CACHED_NUMBER_TYPE, + TEST_CACHED_NUMBER_LABEL); } - // Asserts that the name text view is shown and contains the given text./ + /** Returns a contact info with default values. */ + private ContactInfo createContactInfo(String name, int type, String label) { + ContactInfo info = new ContactInfo(); + info.number = TEST_NUMBER; + info.name = name; + info.type = type; + info.label = label; + info.formattedNumber = TEST_FORMATTED_NUMBER; + info.normalizedNumber = TEST_NUMBER; + info.lookupUri = TEST_LOOKUP_URI; + return info; + } + + // Asserts that the name text view is shown and contains the given text. private void assertNameIs(CallLogListItemViewHolder viewHolder, String name) { assertEquals(View.VISIBLE, viewHolder.phoneCallDetailsViews.nameView.getVisibility()); assertEquals(name, viewHolder.phoneCallDetailsViews.nameView.getText()); } + // Asserts that the label text view contains the given text. + private void assertLabel( + CallLogListItemViewHolder viewHolder, CharSequence number, CharSequence label) { + if (label != null) { + assertTrue(viewHolder.phoneCallDetailsViews.callLocationAndDate.getText() + .toString().contains(label)); + } + } + + private void assertHasCallAction(CallLogListItemViewHolder viewHolder) { + // The primaryActionView tag is set when the ViewHolder is binded. If it is possible + // to place a call to the phone number, a call intent will have been created which + // starts a phone call to the entry's number. + IntentProvider intentProvider = + (IntentProvider) viewHolder.primaryActionButtonView.getTag(); + Intent intent = intentProvider.getIntent(getContext()); + assertEquals(TestConstants.CALL_INTENT_ACTION, intent.getAction()); + assertEquals(Uri.parse("tel:" + TEST_NUMBER), intent.getData()); + } + + /** Returns the label associated with a given phone type. */ + private CharSequence getTypeLabel(int phoneType) { + return Phone.getTypeLabel(getContext().getResources(), phoneType, ""); + } + /// Subclass of {@link CallLogAdapter} used in tests to intercept certain calls. private static final class TestCallLogAdapter extends CallLogAdapter { public TestCallLogAdapter(Context context, CallFetcher callFetcher, diff --git a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java b/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java deleted file mode 100644 index aa4ad80b7..000000000 --- a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java +++ /dev/null @@ -1,545 +0,0 @@ -/* - * Copyright (C) 2009 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.dialer.calllog; - -import android.app.FragmentManager; -import android.app.FragmentTransaction; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.database.MatrixCursor; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.net.Uri; -import android.provider.CallLog.Calls; -import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.provider.VoicemailContract; -import android.telephony.PhoneNumberUtils; -import android.telephony.TelephonyManager; -import android.test.ActivityInstrumentationTestCase2; -import android.test.suitebuilder.annotation.LargeTest; -import android.test.suitebuilder.annotation.MediumTest; -import android.util.Log; -import android.view.View; -import android.widget.FrameLayout; - -import com.android.contacts.common.test.FragmentTestActivity; -import com.android.dialer.CallDetailActivity; -import com.android.dialer.R; -import com.android.dialer.util.TestConstants; - -import java.util.Date; -import java.util.Formatter; -import java.util.HashMap; -import java.util.Random; - -/** - * Tests for the contact call list activity. - * - * Running all tests: - * - * runtest contacts - * or - * adb shell am instrument \ - * -w com.android.dialer.tests/android.test.InstrumentationTestRunner - */ -@LargeTest -public class CallLogFragmentTest extends ActivityInstrumentationTestCase2 { - private static final int RAND_DURATION = -1; - private static final long NOW = -1L; - - /** A test value for the URI of a contact. */ - private static final Uri TEST_LOOKUP_URI = Uri.parse("content://contacts/2"); - /** A test value for the country ISO of the phone number in the call log. */ - private static final String TEST_COUNTRY_ISO = "US"; - /** A phone number to be used in tests. */ - private static final String TEST_NUMBER = "12125551000"; - /** The formatted version of {@link #TEST_NUMBER}. */ - private static final String TEST_FORMATTED_NUMBER = "1 212-555-1000"; - - private static final String TEST_DEFAULT_CUSTOM_LABEL = "myLabel"; - - /** The activity in which we are hosting the fragment. */ - private FragmentTestActivity mActivity; - private CallLogFragment mFragment; - private FrameLayout mParentView; - /** - * The adapter used by the fragment to build the rows in the call log. We use it with our own in - * memory database. - */ - private CallLogAdapter mAdapter; - private String mVoicemail; - - // In memory array to hold the rows corresponding to the 'calls' table. - private MatrixCursor mCursor; - private int mIndex; // Of the next row. - - private Random mRnd; - - // An item in the call list. All the methods performing checks use it. - private CallLogListItemViewHolder mItem; - - // The list of view holderss representing the data in the DB, in reverse order from the DB. - private CallLogListItemViewHolder[] mList; - - public CallLogFragmentTest() { - super(FragmentTestActivity.class); - mIndex = 1; - mRnd = new Random(); - } - - @Override - public void setUp() { - mActivity = getActivity(); - // Needed by the CallLogFragment. - mActivity.setTheme(R.style.DialtactsTheme); - - // Create the fragment and load it into the activity. - mFragment = new CallLogFragment(); - FragmentManager fragmentManager = mActivity.getFragmentManager(); - FragmentTransaction transaction = fragmentManager.beginTransaction(); - transaction.add(FragmentTestActivity.LAYOUT_ID, mFragment); - transaction.commitAllowingStateLoss(); - // Wait for the fragment to be loaded. - getInstrumentation().waitForIdleSync(); - - final TelephonyManager telephonyManager = - (TelephonyManager) mActivity.getSystemService(Context.TELEPHONY_SERVICE); - mVoicemail = telephonyManager.getVoiceMailNumber(); - mAdapter = mFragment.getAdapter(); - // Do not process requests for details during tests. This would start a background thread, - // which makes the tests flaky. - mAdapter.disableRequestProcessingForTest(); - mAdapter.pauseCache(); - mParentView = new FrameLayout(mActivity); - mCursor = new MatrixCursor(CallLogQuery._PROJECTION); - - getInstrumentation().runOnMainSync(new Runnable() { - @Override - public void run() { - mAdapter.changeCursor(mCursor); - } - }); - getInstrumentation().waitForIdleSync(); - } - - /** - * Checks that the call icon is not visible for private and - * unknown numbers. - * Use 2 passes, one where new viewHolder are created and one where - * half of the total viewHolder are updated and the other half created. - */ - @MediumTest - public void testCallViewIsNotVisibleForPrivateAndUnknownNumbers() { - final int SIZE = 50; - mList = new CallLogListItemViewHolder[SIZE]; - - // Insert the first batch of entries. - mCursor.moveToFirst(); - insertRandomEntries(SIZE / 2); - int startOfSecondBatch = mCursor.getPosition(); - - buildViewListFromDb(); - checkCallStatus(); - - // Append the rest of the entries. We keep the first set of - // viewHolder around so they get updated and not built from - // scratch, this exposes some bugs that are not there when the - // call log is launched for the 1st time but show up when the - // call log gets updated afterwards. - mCursor.move(startOfSecondBatch); - insertRandomEntries(SIZE / 2); - - buildViewListFromDb(); - checkCallStatus(); - } - - @MediumTest - public void testBindView_NumberOnlyDbCachedFormattedNumber() { - mCursor.moveToFirst(); - Object[] values = getValuesToInsert(TEST_NUMBER, - Calls.PRESENTATION_ALLOWED, NOW, 0, Calls.INCOMING_TYPE); - values[CallLogQuery.CACHED_FORMATTED_NUMBER] = TEST_FORMATTED_NUMBER; - insertValues(values); - CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder) - mAdapter.onCreateViewHolder(mParentView, /* viewType */ 0); - bindViewForTest(viewHolder); - - assertNameIs(viewHolder, TEST_FORMATTED_NUMBER); - } - - @MediumTest - public void testBindView_WithCachedName() { - mCursor.moveToFirst(); - insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE, - "John Doe", Phone.TYPE_HOME, TEST_DEFAULT_CUSTOM_LABEL); - CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder) - mAdapter.onCreateViewHolder(mParentView, /* viewType */ 0); - bindViewForTest(viewHolder); - - assertNameIs(viewHolder, "John Doe"); - assertLabel(viewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME)); - } - - @MediumTest - public void testBindView_UriNumber() { - mCursor.moveToFirst(); - insertWithCachedValues("sip:johndoe@gmail.com", NOW, 0, Calls.INCOMING_TYPE, - "John Doe", Phone.TYPE_HOME, TEST_DEFAULT_CUSTOM_LABEL); - CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder) - mAdapter.onCreateViewHolder(mParentView, /* viewType */ 0); - bindViewForTest(viewHolder); - - assertNameIs(viewHolder, "John Doe"); - assertLabel(viewHolder, "sip:johndoe@gmail.com", "sip:johndoe@gmail.com"); - } - - @MediumTest - public void testBindView_HomeLabel() { - mCursor.moveToFirst(); - insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE, - "John Doe", Phone.TYPE_HOME, TEST_DEFAULT_CUSTOM_LABEL); - CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder) - mAdapter.onCreateViewHolder(mParentView, /* viewType */ 0); - bindViewForTest(viewHolder); - - assertNameIs(viewHolder, "John Doe"); - assertLabel(viewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME)); - } - - @MediumTest - public void testBindView_WorkLabel() { - mCursor.moveToFirst(); - insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE, - "John Doe", Phone.TYPE_WORK, TEST_DEFAULT_CUSTOM_LABEL); - CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder) - mAdapter.onCreateViewHolder(mParentView, /* viewType */ 0); - bindViewForTest(viewHolder); - - assertNameIs(viewHolder, "John Doe"); - assertLabel(viewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_WORK)); - } - - @MediumTest - public void testBindView_CustomLabel() { - mCursor.moveToFirst(); - String numberLabel = "My label"; - insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE, - "John Doe", Phone.TYPE_CUSTOM, numberLabel); - CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder) - mAdapter.onCreateViewHolder(mParentView, /* viewType */ 0); - bindViewForTest(viewHolder); - - assertNameIs(viewHolder, "John Doe"); - assertLabel(viewHolder, TEST_FORMATTED_NUMBER, numberLabel); - } - - - /** Returns the label associated with a given phone type. */ - private CharSequence getTypeLabel(int phoneType) { - return Phone.getTypeLabel(getActivity().getResources(), phoneType, ""); - } - - // - // HELPERS to check conditions on the DB/viewHolder - // - /** - * Go over the viewHolder in the list and check to ensure that - * callable numbers have an associated call intent, where numbers - * which are not callable have a null intent. - */ - private void checkCallStatus() { - for (int i = 0; i < mList.length; i++) { - if (null == mList[i]) { - break; - } - mItem = (CallLogListItemViewHolder) mList[i]; - int presentation = getPhoneNumberPresentationForListEntry(i); - if (presentation == Calls.PRESENTATION_RESTRICTED || - presentation == Calls.PRESENTATION_UNKNOWN) { - //If number is not callable, the primary action view should have a null tag. - assertNull(mItem.primaryActionButtonView.getTag()); - } else { - //If the number is callable, the primary action view should have a non-null tag. - assertNotNull(mItem.primaryActionButtonView.getTag()); - - IntentProvider intentProvider = - (IntentProvider) mItem.primaryActionButtonView.getTag(); - Intent callIntent = intentProvider.getIntent(mActivity); - - //The intent should be to make the call - assertEquals(TestConstants.CALL_INTENT_ACTION, callIntent.getAction()); - } - } - } - - // - // HELPERS to setup the tests. - // - - /** - * Get the Bitmap from the icons in the contacts package. - */ - private Bitmap getBitmap(String resName) { - Resources r = mActivity.getResources(); - int resid = r.getIdentifier(resName, "drawable", - getInstrumentation().getTargetContext().getPackageName()); - BitmapDrawable d = (BitmapDrawable) r.getDrawable(resid); - assertNotNull(d); - return d.getBitmap(); - } - - // - // HELPERS to build/update the call entries (viewHolder) from the DB. - // - - /** - * Read the DB and foreach call either update the existing view if - * one exists already otherwise create one. - * The list is build from a DESC view of the DB (last inserted entry is first). - */ - private void buildViewListFromDb() { - int i = 0; - mCursor.moveToLast(); - while (!mCursor.isBeforeFirst()) { - if (null == mList[i]) { - mList[i] = (CallLogListItemViewHolder) - mAdapter.onCreateViewHolder(mParentView, /* itemType */ 0); - } - // Bind to the proper position, despite iterating in reverse. - bindViewForTest(mList[i], mCursor.getCount() - i - 1); - mCursor.moveToPrevious(); - i++; - } - } - - /** Returns the number presentation associated with the given entry in {{@link #mList}. */ - private int getPhoneNumberPresentationForListEntry(int index) { - // The entries are added backward, so count from the end of the cursor. - mCursor.moveToPosition(mCursor.getCount() - index - 1); - return mCursor.getInt(CallLogQuery.NUMBER_PRESENTATION); - } - - // - // HELPERS to insert numbers in the call log DB. - // - - /** - * Bind a call log entry view for testing purposes. Also inflates the action view stub so - * unit tests can access the buttons contained within. - * - * @param view The current call log row. - * @param position The position of the item. - */ - private void bindViewForTest(final CallLogListItemViewHolder viewHolder, int position) { - mAdapter.onBindViewHolder(viewHolder, position); - getInstrumentation().runOnMainSync(new Runnable() { - @Override - public void run() { - viewHolder.inflateActionViewStub(); - } - }); - getInstrumentation().waitForIdleSync(); - } - - private void bindViewForTest(CallLogListItemViewHolder viewHolder) { - bindViewForTest(viewHolder, /* position */ 0); - } - - /** - * Insert a certain number of random numbers in the DB. Makes sure - * there is at least one private and one unknown number in the DB. - * @param num Of entries to be inserted. - */ - private void insertRandomEntries(int num) { - if (num < 10) { - throw new IllegalArgumentException("num should be >= 10"); - } - boolean privateOrUnknownOrVm[]; - privateOrUnknownOrVm = insertRandomRange(0, num - 2); - - if (privateOrUnknownOrVm[0] && privateOrUnknownOrVm[1]) { - insertRandomRange(num - 2, num); - } else { - insertPrivate(NOW, RAND_DURATION); - insertUnknown(NOW, RAND_DURATION); - } - } - - /** - * Insert a new call entry in the test DB. - * - * It includes the values for the cached contact associated with the number. - * - * @param number The phone number. - * @param date In millisec since epoch. Use NOW to use the current time. - * @param duration In seconds of the call. Use RAND_DURATION to pick a random one. - * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE. - * @param cachedName the name of the contact with this number - * @param cachedNumberType the type of the number, from the contact with this number - * @param cachedNumberLabel the label of the number, from the contact with this number - */ - private void insertWithCachedValues(String number, long date, int duration, int type, - String cachedName, int cachedNumberType, String cachedNumberLabel) { - insert(number, Calls.PRESENTATION_ALLOWED, date, duration, type); - ContactInfo contactInfo = new ContactInfo(); - contactInfo.lookupUri = TEST_LOOKUP_URI; - contactInfo.name = cachedName; - contactInfo.type = cachedNumberType; - contactInfo.label = cachedNumberLabel; - String formattedNumber = PhoneNumberUtils.formatNumber(number, TEST_COUNTRY_ISO); - if (formattedNumber == null) { - formattedNumber = number; - } - contactInfo.formattedNumber = formattedNumber; - contactInfo.normalizedNumber = number; - contactInfo.photoId = 0; - mAdapter.injectContactInfoForTest(number, TEST_COUNTRY_ISO, contactInfo); - } - - /** - * Insert a new call entry in the test DB. - * @param number The phone number. - * @param presentation Number representing display rules for "allowed", - * "payphone", "restricted", or "unknown". - * @param date In millisec since epoch. Use NOW to use the current time. - * @param duration In seconds of the call. Use RAND_DURATION to pick a random one. - * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE. - */ - private void insert(String number, int presentation, long date, int duration, int type) { - insertValues(getValuesToInsert(number, presentation, date, duration, type)); - } - - /** Inserts the given values in the cursor. */ - private void insertValues(Object[] values) { - mCursor.addRow(values); - ++mIndex; - } - - /** - * Returns the values for a new call entry. - * - * @param number The phone number. - * @param presentation Number representing display rules for "allowed", - * "payphone", "restricted", or "unknown". - * @param date In millisec since epoch. Use NOW to use the current time. - * @param duration In seconds of the call. Use RAND_DURATION to pick a random one. - * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE. - */ - private Object[] getValuesToInsert(String number, int presentation, - long date, int duration, int type) { - Object[] values = CallLogQueryTestUtils.createTestValues(); - values[CallLogQuery.ID] = mIndex; - values[CallLogQuery.NUMBER] = number; - values[CallLogQuery.NUMBER_PRESENTATION] = presentation; - values[CallLogQuery.DATE] = date == NOW ? new Date().getTime() : date; - values[CallLogQuery.DURATION] = duration < 0 ? mRnd.nextInt(10 * 60) : duration; - if (mVoicemail != null && mVoicemail.equals(number)) { - assertEquals(Calls.OUTGOING_TYPE, type); - } - values[CallLogQuery.CALL_TYPE] = type; - values[CallLogQuery.COUNTRY_ISO] = TEST_COUNTRY_ISO; - return values; - } - - /** - * Insert a new private call entry in the test DB. - * @param date In millisec since epoch. Use NOW to use the current time. - * @param duration In seconds of the call. Use RAND_DURATION to pick a random one. - */ - private void insertPrivate(long date, int duration) { - insert("", Calls.PRESENTATION_RESTRICTED, date, duration, Calls.INCOMING_TYPE); - } - - /** - * Insert a new unknown call entry in the test DB. - * @param date In millisec since epoch. Use NOW to use the current time. - * @param duration In seconds of the call. Use RAND_DURATION to pick a random one. - */ - private void insertUnknown(long date, int duration) { - insert("", Calls.PRESENTATION_UNKNOWN, date, duration, Calls.INCOMING_TYPE); - } - - /** - * Insert a new call to voicemail entry in the test DB. - * @param date In millisec since epoch. Use NOW to use the current time. - * @param duration In seconds of the call. Use RAND_DURATION to pick a random one. - */ - private void insertCalltoVoicemail(long date, int duration) { - // mVoicemail may be null - if (mVoicemail != null) { - insert(mVoicemail, Calls.PRESENTATION_ALLOWED, date, duration, Calls.OUTGOING_TYPE); - } - } - - /** - * Insert a range [start, end) of random numbers in the DB. For - * each row, there is a 1/10 probability that the number will be - * marked as PRIVATE or UNKNOWN or VOICEMAIL. For regular numbers, a number is - * inserted, its last 4 digits will be the number of the iteration - * in the range. - * @param start Of the range. - * @param end Of the range (excluded). - * @return An array with 2 booleans [0 = private number, 1 = - * unknown number, 2 = voicemail] to indicate if at least one - * private or unknown or voicemail number has been inserted. Since - * the numbers are random some tests may want to enforce the - * insertion of such numbers. - */ - // TODO: Should insert numbers with contact entries too. - private boolean[] insertRandomRange(int start, int end) { - boolean[] privateOrUnknownOrVm = new boolean[] {false, false, false}; - - for (int i = start; i < end; i++ ) { - int type = mRnd.nextInt(10); - - if (0 == type) { - insertPrivate(NOW, RAND_DURATION); - privateOrUnknownOrVm[0] = true; - } else if (1 == type) { - insertUnknown(NOW, RAND_DURATION); - privateOrUnknownOrVm[1] = true; - } else if (2 == type) { - insertCalltoVoicemail(NOW, RAND_DURATION); - privateOrUnknownOrVm[2] = true; - } else { - int inout = mRnd.nextBoolean() ? Calls.OUTGOING_TYPE : Calls.INCOMING_TYPE; - final Formatter formatter = new Formatter(); - String number = formatter.format("1800123%04d", i).toString(); - formatter.close(); - insert(number, Calls.PRESENTATION_ALLOWED, NOW, RAND_DURATION, inout); - } - } - return privateOrUnknownOrVm; - } - - /** Asserts that the name text view is shown and contains the given text. */ - private void assertNameIs(CallLogListItemViewHolder viewHolder, String name) { - assertEquals(View.VISIBLE, viewHolder.phoneCallDetailsViews.nameView.getVisibility()); - assertEquals(name, viewHolder.phoneCallDetailsViews.nameView.getText().toString()); - } - - /** Asserts that the label text view contains the given text. */ - private void assertLabel(CallLogListItemViewHolder viewHolder, CharSequence number, - CharSequence label) { - if (label != null) { - assertTrue(viewHolder.phoneCallDetailsViews.callLocationAndDate.getText() - .toString().contains(label)); - } - } -} -- cgit v1.2.3