• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MockProvider(Context context)73     public MockProvider(Context context) {
74         super(context, null, null, null);
75     }
76 
MockProvider()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      */
uri(Uri uri)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
delete(Uri uri, String selection, String[] selectionArgs)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
getType(Uri uri)111     public String getType(Uri uri) {
112         throw new UnsupportedOperationException();
113     }
114 
115     @Override
insert(Uri uri, ContentValues values)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
onCreate()134     public boolean onCreate() {
135         return false;
136     }
137 
138     @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)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
update(Uri uri, ContentValues newValues, String selection, String[] selectionArgs)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