1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/tests/testBrowserProviderPerf.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,312 @@ 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.Random; 1.12 +import java.util.UUID; 1.13 + 1.14 +import org.mozilla.gecko.db.BrowserContract; 1.15 +import org.mozilla.gecko.db.BrowserProvider; 1.16 +import org.mozilla.gecko.db.LocalBrowserDB; 1.17 +import org.mozilla.gecko.util.FileUtils; 1.18 + 1.19 +import android.content.ContentProvider; 1.20 +import android.content.ContentProviderClient; 1.21 +import android.content.ContentResolver; 1.22 +import android.content.ContentValues; 1.23 +import android.database.Cursor; 1.24 +import android.database.sqlite.SQLiteDatabase; 1.25 +import android.net.Uri; 1.26 +import android.os.SystemClock; 1.27 +import android.util.Log; 1.28 + 1.29 +/** 1.30 + * This test is meant to exercise the performance of Fennec's history and 1.31 + * bookmarks content provider. 1.32 + * 1.33 + * It does not extend ContentProviderTest because that class is unable to 1.34 + * accurately assess the performance of the ContentProvider -- it's a second 1.35 + * instance of a class that's only supposed to exist once, wrapped in a bunch of 1.36 + * junk. 1.37 + * 1.38 + * Instead, we directly use the existing ContentProvider, accessing a new 1.39 + * profile directory that we initialize via BrowserDB. 1.40 + */ 1.41 +public class testBrowserProviderPerf extends BaseRobocopTest { 1.42 + private final int NUMBER_OF_BASIC_HISTORY_URLS = 10000; 1.43 + private final int NUMBER_OF_BASIC_BOOKMARK_URLS = 500; 1.44 + private final int NUMBER_OF_COMBINED_URLS = 500; 1.45 + 1.46 + private final int NUMBER_OF_KNOWN_URLS = 200; 1.47 + private final int BATCH_SIZE = 500; 1.48 + 1.49 + // Include spaces in prefix to test performance querying with 1.50 + // multiple constraint words. 1.51 + private final String KNOWN_PREFIX = "my mozilla test "; 1.52 + 1.53 + private Random mGenerator; 1.54 + 1.55 + private final String MOBILE_FOLDER_GUID = "mobile"; 1.56 + private long mMobileFolderId; 1.57 + private ContentResolver mResolver; 1.58 + private String mProfile; 1.59 + private Uri mHistoryURI; 1.60 + private Uri mBookmarksURI; 1.61 + private Uri mFaviconsURI; 1.62 + 1.63 + @Override 1.64 + protected Type getTestType() { 1.65 + return Type.TALOS; 1.66 + } 1.67 + 1.68 + private void loadMobileFolderId() throws Exception { 1.69 + Cursor c = mResolver.query(mBookmarksURI, null, 1.70 + BrowserContract.Bookmarks.GUID + " = ?", 1.71 + new String[] { MOBILE_FOLDER_GUID }, 1.72 + null); 1.73 + c.moveToFirst(); 1.74 + mMobileFolderId = c.getLong(c.getColumnIndex(BrowserContract.Bookmarks._ID)); 1.75 + 1.76 + c.close(); 1.77 + } 1.78 + 1.79 + private ContentValues createBookmarkEntry(String title, String url, long parentId, 1.80 + int type, int position, String tags, String description, String keyword) throws Exception { 1.81 + ContentValues bookmark = new ContentValues(); 1.82 + 1.83 + bookmark.put(BrowserContract.Bookmarks.TITLE, title); 1.84 + bookmark.put(BrowserContract.Bookmarks.URL, url); 1.85 + bookmark.put(BrowserContract.Bookmarks.PARENT, parentId); 1.86 + bookmark.put(BrowserContract.Bookmarks.TYPE, type); 1.87 + bookmark.put(BrowserContract.Bookmarks.POSITION, position); 1.88 + bookmark.put(BrowserContract.Bookmarks.TAGS, tags); 1.89 + bookmark.put(BrowserContract.Bookmarks.DESCRIPTION, description); 1.90 + bookmark.put(BrowserContract.Bookmarks.KEYWORD, keyword); 1.91 + 1.92 + return bookmark; 1.93 + } 1.94 + 1.95 + private ContentValues createBookmarkEntryWithUrl(String url) throws Exception { 1.96 + return createBookmarkEntry(url, url, mMobileFolderId, 1.97 + BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword"); 1.98 + } 1.99 + 1.100 + private ContentValues createRandomBookmarkEntry() throws Exception { 1.101 + return createRandomBookmarkEntry(""); 1.102 + } 1.103 + 1.104 + private ContentValues createRandomBookmarkEntry(String knownPrefix) throws Exception { 1.105 + String randomStr = createRandomUrl(knownPrefix); 1.106 + return createBookmarkEntryWithUrl(randomStr); 1.107 + } 1.108 + 1.109 + private ContentValues createHistoryEntry(String title, String url, int visits, 1.110 + long lastVisited) throws Exception { 1.111 + ContentValues historyEntry = new ContentValues(); 1.112 + 1.113 + historyEntry.put(BrowserContract.History.TITLE, title); 1.114 + historyEntry.put(BrowserContract.History.URL, url); 1.115 + historyEntry.put(BrowserContract.History.VISITS, visits); 1.116 + historyEntry.put(BrowserContract.History.DATE_LAST_VISITED, lastVisited); 1.117 + 1.118 + return historyEntry; 1.119 + } 1.120 + 1.121 + private ContentValues createHistoryEntryWithUrl(String url) throws Exception { 1.122 + int visits = mGenerator.nextInt(500); 1.123 + return createHistoryEntry(url, url, visits, 1.124 + System.currentTimeMillis()); 1.125 + } 1.126 + 1.127 + private ContentValues createRandomHistoryEntry() throws Exception { 1.128 + return createRandomHistoryEntry(""); 1.129 + } 1.130 + 1.131 + private ContentValues createRandomHistoryEntry(String knownPrefix) throws Exception { 1.132 + String randomStr = createRandomUrl(knownPrefix); 1.133 + return createHistoryEntryWithUrl(randomStr); 1.134 + } 1.135 + 1.136 + private ContentValues createFaviconEntryWithUrl(String url) throws Exception { 1.137 + ContentValues faviconEntry = new ContentValues(); 1.138 + 1.139 + faviconEntry.put(BrowserContract.Favicons.URL, url + "/favicon.ico"); 1.140 + faviconEntry.put(BrowserContract.Favicons.PAGE_URL, url); 1.141 + faviconEntry.put(BrowserContract.Favicons.DATA, url.getBytes("UTF8")); 1.142 + 1.143 + return faviconEntry; 1.144 + } 1.145 + 1.146 + private String createRandomUrl(String knownPrefix) throws Exception { 1.147 + return knownPrefix + UUID.randomUUID().toString(); 1.148 + } 1.149 + 1.150 + private void addTonsOfUrls() throws Exception { 1.151 + // Create some random bookmark entries. 1.152 + ContentValues[] bookmarkEntries = new ContentValues[BATCH_SIZE]; 1.153 + 1.154 + for (int i = 0; i < NUMBER_OF_BASIC_BOOKMARK_URLS / BATCH_SIZE; i++) { 1.155 + bookmarkEntries = new ContentValues[BATCH_SIZE]; 1.156 + 1.157 + for (int j = 0; j < BATCH_SIZE; j++) { 1.158 + bookmarkEntries[j] = createRandomBookmarkEntry(); 1.159 + } 1.160 + 1.161 + mResolver.bulkInsert(mBookmarksURI, bookmarkEntries); 1.162 + } 1.163 + 1.164 + // Create some random history entries. 1.165 + ContentValues[] historyEntries = new ContentValues[BATCH_SIZE]; 1.166 + ContentValues[] faviconEntries = new ContentValues[BATCH_SIZE]; 1.167 + 1.168 + for (int i = 0; i < NUMBER_OF_BASIC_HISTORY_URLS / BATCH_SIZE; i++) { 1.169 + historyEntries = new ContentValues[BATCH_SIZE]; 1.170 + faviconEntries = new ContentValues[BATCH_SIZE]; 1.171 + 1.172 + for (int j = 0; j < BATCH_SIZE; j++) { 1.173 + historyEntries[j] = createRandomHistoryEntry(); 1.174 + faviconEntries[j] = createFaviconEntryWithUrl(historyEntries[j].getAsString(BrowserContract.History.URL)); 1.175 + } 1.176 + 1.177 + mResolver.bulkInsert(mHistoryURI, historyEntries); 1.178 + mResolver.bulkInsert(mFaviconsURI, faviconEntries); 1.179 + } 1.180 + 1.181 + 1.182 + // Create random bookmark/history entries with the same URL. 1.183 + for (int i = 0; i < NUMBER_OF_COMBINED_URLS / BATCH_SIZE; i++) { 1.184 + bookmarkEntries = new ContentValues[BATCH_SIZE]; 1.185 + historyEntries = new ContentValues[BATCH_SIZE]; 1.186 + 1.187 + for (int j = 0; j < BATCH_SIZE; j++) { 1.188 + String url = createRandomUrl(""); 1.189 + bookmarkEntries[j] = createBookmarkEntryWithUrl(url); 1.190 + historyEntries[j] = createHistoryEntryWithUrl(url); 1.191 + faviconEntries[j] = createFaviconEntryWithUrl(url); 1.192 + } 1.193 + 1.194 + mResolver.bulkInsert(mBookmarksURI, bookmarkEntries); 1.195 + mResolver.bulkInsert(mHistoryURI, historyEntries); 1.196 + mResolver.bulkInsert(mFaviconsURI, faviconEntries); 1.197 + } 1.198 + 1.199 + // Create some history entries with a known prefix. 1.200 + historyEntries = new ContentValues[NUMBER_OF_KNOWN_URLS]; 1.201 + faviconEntries = new ContentValues[NUMBER_OF_KNOWN_URLS]; 1.202 + for (int i = 0; i < NUMBER_OF_KNOWN_URLS; i++) { 1.203 + historyEntries[i] = createRandomHistoryEntry(KNOWN_PREFIX); 1.204 + faviconEntries[i] = createFaviconEntryWithUrl(historyEntries[i].getAsString(BrowserContract.History.URL)); 1.205 + } 1.206 + 1.207 + mResolver.bulkInsert(mHistoryURI, historyEntries); 1.208 + mResolver.bulkInsert(mFaviconsURI, faviconEntries); 1.209 + } 1.210 + 1.211 + @Override 1.212 + public void setUp() throws Exception { 1.213 + super.setUp(); 1.214 + 1.215 + mProfile = "prof" + System.currentTimeMillis(); 1.216 + 1.217 + mHistoryURI = prepUri(BrowserContract.History.CONTENT_URI); 1.218 + mBookmarksURI = prepUri(BrowserContract.Bookmarks.CONTENT_URI); 1.219 + mFaviconsURI = prepUri(BrowserContract.Favicons.CONTENT_URI); 1.220 + 1.221 + mResolver = getActivity().getApplicationContext().getContentResolver(); 1.222 + 1.223 + mGenerator = new Random(19580427); 1.224 + } 1.225 + 1.226 + @Override 1.227 + public void tearDown() { 1.228 + final ContentProviderClient client = mResolver.acquireContentProviderClient(mBookmarksURI); 1.229 + try { 1.230 + final ContentProvider cp = client.getLocalContentProvider(); 1.231 + final BrowserProvider bp = ((BrowserProvider) cp); 1.232 + 1.233 + // This will be the DB we were just testing. 1.234 + final SQLiteDatabase db = bp.getWritableDatabaseForTesting(mBookmarksURI); 1.235 + try { 1.236 + db.close(); 1.237 + } catch (Throwable e) { 1.238 + // Nothing we can do. 1.239 + } 1.240 + } finally { 1.241 + try { 1.242 + client.release(); 1.243 + } catch (Throwable e) { 1.244 + // Still go ahead and try to delete the profile. 1.245 + } 1.246 + 1.247 + try { 1.248 + FileUtils.delTree(new File(mProfile), null, true); 1.249 + } catch (Exception e) { 1.250 + Log.w("GeckoTest", "Unable to delete profile " + mProfile, e); 1.251 + } 1.252 + } 1.253 + } 1.254 + 1.255 + public Uri prepUri(Uri uri) { 1.256 + return uri.buildUpon() 1.257 + .appendQueryParameter(BrowserContract.PARAM_PROFILE, mProfile) 1.258 + .appendQueryParameter(BrowserContract.PARAM_IS_SYNC, "1") // So we don't trigger a sync. 1.259 + .build(); 1.260 + } 1.261 + 1.262 + /** 1.263 + * This method: 1.264 + * 1.265 + * * Adds a bunch of test data via the ContentProvider API. 1.266 + * * Runs a single query against that test data via BrowserDB. 1.267 + * * Reports timing for Talos. 1.268 + */ 1.269 + public void testBrowserProviderQueryPerf() throws Exception { 1.270 + // We add at least this many results. 1.271 + final int limit = 100; 1.272 + 1.273 + // Make sure we're querying the right profile. 1.274 + final LocalBrowserDB db = new LocalBrowserDB(mProfile); 1.275 + 1.276 + final Cursor before = db.filter(mResolver, KNOWN_PREFIX, limit); 1.277 + try { 1.278 + mAsserter.is(before.getCount(), 0, "Starts empty"); 1.279 + } finally { 1.280 + before.close(); 1.281 + } 1.282 + 1.283 + // Add data. 1.284 + loadMobileFolderId(); 1.285 + addTonsOfUrls(); 1.286 + 1.287 + // Wait for a little while after inserting data. We do this because 1.288 + // this test launches about:home, and Top Sites watches for DB changes. 1.289 + // We don't have a good way for it to only watch changes related to 1.290 + // its current profile, nor is it convenient for us to launch a different 1.291 + // activity that doesn't depend on the DB. 1.292 + // We can fix this by: 1.293 + // * Adjusting the provider interface to allow a "don't notify" param. 1.294 + // * Adjusting the interface schema to include the profile in the path, 1.295 + // and only observe the correct path. 1.296 + // * Launching a different activity. 1.297 + Thread.sleep(5000); 1.298 + 1.299 + // Time the query. 1.300 + final long start = SystemClock.uptimeMillis(); 1.301 + final Cursor c = db.filter(mResolver, KNOWN_PREFIX, limit); 1.302 + 1.303 + try { 1.304 + final int count = c.getCount(); 1.305 + final long end = SystemClock.uptimeMillis(); 1.306 + 1.307 + mAsserter.is(count, limit, "Retrieved results"); 1.308 + mAsserter.dumpLog("Results: " + count); 1.309 + mAsserter.dumpLog("__start_report" + Long.toString(end - start) + "__end_report"); 1.310 + mAsserter.dumpLog("__startTimestamp" + Long.toString(end - start) + "__endTimestamp"); 1.311 + } finally { 1.312 + c.close(); 1.313 + } 1.314 + } 1.315 +}