summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDebashish Chatterjee <debashishc@google.com>2011-06-16 15:35:03 +0100
committerDebashish Chatterjee <debashishc@google.com>2011-06-16 17:51:20 +0100
commit1975b56a3368b4b7684429ffa79e7b9dbc35b475 (patch)
tree018f5d6d84bb2c3cf55ad5807c9e20aa159b4472
parent07b66b78439296cd27e1ca7c156bc9eeeae85131 (diff)
downloadpackages_providers_ContactsProvider-1975b56a3368b4b7684429ffa79e7b9dbc35b475.zip
packages_providers_ContactsProvider-1975b56a3368b4b7684429ffa79e7b9dbc35b475.tar.gz
packages_providers_ContactsProvider-1975b56a3368b4b7684429ffa79e7b9dbc35b475.tar.bz2
Unit tests for voicemail provider.
These tests cover basic functionality of the provider including permission checks and media content input/output. The key functionality that is yet to be tested is provider change broadcast intents. This requires us to use a mocking framework, and we are yet to finalize on which one we will use. Change-Id: I2304309c4fc109cc1e0b969ede33d8268a4d4194
-rw-r--r--src/com/android/providers/contacts/VoicemailContentProvider.java71
-rw-r--r--tests/src/com/android/providers/contacts/VoicemailProviderTest.java336
2 files changed, 375 insertions, 32 deletions
diff --git a/src/com/android/providers/contacts/VoicemailContentProvider.java b/src/com/android/providers/contacts/VoicemailContentProvider.java
index d559758..c24fc03 100644
--- a/src/com/android/providers/contacts/VoicemailContentProvider.java
+++ b/src/com/android/providers/contacts/VoicemailContentProvider.java
@@ -86,11 +86,15 @@ public class VoicemailContentProvider extends ContentProvider {
Context context = context();
mContentResolver = context.getContentResolver();
- mDbHelper = ContactsDatabaseHelper.getInstance(context);
+ mDbHelper = getDatabaseHelper(context);
return true;
}
+ /*package for testing*/ ContactsDatabaseHelper getDatabaseHelper(Context context) {
+ return ContactsDatabaseHelper.getInstance(context);
+ }
+
/*package for testing*/ Context context() {
return getContext();
}
@@ -135,7 +139,7 @@ public class VoicemailContentProvider extends ContentProvider {
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
- checkHasOwnPermission();
+ checkCallerHasOwnPermission();
UriData uriData = createUriData(uri);
checkPackagePermission(uriData);
return queryInternal(uriData, projection,
@@ -178,7 +182,7 @@ public class VoicemailContentProvider extends ContentProvider {
@Override
public int bulkInsert(Uri uri, ContentValues[] valuesArray) {
- checkHasOwnPermission();
+ checkCallerHasOwnPermission();
// TODO: There is scope to optimize this method further. At the least we can avoid doing the
// extra work related to the calling provider and checking permissions.
UriData uriData = createUriData(uri);
@@ -196,28 +200,29 @@ public class VoicemailContentProvider extends ContentProvider {
@Override
public Uri insert(Uri uri, ContentValues values) {
- checkHasOwnPermission();
+ checkCallerHasOwnPermission();
return insertInternal(createUriData(uri), values, true);
}
private Uri insertInternal(UriData uriData, ContentValues values,
boolean sendProviderChangedNotification) {
+ ContentValues copiedValues = new ContentValues(values);
checkInsertSupported(uriData);
- checkAndAddSourcePackageIntoValues(uriData, values);
+ checkAndAddSourcePackageIntoValues(uriData, copiedValues);
// "_data" column is used by base ContentProvider's openFileHelper() to determine filename
// when Input/Output stream is requested to be opened.
- values.put(Voicemails._DATA, generateDataFile());
+ copiedValues.put(Voicemails._DATA, generateDataFile());
// call type is always voicemail.
- values.put(Calls.TYPE, Calls.VOICEMAIL_TYPE);
+ copiedValues.put(Calls.TYPE, Calls.VOICEMAIL_TYPE);
SQLiteDatabase db = mDbHelper.getWritableDatabase();
- long rowId = db.insert(VOICEMAILS_TABLE_NAME, null, values);
+ long rowId = db.insert(VOICEMAILS_TABLE_NAME, null, copiedValues);
if (rowId > 0) {
Uri newUri = ContentUris.withAppendedId(
Uri.withAppendedPath(VoicemailContract.CONTENT_URI_SOURCE,
- values.getAsString(Voicemails.SOURCE_PACKAGE)), rowId);
+ copiedValues.getAsString(Voicemails.SOURCE_PACKAGE)), rowId);
if (sendProviderChangedNotification) {
notifyChange(newUri, VoicemailContract.ACTION_NEW_VOICEMAIL,
@@ -248,14 +253,14 @@ public class VoicemailContentProvider extends ContentProvider {
// If you put a provider in the URI and in the values, they must match.
if (uriData.hasSourcePackage() && values.containsKey(Voicemails.SOURCE_PACKAGE)) {
if (!uriData.getSourcePackage().equals(values.get(Voicemails.SOURCE_PACKAGE))) {
- throw new IllegalArgumentException(
+ throw new SecurityException(
"Provider in URI was " + uriData.getSourcePackage() +
" but doesn't match provider in ContentValues which was "
+ values.get(Voicemails.SOURCE_PACKAGE));
}
}
// You must have access to the provider given in values.
- if (!hasFullPermission(getCallingPackage())) {
+ if (!callerHasFullPermission()) {
checkPackagesMatch(getCallingPackage(), values.getAsString(Voicemails.SOURCE_PACKAGE),
uriData.getUri());
}
@@ -286,7 +291,7 @@ public class VoicemailContentProvider extends ContentProvider {
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- checkHasOwnPermission();
+ checkCallerHasOwnPermission();
UriData uriData = createUriData(uri);
checkUpdateSupported(uriData);
checkPackagePermission(uriData);
@@ -311,7 +316,7 @@ public class VoicemailContentProvider extends ContentProvider {
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
- checkHasOwnPermission();
+ checkCallerHasOwnPermission();
UriData uriData = createUriData(uri);
checkPackagePermission(uriData);
final SQLiteDatabase db = mDbHelper.getWritableDatabase();
@@ -347,7 +352,7 @@ public class VoicemailContentProvider extends ContentProvider {
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
- checkHasOwnPermission();
+ checkCallerHasOwnPermission();
UriData uriData = createUriData(uri);
checkPackagePermission(uriData);
@@ -451,7 +456,7 @@ public class VoicemailContentProvider extends ContentProvider {
* @throws SecurityException if the check fails.
*/
private void checkPackagePermission(UriData uriData) {
- if (!hasFullPermission(getCallingPackage())) {
+ if (!callerHasFullPermission()) {
if (!uriData.hasSourcePackage()) {
// You cannot have a match if this is not a provider uri.
throw new SecurityException(String.format(
@@ -513,11 +518,11 @@ public class VoicemailContentProvider extends ContentProvider {
// which one we return.
String bestSoFar = callerPackages[0];
for (String callerPackage : callerPackages) {
- if (hasFullPermission(callerPackage)) {
+ if (hasPermission(callerPackage, Manifest.permission.READ_WRITE_ALL_VOICEMAIL)) {
// Full always wins, we can return early.
return callerPackage;
}
- if (hasOwnPermission(callerPackage)) {
+ if (hasPermission(callerPackage, Manifest.permission.READ_WRITE_OWN_VOICEMAIL)) {
bestSoFar = callerPackage;
}
}
@@ -529,29 +534,31 @@ public class VoicemailContentProvider extends ContentProvider {
*
* @throws SecurityException if the caller does not have the voicemail source permission.
*/
- private void checkHasOwnPermission() {
- if (!hasOwnPermission(getCallingPackage())) {
+ private void checkCallerHasOwnPermission() {
+ if (!callerHasOwnPermission()) {
throw new SecurityException("The caller must have permission: " +
Manifest.permission.READ_WRITE_OWN_VOICEMAIL);
}
}
- /** Tells us if the given package has the source permission. */
- private boolean hasOwnPermission(String packageName) {
- return hasPermission(packageName, Manifest.permission.READ_WRITE_OWN_VOICEMAIL);
+ /** Determines if the calling process has own permission. */
+ private boolean callerHasOwnPermission() {
+ return callerHasPermission(Manifest.permission.READ_WRITE_OWN_VOICEMAIL);
}
- /**
- * Tells us if the given package has the full permission and the source
- * permission.
- */
- private boolean hasFullPermission(String packageName) {
- return hasOwnPermission(packageName) &&
- hasPermission(packageName, Manifest.permission.READ_WRITE_ALL_VOICEMAIL);
+ /** Determines if the calling process has full permission. */
+ private boolean callerHasFullPermission() {
+ return callerHasOwnPermission() &&
+ callerHasPermission(Manifest.permission.READ_WRITE_ALL_VOICEMAIL);
+ }
+
+ /** Determines if the calling process has the given permission. */
+ boolean callerHasPermission(String permission) {
+ return context().checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
}
- /** Tells us if the given package has the given permission. */
- /* package for test */boolean hasPermission(String packageName, String permission) {
+ /** Determines if the given package has the given permission. */
+ boolean hasPermission(String packageName, String permission) {
return context().getPackageManager().checkPermission(permission, packageName)
== PackageManager.PERMISSION_GRANTED;
}
@@ -561,7 +568,7 @@ public class VoicemailContentProvider extends ContentProvider {
* access to all data.
*/
private String getPackageRestrictionClause() {
- if (hasFullPermission(getCallingPackage())) {
+ if (callerHasFullPermission()) {
return null;
}
return getEqualityClause(Voicemails.SOURCE_PACKAGE, getCallingPackage());
diff --git a/tests/src/com/android/providers/contacts/VoicemailProviderTest.java b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java
new file mode 100644
index 0000000..8e06bf5
--- /dev/null
+++ b/tests/src/com/android/providers/contacts/VoicemailProviderTest.java
@@ -0,0 +1,336 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package com.android.providers.contacts;
+
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.VoicemailContract;
+import android.provider.VoicemailContract.Voicemails;
+import android.test.MoreAsserts;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Unit tests for {@link VoicemailContentProvider}.
+ *
+ * Run the test like this:
+ * <code>
+ * runtest -c com.android.providers.contacts.VoicemailProviderTest contactsprov
+ * </code>
+ */
+public class VoicemailProviderTest extends BaseContactsProvider2Test {
+ private static final String ALL_PERMISSION =
+ "com.android.voicemail.permission.READ_WRITE_ALL_VOICEMAIL";
+ private static final String OWN_PERMISSION =
+ "com.android.voicemail.permission.READ_WRITE_OWN_VOICEMAIL";
+ @Override
+ protected Class<? extends ContentProvider> getProviderClass() {
+ return TestVoicemailProvider.class;
+ }
+
+ @Override
+ protected String getAuthority() {
+ return VoicemailContract.AUTHORITY;
+ }
+
+ private boolean mUseSourceUri = false;
+ private File mTestDirectory;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ addProvider(TestVoicemailProvider.class, VoicemailContract.AUTHORITY);
+ setUpForOwnPermission();
+ TestVoicemailProvider.setVvmProviderCallDelegate(createMockProviderCalls());
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ removeTestDirectory();
+ super.tearDown();
+ }
+
+ private void setUpForOwnPermission() {
+ // Give away full permission, in case it was granted previously.
+ mActor.removePermissions(ALL_PERMISSION);
+ mActor.addPermissions(OWN_PERMISSION);
+ mUseSourceUri = false;
+ }
+
+ private void setUpForFullPermission() {
+ mActor.addPermissions(OWN_PERMISSION);
+ mActor.addPermissions(ALL_PERMISSION);
+ mUseSourceUri = true;
+ }
+
+ private void setUpForNoPermission() {
+ mActor.removePermissions(OWN_PERMISSION);
+ mActor.removePermissions(ALL_PERMISSION);
+ mUseSourceUri = true;
+ }
+
+ private Uri contentUri() {
+ if (mUseSourceUri) {
+ return VoicemailContract.CONTENT_URI;
+ } else {
+ return Uri.withAppendedPath(VoicemailContract.CONTENT_URI_SOURCE,
+ mActor.packageName);
+ }
+ }
+
+ public void testInsert() throws Exception {
+ ContentValues values = getDefaultVoicemailValues();
+ Uri uri = mResolver.insert(contentUri(), values);
+ assertStoredValues(uri, values);
+ assertSelection(uri, values, Voicemails._ID, ContentUris.parseId(uri));
+ assertEquals(1, countFilesInTestDirectory());
+ }
+
+ // Test to ensure that media content can be written and read back.
+ public void testFileContent() throws Exception {
+ Uri uri = insertVoicemail();
+ OutputStream out = mResolver.openOutputStream(uri);
+ byte[] outBuffer = {0x1, 0x2, 0x3, 0x4};
+ out.write(outBuffer);
+ out.flush();
+ out.close();
+ InputStream in = mResolver.openInputStream(uri);
+ byte[] inBuffer = new byte[4];
+ int numBytesRead = in.read(inBuffer);
+ assertEquals(numBytesRead, outBuffer.length);
+ MoreAsserts.assertEquals(outBuffer, inBuffer);
+ // No more data should be left.
+ assertEquals(-1, in.read(inBuffer));
+ in.close();
+ }
+
+ public void testUpdate() {
+ Uri uri = insertVoicemail();
+ ContentValues values = new ContentValues();
+ values.put(Voicemails.NUMBER, "1-800-263-7643");
+ values.put(Voicemails.DATE, 2000);
+ values.put(Voicemails.DURATION, 40);
+ values.put(Voicemails.STATE, 2);
+ values.put(Voicemails.HAS_CONTENT, 1);
+ values.put(Voicemails.SOURCE_DATA, "foo");
+ int count = mResolver.update(uri, values, null, null);
+ assertEquals(1, count);
+ assertStoredValues(uri, values);
+ }
+
+ public void testDelete() {
+ Uri uri = insertVoicemail();
+ int count = mResolver.delete(contentUri(), Voicemails._ID + "="
+ + ContentUris.parseId(uri), null);
+ assertEquals(1, count);
+ assertEquals(0, getCount(uri, null, null));
+ }
+
+ public void testPermissions_InsertAndQuery() {
+ setUpForFullPermission();
+ // Insert two records - one each with own and another package.
+ insertVoicemail();
+ insertVoicemailForSourcePackage("another-package");
+ assertEquals(2, getCount(contentUri(), null, null));
+
+ // Now give away full permission and check that only 1 message is accessible.
+ setUpForOwnPermission();
+ assertEquals(1, getCount(contentUri(), null, null));
+
+ // Once again try to insert message for another package. This time
+ // it should fail.
+ EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
+ @Override
+ public void run() {
+ insertVoicemailForSourcePackage("another-package");
+ }
+ });
+ }
+
+ public void testPermissions_UpdateAndDelete() {
+ setUpForFullPermission();
+ // Insert two records - one each with own and another package.
+ final Uri ownVoicemail = insertVoicemail();
+ final Uri anotherVoicemail = insertVoicemailForSourcePackage("another-package");
+ assertEquals(2, getCount(contentUri(), null, null));
+
+ // Now give away full permission and check that we can update and delete only
+ // the own voicemail.
+ setUpForOwnPermission();
+ mResolver.update(ownVoicemail, getDefaultVoicemailValues(), null, null);
+ mResolver.delete(ownVoicemail, null, null);
+
+ // However, attempting to update or delete another-package's voicemail should fail.
+ EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
+ @Override
+ public void run() {
+ mResolver.update(anotherVoicemail, null, null, null);
+ }
+ });
+ EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
+ @Override
+ public void run() {
+ mResolver.delete(anotherVoicemail, null, null);
+ }
+ });
+ }
+
+ // Test to ensure that all operations fail when no voicemail permission is granted.
+ public void testNoPermissions() {
+ setUpForNoPermission();
+ EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
+ @Override
+ public void run() {
+ mResolver.insert(contentUri(), getDefaultVoicemailValues());
+ }
+ });
+ EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
+ @Override
+ public void run() {
+ mResolver.update(contentUri(), getDefaultVoicemailValues(), null, null);
+ }
+ });
+ EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
+ @Override
+ public void run() {
+ mResolver.query(contentUri(), null, null, null, null);
+ }
+ });
+ EvenMoreAsserts.assertThrows(SecurityException.class, new Runnable() {
+ @Override
+ public void run() {
+ mResolver.delete(contentUri(), null, null);
+ }
+ });
+ }
+
+ /**
+ * Inserts a voicemail record with no source package set. The content provider
+ * will detect source package.
+ */
+ private Uri insertVoicemail() {
+ return mResolver.insert(contentUri(), getDefaultVoicemailValues());
+ }
+
+ /** Inserts a voicemail record for the specified source package. */
+ private Uri insertVoicemailForSourcePackage(String sourcePackage) {
+ ContentValues values = getDefaultVoicemailValues();
+ values.put(Voicemails.SOURCE_PACKAGE, sourcePackage);
+ return mResolver.insert(contentUri(), values);
+ }
+
+ private ContentValues getDefaultVoicemailValues() {
+ ContentValues values = new ContentValues();
+ values.put(Voicemails.NUMBER, "1-800-4664-411");
+ values.put(Voicemails.DATE, 1000);
+ values.put(Voicemails.DURATION, 30);
+ values.put(Voicemails.NEW, 1);
+ values.put(Voicemails.HAS_CONTENT, 0);
+ values.put(Voicemails.SOURCE_DATA, "1234");
+ values.put(Voicemails.STATE, Voicemails.STATE_INBOX);
+ return values;
+ }
+
+ /** The calls that we need to mock out for our VvmProvider, used by TestVoicemailProvider. */
+ public interface VvmProviderCalls {
+ public void sendOrderedBroadcast(Intent intent, String receiverPermission);
+ public File getDir(String name, int mode);
+ }
+
+ public static class TestVoicemailProvider extends VoicemailContentProvider {
+ private static VvmProviderCalls mDelgate;
+
+ public static synchronized void setVvmProviderCallDelegate(VvmProviderCalls delegate) {
+ mDelgate = delegate;
+ }
+
+ @Override
+ protected ContactsDatabaseHelper getDatabaseHelper(Context context) {
+ return new ContactsDatabaseHelper(context);
+ }
+
+ @Override
+ protected Context context() {
+ return new ContextWrapper(getContext()) {
+ @Override
+ public File getDir(String name, int mode) {
+ return mDelgate.getDir(name, mode);
+ }
+ };
+ }
+
+ @Override
+ protected String getCallingPackage() {
+ return getContext().getPackageName();
+ }
+ }
+
+ /** Lazily construct the test directory when required. */
+ private synchronized File getTestDirectory() {
+ if (mTestDirectory == null) {
+ File baseDirectory = getContext().getCacheDir();
+ mTestDirectory = new File(baseDirectory, Long.toString(System.currentTimeMillis()));
+ assertFalse(mTestDirectory.exists());
+ assertTrue(mTestDirectory.mkdirs());
+ }
+ return mTestDirectory;
+ }
+
+ private void removeTestDirectory() {
+ if (mTestDirectory != null) {
+ recursiveDeleteAll(mTestDirectory);
+ }
+ }
+
+ private static void recursiveDeleteAll(File input) {
+ if (input.isDirectory()) {
+ for (File file : input.listFiles()) {
+ recursiveDeleteAll(file);
+ }
+ }
+ assertTrue("error deleting " + input.getAbsolutePath(), input.delete());
+ }
+
+ private List<File> findAllFiles(File input) {
+ if (input == null) {
+ return Collections.emptyList();
+ }
+ if (!input.isDirectory()) {
+ return Collections.singletonList(input);
+ }
+ List<File> results = new ArrayList<File>();
+ for (File file : input.listFiles()) {
+ results.addAll(findAllFiles(file));
+ }
+ return results;
+ }
+
+ private int countFilesInTestDirectory() {
+ return findAllFiles(mTestDirectory).size();
+ }
+
+ // TODO: Use a mocking framework to mock these calls.
+ private VvmProviderCalls createMockProviderCalls() {
+ return new VvmProviderCalls() {
+ @Override
+ public void sendOrderedBroadcast(Intent intent, String receiverPermission) {
+ // TODO: Auto-generated method stub
+ }
+
+ @Override
+ public File getDir(String name, int mode) {
+ return getTestDirectory();
+ }
+ };
+ }
+}