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
17package com.android.contacts.tests.mocks;
18
19import com.google.android.collect.Lists;
20import com.google.android.collect.Maps;
21
22import android.content.ContentProvider;
23import android.content.ContentValues;
24import android.database.Cursor;
25import android.database.MatrixCursor;
26import android.net.Uri;
27import android.text.TextUtils;
28
29import java.util.ArrayList;
30import java.util.Arrays;
31import java.util.HashMap;
32import java.util.Iterator;
33
34import junit.framework.Assert;
35
36/**
37 * A programmable mock content provider.
38 */
39public class MockContentProvider extends ContentProvider {
40    private static final String TAG = "MockContentProvider";
41
42    public static class Query {
43
44        private final Uri mUri;
45        private String[] mProjection;
46        private String[] mDefaultProjection;
47        private String mSelection;
48        private String[] mSelectionArgs;
49        private String mSortOrder;
50        private ArrayList<Object> mRows = new ArrayList<Object>();
51        private boolean mAnyProjection;
52        private boolean mAnySelection;
53        private boolean mAnySortOrder;
54        private boolean mAnyNumberOfTimes;
55
56        private boolean mExecuted;
57
58        public Query(Uri uri) {
59            mUri = uri;
60        }
61
62        @Override
63        public String toString() {
64            return queryToString(mUri, mProjection, mSelection, mSelectionArgs, mSortOrder);
65        }
66
67        public Query withProjection(String... projection) {
68            mProjection = projection;
69            return this;
70        }
71
72        public Query withDefaultProjection(String... projection) {
73            mDefaultProjection = projection;
74            return this;
75        }
76
77        public Query withAnyProjection() {
78            mAnyProjection = true;
79            return this;
80        }
81
82        public Query withSelection(String selection, String... selectionArgs) {
83            mSelection = selection;
84            mSelectionArgs = selectionArgs;
85            return this;
86        }
87
88        public Query withAnySelection() {
89            mAnySelection = true;
90            return this;
91        }
92
93        public Query withSortOrder(String sortOrder) {
94            mSortOrder = sortOrder;
95            return this;
96        }
97
98        public Query withAnySortOrder() {
99            mAnySortOrder = true;
100            return this;
101        }
102
103        public Query returnRow(ContentValues values) {
104            mRows.add(values);
105            return this;
106        }
107
108        public Query returnRow(Object... row) {
109            mRows.add(row);
110            return this;
111        }
112
113        public Query returnEmptyCursor() {
114            mRows.clear();
115            return this;
116        }
117
118        public Query anyNumberOfTimes() {
119            mAnyNumberOfTimes = true;
120            return this;
121        }
122
123        public boolean equals(Uri uri, String[] projection, String selection,
124                String[] selectionArgs, String sortOrder) {
125            if (!uri.equals(mUri)) {
126                return false;
127            }
128
129            if (!mAnyProjection && !equals(projection, mProjection)) {
130                return false;
131            }
132
133            if (!mAnySelection && !equals(selection, mSelection)) {
134                return false;
135            }
136
137            if (!mAnySelection && !equals(selectionArgs, mSelectionArgs)) {
138                return false;
139            }
140
141            if (!mAnySortOrder && !equals(sortOrder, mSortOrder)) {
142                return false;
143            }
144
145            return true;
146        }
147
148        private boolean equals(String string1, String string2) {
149            if (TextUtils.isEmpty(string1)) {
150                string1 = null;
151            }
152            if (TextUtils.isEmpty(string2)) {
153                string2 = null;
154            }
155            return TextUtils.equals(string1, string2);
156        }
157
158        private static boolean equals(String[] array1, String[] array2) {
159            boolean empty1 = array1 == null || array1.length == 0;
160            boolean empty2 = array2 == null || array2.length == 0;
161            if (empty1 && empty2) {
162                return true;
163            }
164            if (empty1 != empty2 && (empty1 || empty2)) {
165                return false;
166            }
167
168            if (array1.length != array2.length) return false;
169
170            for (int i = 0; i < array1.length; i++) {
171                if (!array1[i].equals(array2[i])) {
172                    return false;
173                }
174            }
175            return true;
176        }
177
178        public Cursor getResult(String[] projection) {
179            String[] columnNames;
180            if (mAnyProjection) {
181                columnNames = projection;
182            } else {
183                columnNames = mProjection != null ? mProjection : mDefaultProjection;
184            }
185
186            MatrixCursor cursor = new MatrixCursor(columnNames);
187            for (Object row : mRows) {
188                if (row instanceof Object[]) {
189                    cursor.addRow((Object[]) row);
190                } else {
191                    ContentValues values = (ContentValues) row;
192                    Object[] columns = new Object[projection.length];
193                    for (int i = 0; i < projection.length; i++) {
194                        columns[i] = values.get(projection[i]);
195                    }
196                    cursor.addRow(columns);
197                }
198            }
199            return cursor;
200        }
201    }
202
203    public static class TypeQuery {
204        private final Uri mUri;
205        private final String mType;
206
207        public TypeQuery(Uri uri, String type) {
208            mUri = uri;
209            mType = type;
210        }
211
212        public Uri getUri() {
213            return mUri;
214        }
215
216        public String getType() {
217            return mType;
218        }
219
220        @Override
221        public String toString() {
222            return mUri + " --> " + mType;
223        }
224
225        public boolean equals(Uri uri) {
226            return getUri().equals(uri);
227        }
228    }
229
230    private ArrayList<Query> mExpectedQueries = new ArrayList<Query>();
231    private HashMap<Uri, String> mExpectedTypeQueries = Maps.newHashMap();
232
233    @Override
234    public boolean onCreate() {
235        return true;
236    }
237
238    public Query expectQuery(Uri contentUri) {
239        Query query = new Query(contentUri);
240        mExpectedQueries.add(query);
241        return query;
242    }
243
244    public void expectTypeQuery(Uri uri, String type) {
245        mExpectedTypeQueries.put(uri, type);
246    }
247
248    @Override
249    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
250            String sortOrder) {
251
252        for (Iterator<Query> iterator = mExpectedQueries.iterator(); iterator.hasNext();) {
253            Query query = iterator.next();
254            if (query.equals(uri, projection, selection, selectionArgs, sortOrder)) {
255                query.mExecuted = true;
256                if (!query.mAnyNumberOfTimes) {
257                    iterator.remove();
258                }
259                return query.getResult(projection);
260            }
261        }
262
263        if (mExpectedQueries.isEmpty()) {
264            Assert.fail("Unexpected query: "
265                    + queryToString(uri, projection, selection, selectionArgs, sortOrder));
266        } else {
267            StringBuilder sb = new StringBuilder();
268            sb.append(mExpectedQueries.get(0));
269            for (int i = 1; i < mExpectedQueries.size(); i++) {
270                sb.append("\n              ").append(mExpectedQueries.get(i));
271            }
272            Assert.fail("Incorrect query.\n    Expected: " + sb + "\n      Actual: " +
273                    queryToString(uri, projection, selection, selectionArgs, sortOrder));
274        }
275        return null;
276    }
277
278    @Override
279    public int delete(Uri uri, String selection, String[] selectionArgs) {
280        throw new UnsupportedOperationException();
281    }
282
283    @Override
284    public String getType(Uri uri) {
285        if (mExpectedTypeQueries.isEmpty()) {
286            Assert.fail("Unexpected getType query: " + uri);
287        }
288
289        String mimeType = mExpectedTypeQueries.get(uri);
290        if (mimeType != null) {
291            return mimeType;
292        }
293
294        Assert.fail("Unknown mime type for: " + uri);
295        return null;
296    }
297
298    @Override
299    public Uri insert(Uri uri, ContentValues values) {
300        throw new UnsupportedOperationException();
301    }
302
303    @Override
304    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
305        throw new UnsupportedOperationException();
306    }
307
308    private static String queryToString(Uri uri, String[] projection, String selection,
309            String[] selectionArgs, String sortOrder) {
310        StringBuilder sb = new StringBuilder();
311        sb.append(uri).append(" ");
312        if (projection != null) {
313            sb.append(Arrays.toString(projection));
314        } else {
315            sb.append("[]");
316        }
317        if (selection != null) {
318            sb.append(" selection: '").append(selection).append("'");
319            if (selectionArgs != null) {
320                sb.append(Arrays.toString(selectionArgs));
321            } else {
322                sb.append("[]");
323            }
324        }
325        if (sortOrder != null) {
326            sb.append(" sort: '").append(sortOrder).append("'");
327        }
328        return sb.toString();
329    }
330
331    public void verify() {
332        ArrayList<Query> mMissedQueries = Lists.newArrayList();
333        for (Query query : mExpectedQueries) {
334            if (!query.mExecuted) {
335                mMissedQueries.add(query);
336            }
337        }
338        Assert.assertTrue("Not all expected queries have been called: " +
339                mMissedQueries, mMissedQueries.isEmpty());
340    }
341}
342