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.content.pm.ProviderInfo;
     25 import android.database.Cursor;
     26 import android.database.MatrixCursor;
     27 import android.net.Uri;
     28 
     29 import com.android.mail.utils.MatrixCursorWithCachedColumns;
     30 
     31 import java.util.ArrayList;
     32 import java.util.HashMap;
     33 import java.util.Map.Entry;
     34 import java.util.Set;
     35 
     36 /**
     37  * TestProvider is a ContentProvider that can be used to simulate the storage and retrieval of
     38  * rows from any ContentProvider, even if that provider does not exist in the caller's package.
     39  *
     40  * It is specifically designed to enable testing of sync adapters that create
     41  * ContentProviderOperations (CPOs) that are then executed using ContentResolver.applyBatch().
     42  * Why is this useful? Because we can't instantiate CalendarProvider or ContactsProvider from our
     43  * package, as required by MockContentResolver.addProvider()
     44  *
     45  * Usage:
     46  *
     47  * ContentResolver.applyBatch(MockProvider.AUTHORITY, batch) will cause the CPOs to be executed,
     48  * returning an array of ContentProviderResult; in the case of inserts, the result will include
     49  * a Uri that can be used via query(). Note that the CPOs can contain references to any authority.
     50  *
     51  * query() does not allow non-null selection, selectionArgs, or sortOrder arguments; the
     52  * presence of these will result in an UnsupportedOperationException insert() acts as expected,
     53  * returning a Uri that can be directly used in a query
     54  *
     55  * delete() and update() do not allow non-null selection or selectionArgs arguments; the presence
     56  * of these will result in an UnsupportedOperationException
     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 perform
     60  *
     61  * NOTE: See MockProviderTests for usage examples
     62  **/
     63 public class TestProvider extends ContentProvider {
     64     public static final String AUTHORITY = "com.android.mail.mock.provider";
     65 
     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 TestProvider() {
     74         super();
     75     }
     76 
     77     public TestProvider(Context context) {
     78         this();
     79         ProviderInfo info = new ProviderInfo();
     80         info.authority = AUTHORITY;
     81         attachInfo(context, info);
     82     }
     83 
     84     // We'll store our values here
     85     private HashMap<String, ContentValues> mMockStore = new HashMap<String, ContentValues>();
     86     // And we'll generate new id's from here
     87     long mMockId = 1;
     88 
     89     @Override
     90     public int delete(Uri uri, String selection, String[] selectionArgs) {
     91         if (selection != null || selectionArgs != null) {
     92             throw new UnsupportedOperationException();
     93         }
     94         String path = uri.getPath();
     95         if (mMockStore.containsKey(path)) {
     96             mMockStore.remove(path);
     97             return 1;
     98         } else {
     99             return 0;
    100         }
    101     }
    102 
    103     @Override
    104     public String getType(Uri uri) {
    105         throw new UnsupportedOperationException();
    106     }
    107 
    108     @Override
    109     public Uri insert(Uri uri, ContentValues values) {
    110         // Remove the leading slash
    111         String table = uri.getPath().substring(1);
    112         long id = mMockId++;
    113         Uri newUri = new Uri.Builder().scheme("content").authority(AUTHORITY).path(table)
    114                 .appendPath(Long.toString(id)).build();
    115         // Remember to store the _id
    116         values.put(ID_COLUMN, id);
    117         mMockStore.put(newUri.getPath(), values);
    118         int match = sURIMatcher.match(uri);
    119         if (match == UriMatcher.NO_MATCH) {
    120             sURIMatcher.addURI(AUTHORITY, table, TABLE);
    121             sURIMatcher.addURI(AUTHORITY, table + "/#", RECORD);
    122         }
    123         return newUri;
    124     }
    125 
    126     @Override
    127     public boolean onCreate() {
    128         return false;
    129     }
    130 
    131     @Override
    132     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
    133             String sortOrder) {
    134         if (selection != null || selectionArgs != null || sortOrder != null || projection == null) {
    135             throw new UnsupportedOperationException();
    136         }
    137         final int match = sURIMatcher.match(uri);
    138         ArrayList<ContentValues> valuesList = new ArrayList<ContentValues>();
    139         switch (match) {
    140             case TABLE:
    141                 Set<Entry<String, ContentValues>> entrySet = mMockStore.entrySet();
    142                 String prefix = uri.getPath() + "/";
    143                 for (Entry<String, ContentValues> entry : entrySet) {
    144                     if (entry.getKey().startsWith(prefix)) {
    145                         valuesList.add(entry.getValue());
    146                     }
    147                 }
    148                 break;
    149             case RECORD:
    150                 ContentValues values = mMockStore.get(uri.getPath());
    151                 if (values != null) {
    152                     valuesList.add(values);
    153                 }
    154                 break;
    155             default:
    156                 throw new IllegalArgumentException("Unknown URI " + uri);
    157         }
    158         MatrixCursor cursor = new MatrixCursorWithCachedColumns(projection, 1);
    159         for (ContentValues cv : valuesList) {
    160             Object[] rowValues = new Object[projection.length];
    161             int i = 0;
    162             for (String column : projection) {
    163                 rowValues[i++] = cv.get(column);
    164             }
    165             cursor.addRow(rowValues);
    166         }
    167         return cursor;
    168     }
    169 
    170     @Override
    171     public int update(Uri uri, ContentValues newValues, String selection, String[] selectionArgs) {
    172         if (selection != null || selectionArgs != null) {
    173             throw new UnsupportedOperationException();
    174         }
    175         final int match = sURIMatcher.match(uri);
    176         ArrayList<ContentValues> updateValuesList = new ArrayList<ContentValues>();
    177         String path = uri.getPath();
    178         switch (match) {
    179             case TABLE:
    180                 Set<Entry<String, ContentValues>> entrySet = mMockStore.entrySet();
    181                 String prefix = path + "/";
    182                 for (Entry<String, ContentValues> entry : entrySet) {
    183                     if (entry.getKey().startsWith(prefix)) {
    184                         updateValuesList.add(entry.getValue());
    185                     }
    186                 }
    187                 break;
    188             case RECORD:
    189                 ContentValues cv = mMockStore.get(path);
    190                 if (cv != null) {
    191                     updateValuesList.add(cv);
    192                 }
    193                 break;
    194             default:
    195                 throw new IllegalArgumentException("Unknown URI " + uri);
    196         }
    197         Set<Entry<String, Object>> newValuesSet = newValues.valueSet();
    198         for (Entry<String, Object> entry : newValuesSet) {
    199             String key = entry.getKey();
    200             Object value = entry.getValue();
    201             for (ContentValues targetValues : updateValuesList) {
    202                 if (value instanceof Integer) {
    203                     targetValues.put(key, (Integer) value);
    204                 } else if (value instanceof Long) {
    205                     targetValues.put(key, (Long) value);
    206                 } else if (value instanceof String) {
    207                     targetValues.put(key, (String) value);
    208                 } else if (value instanceof Boolean) {
    209                     targetValues.put(key, (Boolean) value);
    210                 } else {
    211                     throw new IllegalArgumentException();
    212                 }
    213             }
    214         }
    215         for (ContentValues targetValues : updateValuesList) {
    216             switch(match) {
    217                 case TABLE:
    218                     mMockStore.put(path + "/" + targetValues.getAsLong(ID_COLUMN), targetValues);
    219                     break;
    220                 case RECORD:
    221                     mMockStore.put(path, targetValues);
    222                     break;
    223             }
    224         }
    225         return updateValuesList.size();
    226     }
    227 }
    228