mobile/android/base/tests/testBrowserProviderPerf.java

changeset 0
6474c204b198
     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 +}

mercurial