mobile/android/base/tests/testBrowserProviderPerf.java

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:2041e3e0f04c
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/. */
4
5 package org.mozilla.gecko.tests;
6
7 import java.io.File;
8 import java.util.Random;
9 import java.util.UUID;
10
11 import org.mozilla.gecko.db.BrowserContract;
12 import org.mozilla.gecko.db.BrowserProvider;
13 import org.mozilla.gecko.db.LocalBrowserDB;
14 import org.mozilla.gecko.util.FileUtils;
15
16 import android.content.ContentProvider;
17 import android.content.ContentProviderClient;
18 import android.content.ContentResolver;
19 import android.content.ContentValues;
20 import android.database.Cursor;
21 import android.database.sqlite.SQLiteDatabase;
22 import android.net.Uri;
23 import android.os.SystemClock;
24 import android.util.Log;
25
26 /**
27 * This test is meant to exercise the performance of Fennec's history and
28 * bookmarks content provider.
29 *
30 * It does not extend ContentProviderTest because that class is unable to
31 * accurately assess the performance of the ContentProvider -- it's a second
32 * instance of a class that's only supposed to exist once, wrapped in a bunch of
33 * junk.
34 *
35 * Instead, we directly use the existing ContentProvider, accessing a new
36 * profile directory that we initialize via BrowserDB.
37 */
38 public class testBrowserProviderPerf extends BaseRobocopTest {
39 private final int NUMBER_OF_BASIC_HISTORY_URLS = 10000;
40 private final int NUMBER_OF_BASIC_BOOKMARK_URLS = 500;
41 private final int NUMBER_OF_COMBINED_URLS = 500;
42
43 private final int NUMBER_OF_KNOWN_URLS = 200;
44 private final int BATCH_SIZE = 500;
45
46 // Include spaces in prefix to test performance querying with
47 // multiple constraint words.
48 private final String KNOWN_PREFIX = "my mozilla test ";
49
50 private Random mGenerator;
51
52 private final String MOBILE_FOLDER_GUID = "mobile";
53 private long mMobileFolderId;
54 private ContentResolver mResolver;
55 private String mProfile;
56 private Uri mHistoryURI;
57 private Uri mBookmarksURI;
58 private Uri mFaviconsURI;
59
60 @Override
61 protected Type getTestType() {
62 return Type.TALOS;
63 }
64
65 private void loadMobileFolderId() throws Exception {
66 Cursor c = mResolver.query(mBookmarksURI, null,
67 BrowserContract.Bookmarks.GUID + " = ?",
68 new String[] { MOBILE_FOLDER_GUID },
69 null);
70 c.moveToFirst();
71 mMobileFolderId = c.getLong(c.getColumnIndex(BrowserContract.Bookmarks._ID));
72
73 c.close();
74 }
75
76 private ContentValues createBookmarkEntry(String title, String url, long parentId,
77 int type, int position, String tags, String description, String keyword) throws Exception {
78 ContentValues bookmark = new ContentValues();
79
80 bookmark.put(BrowserContract.Bookmarks.TITLE, title);
81 bookmark.put(BrowserContract.Bookmarks.URL, url);
82 bookmark.put(BrowserContract.Bookmarks.PARENT, parentId);
83 bookmark.put(BrowserContract.Bookmarks.TYPE, type);
84 bookmark.put(BrowserContract.Bookmarks.POSITION, position);
85 bookmark.put(BrowserContract.Bookmarks.TAGS, tags);
86 bookmark.put(BrowserContract.Bookmarks.DESCRIPTION, description);
87 bookmark.put(BrowserContract.Bookmarks.KEYWORD, keyword);
88
89 return bookmark;
90 }
91
92 private ContentValues createBookmarkEntryWithUrl(String url) throws Exception {
93 return createBookmarkEntry(url, url, mMobileFolderId,
94 BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
95 }
96
97 private ContentValues createRandomBookmarkEntry() throws Exception {
98 return createRandomBookmarkEntry("");
99 }
100
101 private ContentValues createRandomBookmarkEntry(String knownPrefix) throws Exception {
102 String randomStr = createRandomUrl(knownPrefix);
103 return createBookmarkEntryWithUrl(randomStr);
104 }
105
106 private ContentValues createHistoryEntry(String title, String url, int visits,
107 long lastVisited) throws Exception {
108 ContentValues historyEntry = new ContentValues();
109
110 historyEntry.put(BrowserContract.History.TITLE, title);
111 historyEntry.put(BrowserContract.History.URL, url);
112 historyEntry.put(BrowserContract.History.VISITS, visits);
113 historyEntry.put(BrowserContract.History.DATE_LAST_VISITED, lastVisited);
114
115 return historyEntry;
116 }
117
118 private ContentValues createHistoryEntryWithUrl(String url) throws Exception {
119 int visits = mGenerator.nextInt(500);
120 return createHistoryEntry(url, url, visits,
121 System.currentTimeMillis());
122 }
123
124 private ContentValues createRandomHistoryEntry() throws Exception {
125 return createRandomHistoryEntry("");
126 }
127
128 private ContentValues createRandomHistoryEntry(String knownPrefix) throws Exception {
129 String randomStr = createRandomUrl(knownPrefix);
130 return createHistoryEntryWithUrl(randomStr);
131 }
132
133 private ContentValues createFaviconEntryWithUrl(String url) throws Exception {
134 ContentValues faviconEntry = new ContentValues();
135
136 faviconEntry.put(BrowserContract.Favicons.URL, url + "/favicon.ico");
137 faviconEntry.put(BrowserContract.Favicons.PAGE_URL, url);
138 faviconEntry.put(BrowserContract.Favicons.DATA, url.getBytes("UTF8"));
139
140 return faviconEntry;
141 }
142
143 private String createRandomUrl(String knownPrefix) throws Exception {
144 return knownPrefix + UUID.randomUUID().toString();
145 }
146
147 private void addTonsOfUrls() throws Exception {
148 // Create some random bookmark entries.
149 ContentValues[] bookmarkEntries = new ContentValues[BATCH_SIZE];
150
151 for (int i = 0; i < NUMBER_OF_BASIC_BOOKMARK_URLS / BATCH_SIZE; i++) {
152 bookmarkEntries = new ContentValues[BATCH_SIZE];
153
154 for (int j = 0; j < BATCH_SIZE; j++) {
155 bookmarkEntries[j] = createRandomBookmarkEntry();
156 }
157
158 mResolver.bulkInsert(mBookmarksURI, bookmarkEntries);
159 }
160
161 // Create some random history entries.
162 ContentValues[] historyEntries = new ContentValues[BATCH_SIZE];
163 ContentValues[] faviconEntries = new ContentValues[BATCH_SIZE];
164
165 for (int i = 0; i < NUMBER_OF_BASIC_HISTORY_URLS / BATCH_SIZE; i++) {
166 historyEntries = new ContentValues[BATCH_SIZE];
167 faviconEntries = new ContentValues[BATCH_SIZE];
168
169 for (int j = 0; j < BATCH_SIZE; j++) {
170 historyEntries[j] = createRandomHistoryEntry();
171 faviconEntries[j] = createFaviconEntryWithUrl(historyEntries[j].getAsString(BrowserContract.History.URL));
172 }
173
174 mResolver.bulkInsert(mHistoryURI, historyEntries);
175 mResolver.bulkInsert(mFaviconsURI, faviconEntries);
176 }
177
178
179 // Create random bookmark/history entries with the same URL.
180 for (int i = 0; i < NUMBER_OF_COMBINED_URLS / BATCH_SIZE; i++) {
181 bookmarkEntries = new ContentValues[BATCH_SIZE];
182 historyEntries = new ContentValues[BATCH_SIZE];
183
184 for (int j = 0; j < BATCH_SIZE; j++) {
185 String url = createRandomUrl("");
186 bookmarkEntries[j] = createBookmarkEntryWithUrl(url);
187 historyEntries[j] = createHistoryEntryWithUrl(url);
188 faviconEntries[j] = createFaviconEntryWithUrl(url);
189 }
190
191 mResolver.bulkInsert(mBookmarksURI, bookmarkEntries);
192 mResolver.bulkInsert(mHistoryURI, historyEntries);
193 mResolver.bulkInsert(mFaviconsURI, faviconEntries);
194 }
195
196 // Create some history entries with a known prefix.
197 historyEntries = new ContentValues[NUMBER_OF_KNOWN_URLS];
198 faviconEntries = new ContentValues[NUMBER_OF_KNOWN_URLS];
199 for (int i = 0; i < NUMBER_OF_KNOWN_URLS; i++) {
200 historyEntries[i] = createRandomHistoryEntry(KNOWN_PREFIX);
201 faviconEntries[i] = createFaviconEntryWithUrl(historyEntries[i].getAsString(BrowserContract.History.URL));
202 }
203
204 mResolver.bulkInsert(mHistoryURI, historyEntries);
205 mResolver.bulkInsert(mFaviconsURI, faviconEntries);
206 }
207
208 @Override
209 public void setUp() throws Exception {
210 super.setUp();
211
212 mProfile = "prof" + System.currentTimeMillis();
213
214 mHistoryURI = prepUri(BrowserContract.History.CONTENT_URI);
215 mBookmarksURI = prepUri(BrowserContract.Bookmarks.CONTENT_URI);
216 mFaviconsURI = prepUri(BrowserContract.Favicons.CONTENT_URI);
217
218 mResolver = getActivity().getApplicationContext().getContentResolver();
219
220 mGenerator = new Random(19580427);
221 }
222
223 @Override
224 public void tearDown() {
225 final ContentProviderClient client = mResolver.acquireContentProviderClient(mBookmarksURI);
226 try {
227 final ContentProvider cp = client.getLocalContentProvider();
228 final BrowserProvider bp = ((BrowserProvider) cp);
229
230 // This will be the DB we were just testing.
231 final SQLiteDatabase db = bp.getWritableDatabaseForTesting(mBookmarksURI);
232 try {
233 db.close();
234 } catch (Throwable e) {
235 // Nothing we can do.
236 }
237 } finally {
238 try {
239 client.release();
240 } catch (Throwable e) {
241 // Still go ahead and try to delete the profile.
242 }
243
244 try {
245 FileUtils.delTree(new File(mProfile), null, true);
246 } catch (Exception e) {
247 Log.w("GeckoTest", "Unable to delete profile " + mProfile, e);
248 }
249 }
250 }
251
252 public Uri prepUri(Uri uri) {
253 return uri.buildUpon()
254 .appendQueryParameter(BrowserContract.PARAM_PROFILE, mProfile)
255 .appendQueryParameter(BrowserContract.PARAM_IS_SYNC, "1") // So we don't trigger a sync.
256 .build();
257 }
258
259 /**
260 * This method:
261 *
262 * * Adds a bunch of test data via the ContentProvider API.
263 * * Runs a single query against that test data via BrowserDB.
264 * * Reports timing for Talos.
265 */
266 public void testBrowserProviderQueryPerf() throws Exception {
267 // We add at least this many results.
268 final int limit = 100;
269
270 // Make sure we're querying the right profile.
271 final LocalBrowserDB db = new LocalBrowserDB(mProfile);
272
273 final Cursor before = db.filter(mResolver, KNOWN_PREFIX, limit);
274 try {
275 mAsserter.is(before.getCount(), 0, "Starts empty");
276 } finally {
277 before.close();
278 }
279
280 // Add data.
281 loadMobileFolderId();
282 addTonsOfUrls();
283
284 // Wait for a little while after inserting data. We do this because
285 // this test launches about:home, and Top Sites watches for DB changes.
286 // We don't have a good way for it to only watch changes related to
287 // its current profile, nor is it convenient for us to launch a different
288 // activity that doesn't depend on the DB.
289 // We can fix this by:
290 // * Adjusting the provider interface to allow a "don't notify" param.
291 // * Adjusting the interface schema to include the profile in the path,
292 // and only observe the correct path.
293 // * Launching a different activity.
294 Thread.sleep(5000);
295
296 // Time the query.
297 final long start = SystemClock.uptimeMillis();
298 final Cursor c = db.filter(mResolver, KNOWN_PREFIX, limit);
299
300 try {
301 final int count = c.getCount();
302 final long end = SystemClock.uptimeMillis();
303
304 mAsserter.is(count, limit, "Retrieved results");
305 mAsserter.dumpLog("Results: " + count);
306 mAsserter.dumpLog("__start_report" + Long.toString(end - start) + "__end_report");
307 mAsserter.dumpLog("__startTimestamp" + Long.toString(end - start) + "__endTimestamp");
308 } finally {
309 c.close();
310 }
311 }
312 }

mercurial