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