Fixing ContactDeletionInteractionTest
Bug: 3330176
Change-Id: I6726e74440cd7cb8c16d169a97da5042f77c9463
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c0d9240..617d5c7 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -457,6 +457,12 @@
</intent-filter>
</activity>
+ <activity android:name=".test.FragmentTestActivity">
+ <intent-filter>
+ <category android:name="android.intent.category.TEST" />
+ </intent-filter>
+ </activity>
+
<!-- Stub service used to keep our process alive long enough for
background threads to finish their operations. -->
<service
diff --git a/res/values/ids.xml b/res/values/ids.xml
index fd82fe2..1a553d1 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -37,6 +37,7 @@
<!-- For ContactDeletionInteraction -->
<item type="id" name="dialog_delete_contact_confirmation"/>
+ <item type="id" name="dialog_delete_contact_loader_id" />
<!-- For ImportExportInteraction -->
<item type="id" name="dialog_import_export_options"/>
diff --git a/src/com/android/contacts/ContactsApplication.java b/src/com/android/contacts/ContactsApplication.java
index aa1397a..8cbdfbf 100644
--- a/src/com/android/contacts/ContactsApplication.java
+++ b/src/com/android/contacts/ContactsApplication.java
@@ -17,14 +17,36 @@
package com.android.contacts;
import com.android.contacts.model.AccountTypes;
+import com.android.contacts.test.InjectedServices;
import android.app.Application;
+import android.content.ContentResolver;
import android.content.Context;
import android.os.StrictMode;
import android.preference.PreferenceManager;
public final class ContactsApplication extends Application {
+ private static InjectedServices sInjectedServices;
+
+ /**
+ * Overrides the system services with mocks for testing.
+ */
+ public static void injectContentResolver(InjectedServices services) {
+ sInjectedServices = services;
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ if (sInjectedServices != null) {
+ ContentResolver resolver = sInjectedServices.getContentResolver();
+ if (resolver != null) {
+ return resolver;
+ }
+ }
+ return super.getContentResolver();
+ }
+
@Override
public void onCreate() {
super.onCreate();
diff --git a/src/com/android/contacts/interactions/ContactDeletionInteraction.java b/src/com/android/contacts/interactions/ContactDeletionInteraction.java
index d709bc5..654c8cf 100644
--- a/src/com/android/contacts/interactions/ContactDeletionInteraction.java
+++ b/src/com/android/contacts/interactions/ContactDeletionInteraction.java
@@ -51,8 +51,6 @@
private static final String KEY_ACTIVE = "active";
public static final String ARG_CONTACT_URI = "contactUri";
- private static final int LOADER_ID = 0;
-
private static final String[] ENTITY_PROJECTION = new String[] {
Entity.RAW_CONTACT_ID, //0
Entity.ACCOUNT_TYPE, //1
@@ -71,7 +69,10 @@
private AlertDialog mDialog;
- public static void start(Activity activity, Uri contactUri) {
+ // Visible for testing
+ int mMessageId;
+
+ public static ContactDeletionInteraction start(Activity activity, Uri contactUri) {
FragmentManager fragmentManager = activity.getFragmentManager();
ContactDeletionInteraction fragment =
(ContactDeletionInteraction) fragmentManager.findFragmentByTag(FRAGMENT_TAG);
@@ -82,17 +83,13 @@
} else {
fragment.setContactUri(contactUri);
}
+ return fragment;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
- setContext(activity);
- }
-
- /* Visible for testing */
- void setContext(Context context) {
- mContext = context;
+ mContext = activity;
}
public void setContactUri(Uri contactUri) {
@@ -101,7 +98,7 @@
if (isStarted()) {
Bundle args = new Bundle();
args.putParcelable(ARG_CONTACT_URI, mContactUri);
- getLoaderManager().restartLoader(LOADER_ID, args, this);
+ getLoaderManager().restartLoader(R.id.dialog_delete_contact_loader_id, args, this);
}
}
@@ -115,7 +112,7 @@
if (mActive) {
Bundle args = new Bundle();
args.putParcelable(ARG_CONTACT_URI, mContactUri);
- getLoaderManager().initLoader(LOADER_ID, args, this);
+ getLoaderManager().initLoader(R.id.dialog_delete_contact_loader_id, args, this);
}
super.onStart();
}
@@ -146,7 +143,7 @@
HashSet<Long> readOnlyRawContacts = Sets.newHashSet();
HashSet<Long> writableRawContacts = Sets.newHashSet();
- AccountTypes accountTypes = getAccountTypes();
+ AccountTypes accountTypes = AccountTypes.getInstance(getActivity());
cursor.moveToPosition(-1);
while (cursor.moveToNext()) {
final long rawContactId = cursor.getLong(COLUMN_INDEX_RAW_CONTACT_ID);
@@ -162,28 +159,26 @@
}
}
- int messageId;
int readOnlyCount = readOnlyRawContacts.size();
int writableCount = writableRawContacts.size();
if (readOnlyCount > 0 && writableCount > 0) {
- messageId = R.string.readOnlyContactDeleteConfirmation;
+ mMessageId = R.string.readOnlyContactDeleteConfirmation;
} else if (readOnlyCount > 0 && writableCount == 0) {
- messageId = R.string.readOnlyContactWarning;
+ mMessageId = R.string.readOnlyContactWarning;
} else if (readOnlyCount == 0 && writableCount > 1) {
- messageId = R.string.multipleContactDeleteConfirmation;
+ mMessageId = R.string.multipleContactDeleteConfirmation;
} else {
- messageId = R.string.deleteConfirmation;
+ mMessageId = R.string.deleteConfirmation;
}
final Uri contactUri = Contacts.getLookupUri(contactId, lookupKey);
- showDialog(messageId, contactUri);
+ showDialog(mMessageId, contactUri);
}
public void onLoaderReset(Loader<Cursor> loader) {
}
- /* Visible for testing */
- void showDialog(int messageId, final Uri contactUri) {
+ private void showDialog(int messageId, final Uri contactUri) {
mDialog = new AlertDialog.Builder(getActivity())
.setTitle(R.string.deleteConfirmation_title)
.setIcon(android.R.drawable.ic_dialog_alert)
@@ -207,7 +202,7 @@
public void onDismiss(DialogInterface dialog) {
mActive = false;
mDialog = null;
- getLoaderManager().destroyLoader(LOADER_ID);
+ getLoaderManager().destroyLoader(R.id.dialog_delete_contact_loader_id);
}
@Override
@@ -227,9 +222,4 @@
protected void doDeleteContact(Uri contactUri) {
mContext.startService(ContactSaveService.createDeleteContactIntent(mContext, contactUri));
}
-
- /* Visible for testing */
- AccountTypes getAccountTypes() {
- return AccountTypes.getInstance(getActivity());
- }
}
diff --git a/src/com/android/contacts/model/AccountTypes.java b/src/com/android/contacts/model/AccountTypes.java
index ef5265a..d9b692d 100644
--- a/src/com/android/contacts/model/AccountTypes.java
+++ b/src/com/android/contacts/model/AccountTypes.java
@@ -103,6 +103,10 @@
return sInstance;
}
+ public static void injectAccountTypes(AccountTypes injectedAccountTypes) {
+ sInstance = injectedAccountTypes;
+ }
+
/**
* Internal constructor that only performs initial parsing.
*/
diff --git a/src/com/android/contacts/test/FragmentTestActivity.java b/src/com/android/contacts/test/FragmentTestActivity.java
new file mode 100644
index 0000000..f95b284
--- /dev/null
+++ b/src/com/android/contacts/test/FragmentTestActivity.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 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;
+
+import com.android.contacts.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * An activity that is used for testing fragments. A unit test starts this
+ * activity, adds a fragment and then tests the fragment.
+ */
+public class FragmentTestActivity extends Activity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.empty);
+ }
+}
diff --git a/src/com/android/contacts/test/InjectedServices.java b/src/com/android/contacts/test/InjectedServices.java
new file mode 100644
index 0000000..f059f3a
--- /dev/null
+++ b/src/com/android/contacts/test/InjectedServices.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 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;
+
+import com.google.android.collect.Maps;
+
+import android.content.ContentResolver;
+
+import java.util.HashMap;
+
+/**
+ * A mechanism for providing alternative (mock) services to the application
+ * while running tests. Activities, Services and the Application should check
+ * with this class to see if a particular service has been overridden.
+ */
+public class InjectedServices {
+
+ private ContentResolver mContentResolver;
+ private HashMap<String, Object> mSystemServices;
+
+ public void setContentResolver(ContentResolver mContentResolver) {
+ this.mContentResolver = mContentResolver;
+ }
+
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ public void setSystemService(String name, Object service) {
+ if (mSystemServices == null) {
+ mSystemServices = Maps.newHashMap();
+ }
+
+ mSystemServices.put(name, service);
+ }
+
+ public Object getSystemService(String name) {
+ if (mSystemServices != null) {
+ return mSystemServices.get(name);
+ }
+ return null;
+ }
+}
diff --git a/tests/src/com/android/contacts/activities/ContactBrowserActivityTest.java b/tests/src/com/android/contacts/activities/ContactBrowserActivityTest.java
index a642b56..d06f910 100644
--- a/tests/src/com/android/contacts/activities/ContactBrowserActivityTest.java
+++ b/tests/src/com/android/contacts/activities/ContactBrowserActivityTest.java
@@ -66,7 +66,7 @@
getInstrumentation().callActivityOnResume(activity);
getInstrumentation().callActivityOnStart(activity);
- mContext.waitForLoaders(activity, R.id.contact_list_filter_loader);
+ mContext.waitForLoaders(activity.getLoaderManager(), R.id.contact_list_filter_loader);
getInstrumentation().waitForIdleSync();
diff --git a/tests/src/com/android/contacts/interactions/ContactDeletionInteractionTest.java b/tests/src/com/android/contacts/interactions/ContactDeletionInteractionTest.java
index da79311..d507741 100644
--- a/tests/src/com/android/contacts/interactions/ContactDeletionInteractionTest.java
+++ b/tests/src/com/android/contacts/interactions/ContactDeletionInteractionTest.java
@@ -16,22 +16,22 @@
package com.android.contacts.interactions;
+import com.android.contacts.ContactsApplication;
import com.android.contacts.R;
import com.android.contacts.model.AccountTypes;
+import com.android.contacts.test.FragmentTestActivity;
+import com.android.contacts.test.InjectedServices;
import com.android.contacts.tests.mocks.ContactsMockContext;
import com.android.contacts.tests.mocks.MockAccountTypes;
import com.android.contacts.tests.mocks.MockContentProvider;
import com.android.contacts.tests.mocks.MockContentProvider.Query;
-import com.android.contacts.widget.TestLoaderManager;
-import android.app.LoaderManager;
import android.content.ContentUris;
-import android.content.pm.ProviderInfo;
import android.net.Uri;
-import android.provider.ContactsContract;
+import android.os.AsyncTask;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Contacts.Entity;
-import android.test.InstrumentationTestCase;
+import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.Smoke;
/**
@@ -45,48 +45,42 @@
* -w com.android.contacts.tests/android.test.InstrumentationTestRunner
*/
@Smoke
-public class ContactDeletionInteractionTest extends InstrumentationTestCase {
+public class ContactDeletionInteractionTest
+ extends ActivityInstrumentationTestCase2<FragmentTestActivity> {
- private final class TestContactDeletionDialogFragment
- extends ContactDeletionInteraction {
- public Uri contactUri;
- public int messageId;
-
- @Override
- public LoaderManager getLoaderManager() {
- return mLoaderManager;
- }
-
- @Override
- boolean isStarted() {
- return true;
- }
-
- @Override
- void showDialog(int messageId, Uri contactUri) {
- this.messageId = messageId;
- this.contactUri = contactUri;
- }
-
- @Override
- AccountTypes getAccountTypes() {
- return new MockAccountTypes();
- }
+ static {
+ // AsyncTask class needs to be initialized on the main thread.
+ AsyncTask.init();
}
+ private static final Uri CONTACT_URI = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
+ private static final Uri ENTITY_URI = Uri.withAppendedPath(
+ CONTACT_URI, Entity.CONTENT_DIRECTORY);
+
private ContactsMockContext mContext;
private MockContentProvider mContactsProvider;
- private TestLoaderManager mLoaderManager;
+ private ContactDeletionInteraction mFragment;
+
+ public ContactDeletionInteractionTest() {
+ super(FragmentTestActivity.class);
+ }
@Override
protected void setUp() throws Exception {
super.setUp();
mContext = new ContactsMockContext(getInstrumentation().getTargetContext());
+ InjectedServices services = new InjectedServices();
+ services.setContentResolver(mContext.getContentResolver());
+ ContactsApplication.injectContentResolver(services);
+ AccountTypes.injectAccountTypes(new MockAccountTypes());
mContactsProvider = mContext.getContactsProvider();
- ProviderInfo info = new ProviderInfo();
- info.authority = ContactsContract.AUTHORITY;
- mContactsProvider.attachInfo(mContext, info);
- mLoaderManager = new TestLoaderManager();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ ContactsApplication.injectContentResolver(null);
+ AccountTypes.injectAccountTypes(null);
+ super.tearDown();
}
public void testSingleWritableRawContact() {
@@ -114,20 +108,28 @@
}
private Query expectQuery() {
- Uri uri = Uri.withAppendedPath(
- ContentUris.withAppendedId(Contacts.CONTENT_URI, 13), Entity.CONTENT_DIRECTORY);
- return mContactsProvider.expectQuery(uri).withProjection(
+ return mContactsProvider.expectQuery(ENTITY_URI).withProjection(
Entity.RAW_CONTACT_ID, Entity.ACCOUNT_TYPE, Entity.CONTACT_ID, Entity.LOOKUP_KEY);
}
private void assertWithMessageId(int messageId) {
- TestContactDeletionDialogFragment interaction = new TestContactDeletionDialogFragment();
- interaction.setContext(mContext);
- interaction.setContactUri(ContentUris.withAppendedId(Contacts.CONTENT_URI, 13));
- mLoaderManager.executeLoaders();
- assertEquals("content://com.android.contacts/contacts/lookup/foo/13",
- interaction.contactUri.toString());
- assertEquals(messageId, interaction.messageId);
- mContactsProvider.verify();
+ final FragmentTestActivity activity = getActivity();
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFragment = ContactDeletionInteraction.start(activity, CONTACT_URI);
+ }
+ });
+
+ getInstrumentation().waitForIdleSync();
+
+ mContext.waitForLoaders(mFragment.getLoaderManager(), R.id.dialog_delete_contact_loader_id);
+
+ getInstrumentation().waitForIdleSync();
+
+ mContext.verify();
+
+ assertEquals(messageId, mFragment.mMessageId);
}
}
diff --git a/tests/src/com/android/contacts/tests/mocks/ContactsMockContext.java b/tests/src/com/android/contacts/tests/mocks/ContactsMockContext.java
index a7aaba4..55ba053 100644
--- a/tests/src/com/android/contacts/tests/mocks/ContactsMockContext.java
+++ b/tests/src/com/android/contacts/tests/mocks/ContactsMockContext.java
@@ -18,7 +18,7 @@
//import com.android.providers.contacts.ContactsMockPackageManager;
-import android.app.Activity;
+import android.app.LoaderManager;
import android.content.AsyncTaskLoader;
import android.content.ContentResolver;
import android.content.Context;
@@ -28,6 +28,9 @@
import android.provider.ContactsContract;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
+import android.util.Log;
+
+import junit.framework.Assert;
/**
* A mock context for contacts unit tests. Forwards everything to
@@ -36,6 +39,8 @@
*/
public class ContactsMockContext extends ContextWrapper {
+ private static final String TAG = "ContactsMockContext";
+
private ContactsMockPackageManager mPackageManager;
private MockContentResolver mContentResolver;
private MockContentProvider mContactsProvider;
@@ -84,18 +89,28 @@
/**
* Waits for the specified loaders to complete loading.
*/
- public void waitForLoaders(final Activity activity, int... loaderIds) {
+ public void waitForLoaders(LoaderManager loaderManager, int... loaderIds) {
// We want to wait for each loader using a separate thread, so that we can
// simulate race conditions.
Thread[] waitThreads = new Thread[loaderIds.length];
for (int i = 0; i < loaderIds.length; i++) {
final int loaderId = loaderIds[i];
+ final AsyncTaskLoader<?> loader =
+ (AsyncTaskLoader<?>) loaderManager.getLoader(loaderId);
+ if (loader == null) {
+ Assert.fail("Loader does not exist: " + loaderId);
+ return;
+ }
+
waitThreads[i] = new Thread("LoaderWaitingThread" + i) {
@Override
public void run() {
- AsyncTaskLoader<?> loader =
- (AsyncTaskLoader<?>) activity.getLoaderManager().getLoader(loaderId);
- loader.waitForLoader();
+ try {
+ loader.waitForLoader();
+ } catch (Throwable e) {
+ Log.e(TAG, "Exception while waiting for loader: " + loaderId, e);
+ Assert.fail("Exception while waiting for loader: " + loaderId);
+ }
}
};
waitThreads[i].start();