blob: 009c5e12fd848982240d1a211e18963e12f92f04 [file] [log] [blame]
/*
* 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.exchange.provider;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import java.util.Map.Entry;
/**
* MockProvider is a ContentProvider that can be used to simulate the storage and retrieval of
* records from any ContentProvider, even if that ContentProvider does not exist in the caller's
* package. It is specifically designed to enable testing of sync adapters that create
* ContentProviderOperations (CPOs) that are then executed using ContentResolver.applyBatch()
*
* Why is this useful? Because we can't instantiate CalendarProvider or ContactsProvider from our
* package, as required by MockContentResolver.addProvider()
*
* Usage:
* ContentResolver.applyBatch(MockProvider.AUTHORITY, batch) will cause the CPOs to be executed,
* returning an array of ContentProviderResult; in the case of inserts, the result will include
* a Uri that can be used via query(). Note that the CPOs in the batch can contain references
* to any authority.
*
* query() does not allow non-null selection, selectionArgs, or sortOrder arguments; the
* presence of these will result in an UnsupportedOperationException
*
* insert() acts as expected, returning a Uri that can be directly used in a query
*
* delete() and update() do not allow non-null selection or selectionArgs arguments; the
* presence of these will result in an UnsupportedOperationException
*
* NOTE: When using any operation other than applyBatch, the Uri to be used must be created
* with MockProvider.uri(yourUri). This guarantees that the operation is sent to MockProvider
*
* NOTE: MockProvider only simulates direct storage/retrieval of rows; it does not (and can not)
* simulate other actions (e.g. creation of ancillary data) that the actual provider might
* perform
*
* NOTE: See MockProviderTests for usage examples
**/
// The constructor call below is commented out because it won't compile under SDK
public class MockProvider extends ContentProvider {
public static final String AUTHORITY = "com.android.exchange.mock.provider";
/*package*/ static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
/*package*/ static final int TABLE = 100;
/*package*/ static final int RECORD = 101;
public static final String ID_COLUMN = "_id";
// public MockProvider(Context context) {
// super(context, null, null, null);
// }
public MockProvider() {
super();
}
// We'll store our values here
private HashMap<String, ContentValues> mMockStore = new HashMap<String, ContentValues>();
// And we'll generate new id's from here
long mMockId = 1;
/**
* Create a Uri for MockProvider from a given Uri
* @param uri the Uri from which the MockProvider Uri will be created
* @return a Uri that can be used with MockProvider
*/
public static Uri uri(Uri uri) {
return new Uri.Builder().scheme("content").authority(AUTHORITY)
.path(uri.getPath().substring(1)).build();
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
if (selection != null || selectionArgs != null) {
throw new UnsupportedOperationException();
}
String path = uri.getPath();
if (mMockStore.containsKey(path)) {
mMockStore.remove(path);
return 1;
} else {
return 0;
}
}
@Override
public String getType(Uri uri) {
throw new UnsupportedOperationException();
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// Remove the leading slash
String table = uri.getPath().substring(1);
long id = mMockId++;
Uri newUri = new Uri.Builder().scheme("content").authority(AUTHORITY).path(table)
.appendPath(Long.toString(id)).build();
// Remember to store the _id
values.put(ID_COLUMN, id);
mMockStore.put(newUri.getPath(), values);
int match = sURIMatcher.match(uri);
if (match == UriMatcher.NO_MATCH) {
sURIMatcher.addURI(AUTHORITY, table, TABLE);
sURIMatcher.addURI(AUTHORITY, table + "/#", RECORD);
}
return newUri;
}
@Override
public boolean onCreate() {
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
if (selection != null || selectionArgs != null || sortOrder != null || projection == null) {
throw new UnsupportedOperationException();
}
final int match = sURIMatcher.match(uri(uri));
ArrayList<ContentValues> valuesList = new ArrayList<ContentValues>();
switch(match) {
case TABLE:
Set<Entry<String, ContentValues>> entrySet = mMockStore.entrySet();
String prefix = uri.getPath() + "/";
for (Entry<String, ContentValues> entry: entrySet) {
if (entry.getKey().startsWith(prefix)) {
valuesList.add(entry.getValue());
}
}
break;
case RECORD:
ContentValues values = mMockStore.get(uri.getPath());
if (values != null) {
valuesList.add(values);
}
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
MatrixCursor cursor = new MatrixCursor(projection, 1);
for (ContentValues cv: valuesList) {
Object[] rowValues = new Object[projection.length];
int i = 0;
for (String column: projection) {
rowValues[i++] = cv.get(column);
}
cursor.addRow(rowValues);
}
return cursor;
}
@Override
public int update(Uri uri, ContentValues newValues, String selection, String[] selectionArgs) {
if (selection != null || selectionArgs != null) {
throw new UnsupportedOperationException();
}
final int match = sURIMatcher.match(uri(uri));
ArrayList<ContentValues> updateValuesList = new ArrayList<ContentValues>();
String path = uri.getPath();
switch(match) {
case TABLE:
Set<Entry<String, ContentValues>> entrySet = mMockStore.entrySet();
String prefix = path + "/";
for (Entry<String, ContentValues> entry: entrySet) {
if (entry.getKey().startsWith(prefix)) {
updateValuesList.add(entry.getValue());
}
}
break;
case RECORD:
ContentValues cv = mMockStore.get(path);
if (cv != null) {
updateValuesList.add(cv);
}
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
Set<Entry<String, Object>> newValuesSet = newValues.valueSet();
for (Entry<String, Object> entry: newValuesSet) {
String key = entry.getKey();
Object value = entry.getValue();
for (ContentValues targetValues: updateValuesList) {
if (value instanceof Integer) {
targetValues.put(key, (Integer)value);
} else if (value instanceof Long) {
targetValues.put(key, (Long)value);
} else if (value instanceof String) {
targetValues.put(key, (String)value);
} else if (value instanceof Boolean) {
targetValues.put(key, (Boolean)value);
} else {
throw new IllegalArgumentException();
}
}
}
for (ContentValues targetValues: updateValuesList) {
mMockStore.put(path + "/" + targetValues.getAsLong(ID_COLUMN), targetValues);
}
return updateValuesList.size();
}
}