Add tests for ContactsHelperAsync
Change-Id: I6ce3c7523fab97d4dc4d4a23ded84af8ec882e5c
diff --git a/src/com/android/server/telecom/ContactsAsyncHelper.java b/src/com/android/server/telecom/ContactsAsyncHelper.java
index 44fa654..974fc51 100644
--- a/src/com/android/server/telecom/ContactsAsyncHelper.java
+++ b/src/com/android/server/telecom/ContactsAsyncHelper.java
@@ -29,6 +29,7 @@
// TODO: Needed for move to system service: import com.android.internal.R;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@@ -57,15 +58,22 @@
Object cookie);
}
+ /**
+ * Interface to enable stubbing of the call to openInputStream
+ */
+ public interface ContentResolverAdapter {
+ InputStream openInputStream(Context context, Uri uri) throws FileNotFoundException;
+ }
+
// constants
private static final int EVENT_LOAD_IMAGE = 1;
/** Handler run on a worker thread to load photo asynchronously. */
private Handler mThreadHandler;
- private final TelecomSystem.SyncRoot mLock;
+ private final ContentResolverAdapter mContentResolverAdapter;
- public ContactsAsyncHelper(TelecomSystem.SyncRoot lock) {
- mLock = lock;
+ public ContactsAsyncHelper(ContentResolverAdapter contentResolverAdapter) {
+ mContentResolverAdapter = contentResolverAdapter;
}
private static final class WorkerArgs {
@@ -95,8 +103,8 @@
InputStream inputStream = null;
try {
try {
- inputStream = args.context.getContentResolver()
- .openInputStream(args.displayPhotoUri);
+ inputStream = mContentResolverAdapter.openInputStream(
+ args.context, args.displayPhotoUri);
} catch (Exception e) {
Log.e(this, e, "Error opening photo input stream");
}
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index d28a9d7..6b9a7d7 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -25,8 +25,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.Uri;
import android.os.UserHandle;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
/**
* Top-level Application class for Telecom.
*/
@@ -127,7 +131,14 @@
mMissedCallNotifier = missedCallNotifier;
mPhoneAccountRegistrar = new PhoneAccountRegistrar(mContext);
- mContactsAsyncHelper = new ContactsAsyncHelper(mLock);
+ mContactsAsyncHelper = new ContactsAsyncHelper(
+ new ContactsAsyncHelper.ContentResolverAdapter() {
+ @Override
+ public InputStream openInputStream(Context context, Uri uri)
+ throws FileNotFoundException {
+ return context.getContentResolver().openInputStream(uri);
+ }
+ });
BluetoothManager bluetoothManager = new BluetoothManager(mContext);
WiredHeadsetManager wiredHeadsetManager = new WiredHeadsetManager(mContext);
diff --git a/tests/res/drawable-xhdpi/contacts_sample_photo.png b/tests/res/drawable-xhdpi/contacts_sample_photo.png
new file mode 100644
index 0000000..6c0ba64
--- /dev/null
+++ b/tests/res/drawable-xhdpi/contacts_sample_photo.png
Binary files differ
diff --git a/tests/res/drawable-xhdpi/contacts_sample_photo_small.png b/tests/res/drawable-xhdpi/contacts_sample_photo_small.png
new file mode 100644
index 0000000..f53602e
--- /dev/null
+++ b/tests/res/drawable-xhdpi/contacts_sample_photo_small.png
Binary files differ
diff --git a/tests/res/values/dimens.xml b/tests/res/values/dimens.xml
new file mode 100644
index 0000000..031f5b4
--- /dev/null
+++ b/tests/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 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
+ -->
+
+<resources>
+ <dimen name="notification_icon_size">64dp</dimen>
+</resources>
diff --git a/tests/src/com/android/server/telecom/tests/ContactsAsyncHelperTest.java b/tests/src/com/android/server/telecom/tests/ContactsAsyncHelperTest.java
new file mode 100644
index 0000000..07ae122
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/ContactsAsyncHelperTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2015 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.server.telecom.tests;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+
+import com.android.server.telecom.ContactsAsyncHelper;
+
+import org.mockito.ArgumentCaptor;
+
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+public class ContactsAsyncHelperTest extends TelecomTestCase {
+ private static final Uri SAMPLE_CONTACT_PHOTO_URI = Uri.parse(
+ "android.resource://com.android.server.telecom.tests/"
+ + R.drawable.contacts_sample_photo);
+
+ private static final Uri SAMPLE_CONTACT_PHOTO_URI_SMALL = Uri.parse(
+ "android.resource://com.android.server.telecom.tests/"
+ + R.drawable.contacts_sample_photo_small);
+
+ private static final int TOKEN = 4847524;
+ private static final int TEST_TIMEOUT = 500;
+ private static final Object COOKIE = new Object();
+
+ public static class ImageLoadListenerImpl
+ implements ContactsAsyncHelper.OnImageLoadCompleteListener {
+ @Override
+ public void onImageLoadComplete(int token, Drawable photo,
+ Bitmap photoIcon, Object cookie) {
+ }
+ }
+
+ private ImageLoadListenerImpl mListener = spy(new ImageLoadListenerImpl());
+
+ private ContactsAsyncHelper.ContentResolverAdapter mWorkingContentResolverAdapter =
+ new ContactsAsyncHelper.ContentResolverAdapter() {
+ @Override
+ public InputStream openInputStream(Context context, Uri uri)
+ throws FileNotFoundException {
+ return context.getContentResolver().openInputStream(uri);
+ }
+ };
+
+ private ContactsAsyncHelper.ContentResolverAdapter mNullContentResolverAdapter =
+ new ContactsAsyncHelper.ContentResolverAdapter() {
+ @Override
+ public InputStream openInputStream(Context context, Uri uri)
+ throws FileNotFoundException {
+ return null;
+ }
+ };
+
+ @Override
+ public void setUp() throws Exception {
+ mContext = getTestContext();
+ super.setUp();
+ }
+
+ public void testEmptyUri() {
+ ContactsAsyncHelper cah = new ContactsAsyncHelper(mNullContentResolverAdapter);
+ try {
+ cah.startObtainPhotoAsync(TOKEN, mContext, null, mListener, COOKIE);
+ } catch (IllegalStateException e) {
+ // expected to fail
+ }
+ verify(mListener, timeout(TEST_TIMEOUT).never()).onImageLoadComplete(anyInt(),
+ any(Drawable.class), any(Bitmap.class), anyObject());
+ }
+
+ public void testNullReturnFromOpenInputStream() {
+ ContactsAsyncHelper cah = new ContactsAsyncHelper(mNullContentResolverAdapter);
+ cah.startObtainPhotoAsync(TOKEN, mContext, SAMPLE_CONTACT_PHOTO_URI, mListener, COOKIE);
+
+ verify(mListener, timeout(TEST_TIMEOUT)).onImageLoadComplete(eq(TOKEN),
+ isNull(Drawable.class), isNull(Bitmap.class), eq(COOKIE));
+ }
+
+ public void testImageScaling() {
+ ContactsAsyncHelper cah = new ContactsAsyncHelper(mWorkingContentResolverAdapter);
+ cah.startObtainPhotoAsync(TOKEN, mContext, SAMPLE_CONTACT_PHOTO_URI, mListener, COOKIE);
+
+ ArgumentCaptor<Drawable> photoCaptor = ArgumentCaptor.forClass(Drawable.class);
+ ArgumentCaptor<Bitmap> iconCaptor = ArgumentCaptor.forClass(Bitmap.class);
+
+ verify(mListener, timeout(TEST_TIMEOUT)).onImageLoadComplete(eq(TOKEN),
+ photoCaptor.capture(), iconCaptor.capture(), eq(COOKIE));
+
+ Bitmap capturedPhoto = ((BitmapDrawable) photoCaptor.getValue()).getBitmap();
+ assertTrue(getExpectedPhoto(SAMPLE_CONTACT_PHOTO_URI).sameAs(capturedPhoto));
+ int iconSize = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.notification_icon_size);
+ assertTrue(iconSize >= iconCaptor.getValue().getHeight());
+ assertTrue(iconSize >= iconCaptor.getValue().getWidth());
+ }
+
+ public void testNoScaling() {
+ ContactsAsyncHelper cah = new ContactsAsyncHelper(mWorkingContentResolverAdapter);
+ cah.startObtainPhotoAsync(TOKEN, mContext, SAMPLE_CONTACT_PHOTO_URI_SMALL,
+ mListener, COOKIE);
+
+ ArgumentCaptor<Drawable> photoCaptor = ArgumentCaptor.forClass(Drawable.class);
+ ArgumentCaptor<Bitmap> iconCaptor = ArgumentCaptor.forClass(Bitmap.class);
+
+ verify(mListener, timeout(TEST_TIMEOUT)).onImageLoadComplete(eq(TOKEN),
+ photoCaptor.capture(), iconCaptor.capture(), eq(COOKIE));
+
+ Bitmap capturedPhoto = ((BitmapDrawable) photoCaptor.getValue()).getBitmap();
+ assertTrue(getExpectedPhoto(SAMPLE_CONTACT_PHOTO_URI_SMALL).sameAs(capturedPhoto));
+ assertTrue(capturedPhoto.sameAs(iconCaptor.getValue()));
+ }
+
+ private Bitmap getExpectedPhoto(Uri uri) {
+ InputStream is;
+ try {
+ is = mContext.getContentResolver().openInputStream(uri);
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+
+ Drawable d = Drawable.createFromStream(is, uri.toString());
+ return ((BitmapDrawable) d).getBitmap();
+ }
+}