Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 package org.mozilla.gecko.tests;
7 import java.io.File;
8 import java.util.ArrayList;
9 import java.util.LinkedList;
10 import java.util.concurrent.Callable;
12 import org.mozilla.gecko.db.BrowserContract;
13 import org.mozilla.gecko.db.BrowserProvider;
15 import android.content.ContentProvider;
16 import android.content.ContentProviderOperation;
17 import android.content.ContentProviderResult;
18 import android.content.ContentValues;
19 import android.content.Context;
20 import android.content.OperationApplicationException;
21 import android.content.SharedPreferences;
22 import android.content.pm.ApplicationInfo;
23 import android.content.res.AssetManager;
24 import android.content.res.Resources;
25 import android.database.ContentObserver;
26 import android.database.Cursor;
27 import android.net.Uri;
28 import android.os.Build;
29 import android.test.IsolatedContext;
30 import android.test.RenamingDelegatingContext;
31 import android.test.mock.MockContentResolver;
32 import android.test.mock.MockContext;
34 /*
35 * ContentProviderTest provides the infrastructure to run content provider
36 * tests in an controlled/isolated environment, guaranteeing that your tests
37 * will not affect or be affected by any UI-related code. This is basically
38 * a heavily adapted port of Android's ProviderTestCase2 to work on Mozilla's
39 * infrastructure.
40 *
41 * For some tests, we need to have access to UI parts, or at least launch
42 * the activity so the assets with test data become available, which requires
43 * that we derive this test from BaseTest and consequently pull in some more
44 * UI code than we'd ideally want. Furthermore, we need to pass the
45 * Activity and not the instrumentation Context for the UI part to find some
46 * of its required resources.
47 * Similarly, we need to pass the Activity instead of the Instrumentation
48 * Context down to some of our users (still wrapped in the Delegating Provider)
49 * because they will stop working correctly if we do not. A typical problem
50 * is that databases used in the ContentProvider will be attempted to be
51 * opened twice.
52 */
53 abstract class ContentProviderTest extends BaseTest {
54 protected ContentProvider mProvider;
55 protected ChangeRecordingMockContentResolver mResolver;
56 protected ArrayList<Runnable> mTests;
57 protected String mDatabaseName;
58 protected String mProviderAuthority;
59 protected IsolatedContext mProviderContext;
61 private class ContentProviderMockContext extends MockContext {
62 @Override
63 public Resources getResources() {
64 // We will fail to find some resources if we don't point
65 // at the original activity.
66 return ((Context)getActivity()).getResources();
67 }
69 @Override
70 public String getPackageName() {
71 return getInstrumentation().getContext().getPackageName();
72 }
74 @Override
75 public String getPackageResourcePath() {
76 return getInstrumentation().getContext().getPackageResourcePath();
77 }
79 @Override
80 public File getDir(String name, int mode) {
81 return getInstrumentation().getContext().getDir(this.getClass().getSimpleName() + "_" + name, mode);
82 }
84 @Override
85 public Context getApplicationContext() {
86 return this;
87 }
89 @Override
90 public SharedPreferences getSharedPreferences(String name, int mode) {
91 return getInstrumentation().getContext().getSharedPreferences(name, mode);
92 }
94 @Override
95 public ApplicationInfo getApplicationInfo() {
96 return getInstrumentation().getContext().getApplicationInfo();
97 }
98 }
100 protected class DelegatingTestContentProvider extends ContentProvider {
101 ContentProvider mTargetProvider;
103 public DelegatingTestContentProvider(ContentProvider targetProvider) {
104 super();
105 mTargetProvider = targetProvider;
106 }
108 private Uri appendTestParam(Uri uri) {
109 try {
110 return appendUriParam(uri, BrowserContract.PARAM_IS_TEST, "1");
111 } catch (Exception e) {}
113 return null;
114 }
116 @Override
117 public boolean onCreate() {
118 return mTargetProvider.onCreate();
119 }
121 @Override
122 public String getType(Uri uri) {
123 return mTargetProvider.getType(uri);
124 }
126 @Override
127 public int delete(Uri uri, String selection, String[] selectionArgs) {
128 return mTargetProvider.delete(appendTestParam(uri), selection, selectionArgs);
129 }
131 @Override
132 public Uri insert(Uri uri, ContentValues values) {
133 return mTargetProvider.insert(appendTestParam(uri), values);
134 }
136 @Override
137 public int update(Uri uri, ContentValues values, String selection,
138 String[] selectionArgs) {
139 return mTargetProvider.update(appendTestParam(uri), values,
140 selection, selectionArgs);
141 }
143 @Override
144 public Cursor query(Uri uri, String[] projection, String selection,
145 String[] selectionArgs, String sortOrder) {
146 return mTargetProvider.query(appendTestParam(uri), projection, selection,
147 selectionArgs, sortOrder);
148 }
150 @Override
151 public ContentProviderResult[] applyBatch (ArrayList<ContentProviderOperation> operations)
152 throws OperationApplicationException {
153 return mTargetProvider.applyBatch(operations);
154 }
156 @Override
157 public int bulkInsert(Uri uri, ContentValues[] values) {
158 return mTargetProvider.bulkInsert(appendTestParam(uri), values);
159 }
161 public ContentProvider getTargetProvider() {
162 return mTargetProvider;
163 }
164 }
166 /*
167 * A MockContentResolver that records each URI that is supplied to
168 * notifyChange. Warning: the list of changed URIs is not
169 * synchronized.
170 */
171 protected class ChangeRecordingMockContentResolver extends MockContentResolver {
172 public final LinkedList<Uri> notifyChangeList = new LinkedList<Uri>();
174 @Override
175 public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
176 notifyChangeList.addLast(uri);
178 super.notifyChange(uri, observer, syncToNetwork);
179 }
180 }
182 /**
183 * Factory function that makes new ContentProvider instances.
184 * <p>
185 * We want a fresh provider each test, so this should be invoked in
186 * <code>setUp</code> before each individual test.
187 */
188 protected static Callable<ContentProvider> sBrowserProviderCallable = new Callable<ContentProvider>() {
189 @Override
190 public ContentProvider call() {
191 return new BrowserProvider();
192 }
193 };
195 private void setUpContentProvider(ContentProvider targetProvider) throws Exception {
196 mResolver = new ChangeRecordingMockContentResolver();
198 final String filenamePrefix = this.getClass().getSimpleName() + ".";
199 RenamingDelegatingContext targetContextWrapper =
200 new RenamingDelegatingContext(
201 new ContentProviderMockContext(),
202 (Context)getActivity(),
203 filenamePrefix);
205 mProviderContext = new IsolatedContext(mResolver, targetContextWrapper);
207 targetProvider.attachInfo(mProviderContext, null);
209 mProvider = new DelegatingTestContentProvider(targetProvider);
210 mProvider.attachInfo(mProviderContext, null);
212 mResolver.addProvider(mProviderAuthority, mProvider);
213 }
215 public static Uri appendUriParam(Uri uri, String param, String value) {
216 return uri.buildUpon().appendQueryParameter(param, value).build();
217 }
219 public void setTestName(String testName) {
220 mAsserter.setTestName(this.getClass().getName() + " - " + testName);
221 }
223 @Override
224 public void setUp() throws Exception {
225 throw new UnsupportedOperationException("You should call setUp(authority, databaseName) instead");
226 }
228 public void setUp(Callable<ContentProvider> contentProviderFactory, String authority, String databaseName) throws Exception {
229 super.setUp();
231 mTests = new ArrayList<Runnable>();
232 mDatabaseName = databaseName;
234 mProviderAuthority = authority;
236 setUpContentProvider(contentProviderFactory.call());
237 }
239 @Override
240 public void tearDown() throws Exception {
241 if (Build.VERSION.SDK_INT >= 11) {
242 mProvider.shutdown();
243 }
245 if (mDatabaseName != null) {
246 mProviderContext.deleteDatabase(mDatabaseName);
247 }
249 super.tearDown();
250 }
252 public AssetManager getAssetManager() {
253 return getInstrumentation().getContext().getAssets();
254 }
255 }