diff options
author | Marcus Hagerott <mhagerott@google.com> | 2016-12-20 14:12:37 -0800 |
---|---|---|
committer | Marcus Hagerott <mhagerott@google.com> | 2016-12-20 15:58:59 -0800 |
commit | 2054306415223d2f68972d8f663334c2956edd38 (patch) | |
tree | 69ae4b20e873e29e2e060df412b6f80dd3ff25a2 /tests | |
parent | 24b4613a00504bd2b6d81a7edb7261e0913f932e (diff) | |
download | Contacts-2054306415223d2f68972d8f663334c2956edd38.tar.gz |
Unsuppress SIM import UI test
Test: ran GoogleContactsTests
Change-Id: I52e286c35a7b3f95bc2d8ed469b9e4bf473f8f64
Diffstat (limited to 'tests')
-rw-r--r-- | tests/src/com/android/contacts/activities/SimImportActivityTest.java | 158 | ||||
-rw-r--r-- | tests/src/com/android/contacts/test/mocks/ForwardingContentProvider.java | 206 |
2 files changed, 323 insertions, 41 deletions
diff --git a/tests/src/com/android/contacts/activities/SimImportActivityTest.java b/tests/src/com/android/contacts/activities/SimImportActivityTest.java index 8362b9fac..64b86e21e 100644 --- a/tests/src/com/android/contacts/activities/SimImportActivityTest.java +++ b/tests/src/com/android/contacts/activities/SimImportActivityTest.java @@ -1,18 +1,37 @@ +/* + * Copyright (C) 2016 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.contacts.activities; import static com.android.contacts.tests.ContactsMatchers.DataCursor.hasMimeType; import static com.android.contacts.tests.ContactsMatchers.hasRowMatching; import static com.android.contacts.tests.ContactsMatchers.hasValueForColumn; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import android.annotation.TargetApi; import android.app.Activity; import android.app.Instrumentation; +import android.content.BroadcastReceiver; +import android.content.ContentProviderClient; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.database.Cursor; import android.os.Build; @@ -20,47 +39,54 @@ import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.Data; import android.support.test.InstrumentationRegistry; -import android.support.test.filters.MediumTest; +import android.support.test.filters.LargeTest; import android.support.test.filters.SdkSuppress; -import android.support.test.filters.Suppress; import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.Until; +import android.support.v4.content.LocalBroadcastManager; +import android.telephony.TelephonyManager; +import android.test.mock.MockContentResolver; +import com.android.contacts.SimImportService; import com.android.contacts.database.SimContactDao; +import com.android.contacts.database.SimContactDaoImpl; import com.android.contacts.model.AccountTypeManager; import com.android.contacts.model.SimCard; import com.android.contacts.model.SimContact; import com.android.contacts.model.account.AccountWithDataSet; +import com.android.contacts.test.mocks.ForwardingContentProvider; +import com.android.contacts.test.mocks.MockContentProvider; import com.android.contacts.tests.AccountsTestHelper; import com.android.contacts.tests.ContactsMatchers; import com.android.contacts.tests.FakeSimContactDao; import com.android.contacts.tests.StringableCursor; - +import com.google.common.base.Function; import com.google.common.base.Functions; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; -import org.hamcrest.BaseMatcher; -import org.hamcrest.Description; -import org.hamcrest.Matcher; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.TimeUnit; + /** * UI Tests for {@link SimImportActivity} * * These should probably be converted to espresso tests because espresso does a better job of * waiting for the app to be idle once espresso library is added */ -@MediumTest +@LargeTest @RunWith(AndroidJUnit4.class) @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M) @TargetApi(Build.VERSION_CODES.M) public class SimImportActivityTest { - public static final int TIMEOUT = 1000; + public static final int TIMEOUT = 3000; private Context mContext; private UiDevice mDevice; private Instrumentation mInstrumentation; @@ -111,7 +137,7 @@ public class SimImportActivityTest { assertTrue(mDevice.hasObject(By.text("Sim One"))); assertTrue(mDevice.hasObject(By.text("Sim Two"))); assertTrue(mDevice.hasObject(By.text("5550103"))); -} + } @Test public void shouldHaveEmptyState() { @@ -160,27 +186,64 @@ public class SimImportActivityTest { assertTrue(mDevice.wait(Until.hasObject(By.textStartsWith("Name One")), TIMEOUT)); } - - // TODO: fix this test. This doesn't work because AccountTypeManager returns a stale account - // list (it doesn't contain the accounts added during the current test run). - // Could use MockAccountTypeManager but probably ought to look at improving how - // AccountTypeManager updates it's account list. - @Suppress + /** + * Tests a complete import flow + * + * <p>Test case outline:</p> + * <ul> + * <li>Load SIM contacts + * <li>Change to a specific target account + * <li>Deselect 3 specific SIM contacts + * <li>Rotate the screen to landscape + * <li>Rotate the screen back to portrait + * <li>Press the import button + * <li>Wait for import to complete + * <li>Query contacts in target account and verify that they match selected contacts + * <li>Start import activity again + * <li>Switch to target account + * <li>Verify that previously imported contacts are disabled and not checked + * </ul> + * + * <p>This mocks out the IccProvider and stubs the canReadSimContacts method to make it work on + * an emulator but otherwise uses real dependency. + * </p> + */ @Test - public void selectionsAreImportedAndDisabledOnSubsequentViews() throws Exception { + public void selectionsAreImportedAndDisabledOnSubsequentImports() throws Exception { // Clear out the instance so that it will have the most recent accounts when reloaded AccountTypeManager.setInstanceForTest(null); final AccountWithDataSet targetAccount = mAccountHelper.addTestAccount( mAccountHelper.generateAccountName("SimImportActivity_target_")); - mDao.addSim(someSimCard(), - new SimContact(1, "Import One", "5550101"), - new SimContact(2, "Skip Two", "5550102"), - new SimContact(3, "Import Three", "5550103"), - new SimContact(4, "Skip Four", "5550104"), - new SimContact(5, "Skip Five", "5550105"), - new SimContact(6, "Import Six", "5550106")); + final MockContentProvider iccProvider = new MockContentProvider(); + iccProvider.expect(MockContentProvider.Query.forAnyUri()) + .withDefaultProjection(new String[] {SimContactDaoImpl._ID, SimContactDaoImpl.NAME, + SimContactDaoImpl.NUMBER, SimContactDaoImpl.EMAILS }) + .anyNumberOfTimes() + .returnRow(toCursorRow(new SimContact(1, "Import One", "5550101"))) + .returnRow(toCursorRow(new SimContact(2, "Skip Two", "5550102"))) + .returnRow(toCursorRow(new SimContact(3, "Import Three", "5550103"))) + .returnRow(toCursorRow(new SimContact(4, "Skip Four", "5550104"))) + .returnRow(toCursorRow(new SimContact(5, "Skip Five", "5550105"))) + .returnRow(toCursorRow(new SimContact(6, "Import Six", "5550106"))); + final MockContentResolver mockResolver = new MockContentResolver(); + mockResolver.addProvider("icc", iccProvider); + final ContentProviderClient contactsProviderClient = mContext.getContentResolver() + .acquireContentProviderClient(ContactsContract.AUTHORITY); + mockResolver.addProvider(ContactsContract.AUTHORITY, new ForwardingContentProvider( + contactsProviderClient)); + + SimContactDao.setFactoryForTest(new Function<Context, SimContactDao>() { + @Override + public SimContactDao apply(Context input) { + final SimContactDaoImpl spy = spy(new SimContactDaoImpl( + mContext, mockResolver, + (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))); + when(spy.canReadSimContacts()).thenReturn(true); + return spy; + } + }); mActivity = mInstrumentation.startActivitySync( new Intent(mContext, SimImportActivity.class) @@ -190,9 +253,11 @@ public class SimImportActivityTest { mDevice.findObject(By.desc("Show more")).clickAndWait(Until.newWindow(), TIMEOUT); mDevice.findObject(By.textStartsWith("SimImportActivity_target_")).click(); - mDevice.waitForIdle(); + + assertTrue(mDevice.wait(Until.hasObject(By.text("Skip Two")), TIMEOUT)); mDevice.findObject(By.text("Skip Two")).click(); + mDevice.findObject(By.text("Skip Four")).click(); mDevice.findObject(By.text("Skip Five")).click(); mDevice.waitForIdle(); @@ -204,8 +269,12 @@ public class SimImportActivityTest { mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER); mDevice.wait(Until.hasObject(By.text("Import One")), TIMEOUT); + ListenableFuture<?> nextImportFuture = nextImportCompleteBroadcast(); + mDevice.findObject(By.text("IMPORT").clickable(true)).click(); - mDevice.waitForIdle(); + + // Block until import completes + nextImportFuture.get(TIMEOUT, TimeUnit.MILLISECONDS); final Cursor cursor = new StringableCursor( mContext.getContentResolver().query(Data.CONTENT_URI, null, @@ -237,37 +306,44 @@ public class SimImportActivityTest { cursor.close(); - mInstrumentation.startActivitySync( + mActivity = mInstrumentation.startActivitySync( new Intent(mContext, SimImportActivity.class) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); assertTrue(mDevice.wait(Until.hasObject(By.text("Import One")), TIMEOUT)); - mDevice.findObject(By.descStartsWith("Saving to")).clickAndWait(Until.newWindow(), TIMEOUT); + mDevice.findObject(By.descStartsWith("Show more")).clickAndWait(Until.newWindow(), TIMEOUT); mDevice.findObject(By.textContains(targetAccount.name)).click(); mDevice.waitForIdle(); - assertTrue(mDevice.hasObject(By.text("Import One").checked(false).enabled(false))); + assertTrue(mDevice.wait(Until.hasObject(By.text("Import One").checked(false).enabled(false)), TIMEOUT)); assertTrue(mDevice.hasObject(By.text("Import Three").checked(false).enabled(false))); assertTrue(mDevice.hasObject(By.text("Import Six").checked(false).enabled(false))); - } - private SimCard someSimCard() { - return new SimCard("id", 1, "Carrier", "SIM", "18005550101", "us"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + contactsProviderClient.close(); + } } - private Matcher<SimContact> withContactId(final long id) { - return new BaseMatcher<SimContact>() { + private ListenableFuture<Intent> nextImportCompleteBroadcast() { + final SettableFuture<Intent> result = SettableFuture.create(); + final BroadcastReceiver receiver = new BroadcastReceiver() { @Override - public boolean matches(Object o) { - return (o instanceof SimContact) && ((SimContact) o).getId() == id; + public void onReceive(Context context, Intent intent) { + result.set(intent); + LocalBroadcastManager.getInstance(mContext).unregisterReceiver(this); } + }; + LocalBroadcastManager.getInstance(mContext).registerReceiver(receiver, new IntentFilter( + SimImportService.BROADCAST_SIM_IMPORT_COMPLETE)); + return result; + } + private Object[] toCursorRow(SimContact contact) { + return new Object[] { contact.getId(), contact.getName(), contact.getPhone(), null }; + } - @Override - public void describeTo(Description description) { - description.appendText("Expected SimContact with id=" + id); - } - }; + private SimCard someSimCard() { + return new SimCard("id", 1, "Carrier", "SIM", "18005550101", "us"); } } diff --git a/tests/src/com/android/contacts/test/mocks/ForwardingContentProvider.java b/tests/src/com/android/contacts/test/mocks/ForwardingContentProvider.java new file mode 100644 index 000000000..b6ca983c9 --- /dev/null +++ b/tests/src/com/android/contacts/test/mocks/ForwardingContentProvider.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2016 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.contacts.test.mocks; + +import android.content.ContentProviderClient; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentValues; +import android.content.OperationApplicationException; +import android.content.res.AssetFileDescriptor; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.support.annotation.Nullable; + +import java.io.FileNotFoundException; +import java.util.ArrayList; + +/** + * Forwards calls to a {@link ContentProviderClient} + * + * <p>This allows mixing use of the system content providers in a + * {@link android.test.mock.MockContentResolver} + * </p> + */ +public class ForwardingContentProvider extends android.test.mock.MockContentProvider { + + private final ContentProviderClient mClient; + + public ForwardingContentProvider(ContentProviderClient client) { + mClient = client; + } + + @Override + public synchronized Cursor query(Uri url, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + try { + return mClient.query(url, projection, selection, selectionArgs, sortOrder); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + @Nullable + @Override + public synchronized Cursor query(Uri url, String[] projection, String selection, + String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal) { + try { + return mClient.query(url, projection, selection, selectionArgs, sortOrder, + cancellationSignal); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + @Override + public synchronized String getType(Uri url) { + try { + return mClient.getType(url); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + @Override + public synchronized String[] getStreamTypes(Uri url, String mimeTypeFilter) { + try { + return mClient.getStreamTypes(url, mimeTypeFilter); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + @Override + public synchronized Uri insert(Uri url, ContentValues initialValues) { + try { + return mClient.insert(url, initialValues); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + @Override + public synchronized int bulkInsert(Uri url, ContentValues[] initialValues) { + try { + return mClient.bulkInsert(url, initialValues); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + @Override + public synchronized int delete(Uri url, String selection, String[] selectionArgs) { + try { + return mClient.delete(url, selection, selectionArgs); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + @Override + public synchronized int update(Uri url, ContentValues values, + String selection, String[] selectionArgs) { + try { + return mClient.update(url, values, selection, selectionArgs); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + @Nullable + @Override + public synchronized ParcelFileDescriptor openFile(Uri url, String mode) { + try { + return mClient.openFile(url, mode); + } catch (RemoteException|FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Nullable + @Override + public synchronized ParcelFileDescriptor openFile(Uri url, String mode, + CancellationSignal signal) { + try { + return mClient.openFile(url, mode, signal); + } catch (RemoteException|FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Nullable + @Override + public synchronized AssetFileDescriptor openAssetFile(Uri url, String mode) { + try { + return mClient.openAssetFile(url, mode); + } catch (RemoteException|FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Nullable + @Override + public synchronized AssetFileDescriptor openAssetFile(Uri url, String mode, + CancellationSignal signal) { + try { + return mClient.openAssetFile(url, mode, signal); + } catch (RemoteException|FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + public synchronized AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri, String mimeType, + Bundle opts) { + try { + return mClient.openTypedAssetFileDescriptor(uri, mimeType, opts); + } catch (RemoteException|FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + public synchronized AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri, String mimeType, + Bundle opts, CancellationSignal signal) { + try { + return mClient.openTypedAssetFileDescriptor(uri, mimeType, opts, signal); + } catch (RemoteException|FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Override + public synchronized ContentProviderResult[] applyBatch( + ArrayList<ContentProviderOperation> operations) { + try { + return mClient.applyBatch(operations); + } catch (RemoteException|OperationApplicationException e) { + throw new RuntimeException(e); + } + } + + @Nullable + @Override + public synchronized Bundle call(String method, String arg, Bundle extras) { + try { + return mClient.call(method, arg, extras); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } +} |