Home | History | Annotate | Download | only in provider
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.exchange.provider;
     18 
     19 import android.content.ContentProvider;
     20 import android.content.ContentValues;
     21 import android.content.Context;
     22 import android.content.UriMatcher;
     23 import android.database.Cursor;
     24 import android.database.MatrixCursor;
     25 import android.net.Uri;
     26 
     27 import java.util.ArrayList;
     28 import java.util.HashMap;
     29 import java.util.Set;
     30 import java.util.Map.Entry;
     31 
     32 /**
     33  * MockProvider is a ContentProvider that can be used to simulate the storage and retrieval of
     34  * records from any ContentProvider, even if that ContentProvider does not exist in the caller's
     35  * package.  It is specifically designed to enable testing of sync adapters that create
     36  * ContentProviderOperations (CPOs) that are then executed using ContentResolver.applyBatch()
     37  *
     38  * Why is this useful?  Because we can't instantiate CalendarProvider or ContactsProvider from our
     39  * package, as required by MockContentResolver.addProvider()
     40  *
     41  * Usage:
     42  *     ContentResolver.applyBatch(MockProvider.AUTHORITY, batch) will cause the CPOs to be executed,
     43  *     returning an array of ContentProviderResult; in the case of inserts, the result will include
     44  *     a Uri that can be used via query().  Note that the CPOs in the batch can contain references
     45  *     to any authority.
     46  *
     47  *     query() does not allow non-null selection, selectionArgs, or sortOrder arguments; the
     48  *     presence of these will result in an UnsupportedOperationException
     49  *
     50  *     insert() acts as expected, returning a Uri that can be directly used in a query
     51  *
     52  *     delete() and update() do not allow non-null selection or selectionArgs arguments; the
     53  *     presence of these will result in an UnsupportedOperationException
     54  *
     55  *     NOTE: When using any operation other than applyBatch, the Uri to be used must be created
     56  *     with MockProvider.uri(yourUri).  This guarantees that the operation is sent to MockProvider
     57  *
     58  *     NOTE: MockProvider only simulates direct storage/retrieval of rows; it does not (and can not)
     59  *     simulate other actions (e.g. creation of ancillary data) that the actual provider might
     60  *     perform
     61  *
     62  *     NOTE: See MockProviderTests for usage examples
     63  **/
     64 public class MockProvider extends ContentProvider {
     65     public static final String AUTHORITY = "com.android.exchange.mock.provider";
     66     /*package*/ static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
     67 
     68     /*package*/ static final int TABLE = 100;
     69     /*package*/ static final int RECORD = 101;
     70 
     71     public static final String ID_COLUMN = "_id";
     72 
     73     public MockProvider(Context context) {
     74         super(context, null, null, null);
     75     }
     76 
     77     public MockProvider() {
     78         super();
     79     }
     80 
     81     // We'll store our values here
     82     private HashMap<String, ContentValues> mMockStore = new HashMap<String, ContentValues>();
     83     // And we'll generate new id's from here
     84     long mMockId = 1;
     85 
     86     /**
     87      * Create a Uri for MockProvider from a given Uri
     88      * @param uri the Uri from which the MockProvider Uri will be created
     89      * @return a Uri that can be used with MockProvider
     90      */
     91     public static Uri uri(Uri uri) {
     92         return new Uri.Builder().scheme("content").authority(AUTHORITY)
     93             .path(uri.getPath().substring(1)).build();
     94     }
     95 
     96     @Override
     97     public int delete(Uri uri, String selection, String[] selectionArgs) {
     98         if (selection != null || selectionArgs != null) {
     99             throw new UnsupportedOperationException();
    100         }
    101         String path = uri.getPath();
    102         if (mMockStore.containsKey(path)) {
    103             mMockStore.remove(path);
    104             return 1;
    105         } else {
    106             return 0;
    107         }
    108     }
    109 
    110     @Override
    111     public String getType(Uri uri) {
    112         throw new UnsupportedOperationException();
    113     }
    114 
    115     @Override
    116     public Uri insert(Uri uri, ContentValues values) {
    117         // Remove the leading slash
    118         String table = uri.getPath().substring(1);
    119         long id = mMockId++;
    120         Uri newUri = new Uri.Builder().scheme("content").authority(AUTHORITY).path(table)
    121             .appendPath(Long.toString(id)).build();
    122         // Remember to store the _id
    123         values.put(ID_COLUMN, id);
    124         mMockStore.put(newUri.getPath(), values);
    125         int match = sURIMatcher.match(uri);
    126         if (match == UriMatcher.NO_MATCH) {
    127             sURIMatcher.addURI(AUTHORITY, table, TABLE);
    128             sURIMatcher.addURI(AUTHORITY, table + "/#", RECORD);
    129         }
    130         return newUri;
    131     }
    132 
    133     @Override
    134     public boolean onCreate() {
    135         return false;
    136     }
    137 
    138     @Override
    139     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
    140             String sortOrder) {
    141         if (selection != null || selectionArgs != null || sortOrder != null || projection == null) {
    142             throw new UnsupportedOperationException();
    143         }
    144         final int match = sURIMatcher.match(uri(uri));
    145         ArrayList<ContentValues> valuesList = new ArrayList<ContentValues>();
    146         switch(match) {
    147             case TABLE:
    148                 Set<Entry<String, ContentValues>> entrySet = mMockStore.entrySet();
    149                 String prefix = uri.getPath() + "/";
    150                 for (Entry<String, ContentValues> entry: entrySet) {
    151                     if (entry.getKey().startsWith(prefix)) {
    152                         valuesList.add(entry.getValue());
    153                     }
    154                 }
    155                 break;
    156             case RECORD:
    157                 ContentValues values = mMockStore.get(uri.getPath());
    158                 if (values != null) {
    159                     valuesList.add(values);
    160                 }
    161                 break;
    162             default:
    163                 throw new IllegalArgumentException("Unknown URI " + uri);
    164         }
    165         MatrixCursor cursor = new MatrixCursor(projection, 1);
    166         for (ContentValues cv: valuesList) {
    167             Object[] rowValues = new Object[projection.length];
    168             int i = 0;
    169             for (String column: projection) {
    170                 rowValues[i++] = cv.get(column);
    171             }
    172             cursor.addRow(rowValues);
    173         }
    174         return cursor;
    175     }
    176 
    177     @Override
    178     public int update(Uri uri, ContentValues newValues, String selection, String[] selectionArgs) {
    179         if (selection != null || selectionArgs != null) {
    180             throw new UnsupportedOperationException();
    181         }
    182         final int match = sURIMatcher.match(uri(uri));
    183         ArrayList<ContentValues> updateValuesList = new ArrayList<ContentValues>();
    184         String path = uri.getPath();
    185         switch(match) {
    186             case TABLE:
    187                 Set<Entry<String, ContentValues>> entrySet = mMockStore.entrySet();
    188                 String prefix = path + "/";
    189                 for (Entry<String, ContentValues> entry: entrySet) {
    190                     if (entry.getKey().startsWith(prefix)) {
    191                         updateValuesList.add(entry.getValue());
    192                     }
    193                 }
    194                 break;
    195             case RECORD:
    196                 ContentValues cv = mMockStore.get(path);
    197                 if (cv != null) {
    198                     updateValuesList.add(cv);
    199                 }
    200                 break;
    201             default:
    202                 throw new IllegalArgumentException("Unknown URI " + uri);
    203         }
    204         Set<Entry<String, Object>> newValuesSet = newValues.valueSet();
    205         for (Entry<String, Object> entry: newValuesSet) {
    206             String key = entry.getKey();
    207             Object value = entry.getValue();
    208             for (ContentValues targetValues: updateValuesList) {
    209                 if (value instanceof Integer) {
    210                     targetValues.put(key, (Integer)value);
    211                 } else if (value instanceof Long) {
    212                     targetValues.put(key, (Long)value);
    213                 } else if (value instanceof String) {
    214                     targetValues.put(key, (String)value);
    215                 } else if (value instanceof Boolean) {
    216                     targetValues.put(key, (Boolean)value);
    217                 } else {
    218                     throw new IllegalArgumentException();
    219                 }
    220             }
    221         }
    222         for (ContentValues targetValues: updateValuesList) {
    223             mMockStore.put(path + "/" + targetValues.getAsLong(ID_COLUMN), targetValues);
    224         }
    225         return updateValuesList.size();
    226     }
    227 }
    228