1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/tests/ContentProviderTest.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,255 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +package org.mozilla.gecko.tests; 1.9 + 1.10 +import java.io.File; 1.11 +import java.util.ArrayList; 1.12 +import java.util.LinkedList; 1.13 +import java.util.concurrent.Callable; 1.14 + 1.15 +import org.mozilla.gecko.db.BrowserContract; 1.16 +import org.mozilla.gecko.db.BrowserProvider; 1.17 + 1.18 +import android.content.ContentProvider; 1.19 +import android.content.ContentProviderOperation; 1.20 +import android.content.ContentProviderResult; 1.21 +import android.content.ContentValues; 1.22 +import android.content.Context; 1.23 +import android.content.OperationApplicationException; 1.24 +import android.content.SharedPreferences; 1.25 +import android.content.pm.ApplicationInfo; 1.26 +import android.content.res.AssetManager; 1.27 +import android.content.res.Resources; 1.28 +import android.database.ContentObserver; 1.29 +import android.database.Cursor; 1.30 +import android.net.Uri; 1.31 +import android.os.Build; 1.32 +import android.test.IsolatedContext; 1.33 +import android.test.RenamingDelegatingContext; 1.34 +import android.test.mock.MockContentResolver; 1.35 +import android.test.mock.MockContext; 1.36 + 1.37 +/* 1.38 + * ContentProviderTest provides the infrastructure to run content provider 1.39 + * tests in an controlled/isolated environment, guaranteeing that your tests 1.40 + * will not affect or be affected by any UI-related code. This is basically 1.41 + * a heavily adapted port of Android's ProviderTestCase2 to work on Mozilla's 1.42 + * infrastructure. 1.43 + * 1.44 + * For some tests, we need to have access to UI parts, or at least launch 1.45 + * the activity so the assets with test data become available, which requires 1.46 + * that we derive this test from BaseTest and consequently pull in some more 1.47 + * UI code than we'd ideally want. Furthermore, we need to pass the 1.48 + * Activity and not the instrumentation Context for the UI part to find some 1.49 + * of its required resources. 1.50 + * Similarly, we need to pass the Activity instead of the Instrumentation 1.51 + * Context down to some of our users (still wrapped in the Delegating Provider) 1.52 + * because they will stop working correctly if we do not. A typical problem 1.53 + * is that databases used in the ContentProvider will be attempted to be 1.54 + * opened twice. 1.55 + */ 1.56 +abstract class ContentProviderTest extends BaseTest { 1.57 + protected ContentProvider mProvider; 1.58 + protected ChangeRecordingMockContentResolver mResolver; 1.59 + protected ArrayList<Runnable> mTests; 1.60 + protected String mDatabaseName; 1.61 + protected String mProviderAuthority; 1.62 + protected IsolatedContext mProviderContext; 1.63 + 1.64 + private class ContentProviderMockContext extends MockContext { 1.65 + @Override 1.66 + public Resources getResources() { 1.67 + // We will fail to find some resources if we don't point 1.68 + // at the original activity. 1.69 + return ((Context)getActivity()).getResources(); 1.70 + } 1.71 + 1.72 + @Override 1.73 + public String getPackageName() { 1.74 + return getInstrumentation().getContext().getPackageName(); 1.75 + } 1.76 + 1.77 + @Override 1.78 + public String getPackageResourcePath() { 1.79 + return getInstrumentation().getContext().getPackageResourcePath(); 1.80 + } 1.81 + 1.82 + @Override 1.83 + public File getDir(String name, int mode) { 1.84 + return getInstrumentation().getContext().getDir(this.getClass().getSimpleName() + "_" + name, mode); 1.85 + } 1.86 + 1.87 + @Override 1.88 + public Context getApplicationContext() { 1.89 + return this; 1.90 + } 1.91 + 1.92 + @Override 1.93 + public SharedPreferences getSharedPreferences(String name, int mode) { 1.94 + return getInstrumentation().getContext().getSharedPreferences(name, mode); 1.95 + } 1.96 + 1.97 + @Override 1.98 + public ApplicationInfo getApplicationInfo() { 1.99 + return getInstrumentation().getContext().getApplicationInfo(); 1.100 + } 1.101 + } 1.102 + 1.103 + protected class DelegatingTestContentProvider extends ContentProvider { 1.104 + ContentProvider mTargetProvider; 1.105 + 1.106 + public DelegatingTestContentProvider(ContentProvider targetProvider) { 1.107 + super(); 1.108 + mTargetProvider = targetProvider; 1.109 + } 1.110 + 1.111 + private Uri appendTestParam(Uri uri) { 1.112 + try { 1.113 + return appendUriParam(uri, BrowserContract.PARAM_IS_TEST, "1"); 1.114 + } catch (Exception e) {} 1.115 + 1.116 + return null; 1.117 + } 1.118 + 1.119 + @Override 1.120 + public boolean onCreate() { 1.121 + return mTargetProvider.onCreate(); 1.122 + } 1.123 + 1.124 + @Override 1.125 + public String getType(Uri uri) { 1.126 + return mTargetProvider.getType(uri); 1.127 + } 1.128 + 1.129 + @Override 1.130 + public int delete(Uri uri, String selection, String[] selectionArgs) { 1.131 + return mTargetProvider.delete(appendTestParam(uri), selection, selectionArgs); 1.132 + } 1.133 + 1.134 + @Override 1.135 + public Uri insert(Uri uri, ContentValues values) { 1.136 + return mTargetProvider.insert(appendTestParam(uri), values); 1.137 + } 1.138 + 1.139 + @Override 1.140 + public int update(Uri uri, ContentValues values, String selection, 1.141 + String[] selectionArgs) { 1.142 + return mTargetProvider.update(appendTestParam(uri), values, 1.143 + selection, selectionArgs); 1.144 + } 1.145 + 1.146 + @Override 1.147 + public Cursor query(Uri uri, String[] projection, String selection, 1.148 + String[] selectionArgs, String sortOrder) { 1.149 + return mTargetProvider.query(appendTestParam(uri), projection, selection, 1.150 + selectionArgs, sortOrder); 1.151 + } 1.152 + 1.153 + @Override 1.154 + public ContentProviderResult[] applyBatch (ArrayList<ContentProviderOperation> operations) 1.155 + throws OperationApplicationException { 1.156 + return mTargetProvider.applyBatch(operations); 1.157 + } 1.158 + 1.159 + @Override 1.160 + public int bulkInsert(Uri uri, ContentValues[] values) { 1.161 + return mTargetProvider.bulkInsert(appendTestParam(uri), values); 1.162 + } 1.163 + 1.164 + public ContentProvider getTargetProvider() { 1.165 + return mTargetProvider; 1.166 + } 1.167 + } 1.168 + 1.169 + /* 1.170 + * A MockContentResolver that records each URI that is supplied to 1.171 + * notifyChange. Warning: the list of changed URIs is not 1.172 + * synchronized. 1.173 + */ 1.174 + protected class ChangeRecordingMockContentResolver extends MockContentResolver { 1.175 + public final LinkedList<Uri> notifyChangeList = new LinkedList<Uri>(); 1.176 + 1.177 + @Override 1.178 + public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { 1.179 + notifyChangeList.addLast(uri); 1.180 + 1.181 + super.notifyChange(uri, observer, syncToNetwork); 1.182 + } 1.183 + } 1.184 + 1.185 + /** 1.186 + * Factory function that makes new ContentProvider instances. 1.187 + * <p> 1.188 + * We want a fresh provider each test, so this should be invoked in 1.189 + * <code>setUp</code> before each individual test. 1.190 + */ 1.191 + protected static Callable<ContentProvider> sBrowserProviderCallable = new Callable<ContentProvider>() { 1.192 + @Override 1.193 + public ContentProvider call() { 1.194 + return new BrowserProvider(); 1.195 + } 1.196 + }; 1.197 + 1.198 + private void setUpContentProvider(ContentProvider targetProvider) throws Exception { 1.199 + mResolver = new ChangeRecordingMockContentResolver(); 1.200 + 1.201 + final String filenamePrefix = this.getClass().getSimpleName() + "."; 1.202 + RenamingDelegatingContext targetContextWrapper = 1.203 + new RenamingDelegatingContext( 1.204 + new ContentProviderMockContext(), 1.205 + (Context)getActivity(), 1.206 + filenamePrefix); 1.207 + 1.208 + mProviderContext = new IsolatedContext(mResolver, targetContextWrapper); 1.209 + 1.210 + targetProvider.attachInfo(mProviderContext, null); 1.211 + 1.212 + mProvider = new DelegatingTestContentProvider(targetProvider); 1.213 + mProvider.attachInfo(mProviderContext, null); 1.214 + 1.215 + mResolver.addProvider(mProviderAuthority, mProvider); 1.216 + } 1.217 + 1.218 + public static Uri appendUriParam(Uri uri, String param, String value) { 1.219 + return uri.buildUpon().appendQueryParameter(param, value).build(); 1.220 + } 1.221 + 1.222 + public void setTestName(String testName) { 1.223 + mAsserter.setTestName(this.getClass().getName() + " - " + testName); 1.224 + } 1.225 + 1.226 + @Override 1.227 + public void setUp() throws Exception { 1.228 + throw new UnsupportedOperationException("You should call setUp(authority, databaseName) instead"); 1.229 + } 1.230 + 1.231 + public void setUp(Callable<ContentProvider> contentProviderFactory, String authority, String databaseName) throws Exception { 1.232 + super.setUp(); 1.233 + 1.234 + mTests = new ArrayList<Runnable>(); 1.235 + mDatabaseName = databaseName; 1.236 + 1.237 + mProviderAuthority = authority; 1.238 + 1.239 + setUpContentProvider(contentProviderFactory.call()); 1.240 + } 1.241 + 1.242 + @Override 1.243 + public void tearDown() throws Exception { 1.244 + if (Build.VERSION.SDK_INT >= 11) { 1.245 + mProvider.shutdown(); 1.246 + } 1.247 + 1.248 + if (mDatabaseName != null) { 1.249 + mProviderContext.deleteDatabase(mDatabaseName); 1.250 + } 1.251 + 1.252 + super.tearDown(); 1.253 + } 1.254 + 1.255 + public AssetManager getAssetManager() { 1.256 + return getInstrumentation().getContext().getAssets(); 1.257 + } 1.258 +}