• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TestProvider()74     public TestProvider() {
75         super();
76     }
77 
TestProvider(Context context)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      */
uri(Uri uri)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
delete(Uri uri, String selection, String[] selectionArgs)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
getType(Uri uri)114     public String getType(Uri uri) {
115         throw new UnsupportedOperationException();
116     }
117 
118     @Override
insert(Uri uri, ContentValues values)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
onCreate()137     public boolean onCreate() {
138         return false;
139     }
140 
141     @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)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
update(Uri uri, ContentValues newValues, String selection, String[] selectionArgs)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