Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(376)

Side by Side Diff: chrome/android/javatests/src/org/chromium/chrome/browser/bookmarkimport/AndroidBrowserImporterTest.java

Issue 1141283003: Upstream oodles of Chrome for Android code into Chromium. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: final patch? Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.bookmarkimport;
6
7 import android.content.ContentValues;
8 import android.content.Context;
9 import android.database.Cursor;
10 import android.database.MatrixCursor;
11 import android.net.Uri;
12 import android.provider.Browser.BookmarkColumns;
13 import android.test.mock.MockContentProvider;
14 import android.test.mock.MockContentResolver;
15 import android.test.suitebuilder.annotation.MediumTest;
16 import android.util.Log;
17
18 import org.chromium.base.test.util.Feature;
19 import org.chromium.chrome.browser.ChromeBrowserProvider;
20 import org.chromium.chrome.test.util.ApplicationData;
21 import org.chromium.chrome.test.util.BookmarkTestUtils;
22 import org.chromium.content.browser.test.NativeLibraryTestBase;
23 import org.chromium.content.browser.test.util.CallbackHelper;
24
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.concurrent.TimeoutException;
28
29 /**
30 * Tests importing bookmarks from Android Browser.
31 */
32 public class AndroidBrowserImporterTest extends NativeLibraryTestBase {
33 private static final String TAG = "BookmarkImporterTest";
34
35 private static final String PUBLIC_PROVIDER = "browser";
36
37 private static final String IS_BOOKMARK = "bookmark=1";
38 private static final String HAS_URL = "url=?";
39 private static final String BOOKMARK_MATCHES = IS_BOOKMARK + " AND "
40 + BookmarkColumns.URL + "=? AND " + BookmarkColumns.TITLE + "=?";
41
42 private static final String[] COLUMN_NAMES = new String[] {
43 BookmarkColumns.BOOKMARK,
44 BookmarkColumns.URL,
45 BookmarkColumns.TITLE,
46 BookmarkColumns.DATE,
47 BookmarkColumns.CREATED,
48 BookmarkColumns.VISITS,
49 BookmarkColumns.FAVICON
50 };
51
52 // Bookmark source to mock.
53 private enum BookmarksSource {
54 NONE,
55 PUBLIC_API
56 }
57
58 // Data of the mock bookmarks used for testing.
59 private static class MockBookmark {
60 public boolean isFolder;
61 public String url;
62 public String title;
63 public Long lastVisit;
64 public Long created;
65 public Long visits;
66 public byte[] favicon;
67
68 public MockBookmark() {}
69
70 public MockBookmark(boolean isFolder, String url, String title, Long las tVisit,
71 Long created, Long visits, byte[] favicon) {
72 this.isFolder = isFolder;
73 this.url = url;
74 this.title = title;
75 this.lastVisit = lastVisit;
76 this.created = created;
77 this.visits = visits;
78 this.favicon = favicon;
79 }
80 }
81
82 private boolean mImportSucceeded;
83
84 @Override
85 public void tearDown() throws Exception {
86 super.tearDown();
87 ApplicationData.clearAppData(getInstrumentation().getTargetContext());
88 }
89
90 @Override
91 public void setUp() {
92 loadNativeLibraryAndInitBrowserProcess();
93 }
94
95 private AndroidBrowserImporter getBookmarkImporter(MockBookmark[] mockBookma rks,
96 BookmarksSource source) {
97 AndroidBrowserImporter importer =
98 new AndroidBrowserImporter(getInstrumentation().getTargetContext ());
99 importer.setInputResolver(createMockResolver(mockBookmarks, source));
100 if (source != BookmarksSource.NONE) {
101 importer.setIgnoreAvailableProvidersForTestPurposes(true);
102 }
103 return importer;
104 }
105
106 private boolean importNewBookmarks(MockBookmark[] mockBookmarks, BookmarksSo urce source)
107 throws InterruptedException {
108 BookmarkImporter importer = getBookmarkImporter(mockBookmarks, source);
109
110 // Note: the UI doesn't require any new bookmarks to consider the import as successful.
111 // For testing purposes we do.
112 final CallbackHelper importFinishedEvent = new CallbackHelper();
113 importer.importBookmarks(new BookmarkImporter.OnBookmarksImportedListene r() {
114 @Override
115 public void onBookmarksImported(BookmarkImporter.ImportResults resul ts) {
116 mImportSucceeded = results != null && results.newBookmarks > 0
117 && results.numImported == results.newBookmarks;
118 importFinishedEvent.notifyCalled();
119 }
120 });
121 try {
122 importFinishedEvent.waitForCallback(0);
123 } catch (TimeoutException e) {
124 fail("Never received import finished event");
125 }
126 return mImportSucceeded;
127 }
128
129 private int countImportedNonFolderBookmarks() {
130 Cursor cursor = getInstrumentation().getTargetContext().getContentResolv er().query(
131 ChromeBrowserProvider.getBookmarksApiUri(getInstrumentation().ge tTargetContext()),
132 null, IS_BOOKMARK, null, null);
133 int count = cursor.getCount();
134 cursor.close();
135 return count;
136 }
137
138 // TODO(leandrogracia): add support for hierarchical matching. This is likel y to require changes
139 // in ChromeBrowserProvider that should be addressed by a later patch.
140 private boolean matchFlattenedImportedBookmarks(MockBookmark[] mockBookmarks ) {
141 Cursor cursor = getInstrumentation().getTargetContext().getContentResolv er().query(
142 ChromeBrowserProvider.getBookmarksApiUri(getInstrumentation().ge tTargetContext()),
143 null, IS_BOOKMARK, null, null);
144 if (mockBookmarks == null && cursor == null) return true;
145
146 // Read all the imports to avoid problems with their order.
147 HashMap<String, MockBookmark> importedBookmarks = new HashMap<String, Mo ckBookmark>();
148 assertTrue(cursor != null);
149 while (cursor.moveToNext()) {
150 MockBookmark bookmark = new MockBookmark();
151 int index = cursor.getColumnIndex(BookmarkColumns.URL);
152 if (index != -1 && !cursor.isNull(index)) bookmark.url = cursor.getS tring(index);
153
154 index = cursor.getColumnIndex(BookmarkColumns.TITLE);
155 if (index != -1 && !cursor.isNull(index)) bookmark.title = cursor.ge tString(index);
156
157 index = cursor.getColumnIndex(BookmarkColumns.CREATED);
158 if (index != -1 && !cursor.isNull(index)) bookmark.created = cursor. getLong(index);
159
160 index = cursor.getColumnIndex(BookmarkColumns.DATE);
161 if (index != -1 && !cursor.isNull(index)) bookmark.lastVisit = curso r.getLong(index);
162
163 index = cursor.getColumnIndex(BookmarkColumns.VISITS);
164 if (index != -1 && !cursor.isNull(index)) bookmark.visits = cursor.g etLong(index);
165
166 index = cursor.getColumnIndex(BookmarkColumns.FAVICON);
167 if (index != -1 && !cursor.isNull(index)) bookmark.favicon = cursor. getBlob(index);
168
169 // URLs should be unique in our model.
170 assertFalse(importedBookmarks.containsKey(bookmark.url));
171 importedBookmarks.put(bookmark.url, bookmark);
172 }
173 cursor.close();
174
175 // Auxiliary class to get bookmark mismatch errors.
176 class BookmarkMatchingException extends Exception {
177 public BookmarkMatchingException(String message) {
178 super(message);
179 }
180 }
181
182 HashSet<String> matchingBookmarks = new HashSet<String>();
183 final long now = System.currentTimeMillis();
184 try {
185 for (MockBookmark expected : mockBookmarks) {
186 // Folders are not returned by our provider (not part of the pub lic API yet).
187 if (expected.isFolder) continue;
188
189 // Matching can't be performed if the expectation doesn't have a URL.
190 if (expected.url == null) {
191 throw new BookmarkMatchingException("Expected bookmark witho ut a URL found.");
192 }
193
194 // Get the corresponding imported bookmark by its URL. If multip le entries in the
195 // mock bookmarks with the same URL are found, all but the first will be skipped.
196 if (matchingBookmarks.contains(expected.url)) continue;
197
198 MockBookmark imported = importedBookmarks.get(expected.url);
199 if (imported == null) {
200 throw new BookmarkMatchingException("Bookmark with URL " + e xpected.url
201 + " not found");
202 }
203
204 // Imported data must have a valid title despite what the expect ation says.
205 if (imported.title == null) {
206 throw new BookmarkMatchingException("Bookmark without title found: "
207 + imported.url);
208 } else if (!imported.title.equals(expected.title)) {
209 throw new BookmarkMatchingException("Title doesn't match: " + expected.title
210 + " != " + imported.title);
211 }
212
213 // If not explicitely set in the expectations, the creation date should be available
214 // after importing with a value not in the future.
215 if (expected.created != null) {
216 if (!expected.created.equals(imported.created)) {
217 throw new BookmarkMatchingException("Creation timestamp doesn't match: "
218 + expected.created.toString() + " != "
219 + (imported.created != null ? imported.created.t oString()
220 : "null"));
221 }
222 } else {
223 if (imported.created == null) {
224 throw new BookmarkMatchingException("Creation date not i nitialised.");
225 } else if (imported.created.longValue() > now) {
226 throw new BookmarkMatchingException("Creation set in the future");
227 }
228 }
229
230 // If not explicitely set in the expectations, the last visit sh ould be available
231 // after importing with a value not in the future.
232 if (expected.lastVisit != null) {
233 if (!expected.lastVisit.equals(imported.lastVisit)) {
234 throw new BookmarkMatchingException("Last visit timestam p doesn't match: "
235 + expected.lastVisit.toString() + " != "
236 + (imported.lastVisit != null ? imported.lastVis it.toString()
237 : "null"));
238 }
239 } else {
240 if (imported.lastVisit == null) {
241 throw new BookmarkMatchingException("Last visit date not initialised.");
242 } else if (imported.lastVisit.longValue() > now) {
243 throw new BookmarkMatchingException("Last visit set in t he future");
244 }
245 }
246
247 // Visits should be 1 if not explicitly set in the mock bookmark .
248 if (expected.visits != null) {
249 if (!expected.visits.equals(imported.visits)) {
250 throw new BookmarkMatchingException("Number of visits do esn't match: "
251 + expected.visits.toString() + " != "
252 + (imported.visits != null ? imported.visits.toS tring() : "null"));
253 }
254 } else {
255 if (imported.visits == null) {
256 throw new BookmarkMatchingException("Visit count not ini tialised.");
257 } else if (imported.visits.longValue() != 0) {
258 throw new BookmarkMatchingException("Invalid visit count initialization");
259 }
260 }
261
262 // If set, the favicons should be binary equals.
263 if (!BookmarkTestUtils.byteArrayEqual(expected.favicon, imported .favicon)) {
264 throw new BookmarkMatchingException("Favicon doesn't match") ;
265 }
266
267 matchingBookmarks.add(expected.url);
268 }
269
270 // At the end the map and the set should have the same size.
271 if (importedBookmarks.size() != matchingBookmarks.size()) {
272 throw new BookmarkMatchingException(
273 "Not match could be found for all imported bookmarks: "
274 + matchingBookmarks.size() + " matched, should be "
275 + importedBookmarks.size());
276 }
277 } catch (BookmarkMatchingException e) {
278 Log.w(TAG, e.getMessage());
279 return false;
280 }
281
282 return true;
283 }
284
285 private void internalTestBookmarkFields(BookmarksSource source) throws Inter ruptedException {
286 // Load a favicon for testing.
287 byte[] favicon = BookmarkTestUtils.getIcon("chrome/test/data/android/fav icon.png");
288 assertTrue(favicon != null);
289
290 // Create mock bookmark data to import. Test all non-hierarchy fields.
291 final long now = System.currentTimeMillis();
292 MockBookmark[] mockBookmarks = new MockBookmark[] {
293 new MockBookmark(false, "http://www.google.com/", "Google", Long.val ueOf(now), Long.valueOf(now - 100 * 60 * 60),
294 Long.valueOf(100), favicon),
295 new MockBookmark(false, "http://maps.google.com/", "Google Maps", Lo ng.valueOf(now - 10 * 60), Long.valueOf(now - 5 * 60 * 60),
296 Long.valueOf(20), null),
297 new MockBookmark(false, "http://mail.google.com/", "Google Mail", nu ll, null,
298 null, null)
299 };
300
301 // Import bookmarks and match the contents.
302 assertTrue(importNewBookmarks(mockBookmarks, source));
303 assertTrue(matchFlattenedImportedBookmarks(mockBookmarks));
304 }
305
306 private void internalTestIncompleteBookmarks(BookmarksSource source)
307 throws InterruptedException {
308 // Create incomplete mock bookmark data.
309 MockBookmark[] mockBookmarks = new MockBookmark[] {
310 new MockBookmark(false, "http://www.google.com/", null, null, null, null, null),
311 new MockBookmark(false, null, "Google Maps", null, null, null, null)
312 };
313
314 // Should fail importing bookmarks.
315 assertFalse(importNewBookmarks(mockBookmarks, source));
316 }
317
318 private void internalTestImportExisting(BookmarksSource source) throws Inter ruptedException {
319 // One simple mock bookmark.
320 MockBookmark[] mockBookmarks = new MockBookmark[] {
321 new MockBookmark(false, "http://mail.google.com/", "Google Mail", nu ll, null, null,
322 null)
323 };
324
325 // Import bookmarks. There should only be one imported.
326 assertTrue(importNewBookmarks(mockBookmarks, source));
327 assertEquals(1, countImportedNonFolderBookmarks());
328
329 // Import the same bookmarks again. It should fail becase there aren't a ny new bookmarks.
330 assertFalse(importNewBookmarks(mockBookmarks, source));
331 assertEquals(1, countImportedNonFolderBookmarks());
332
333 // Let's try adding something else.
334 MockBookmark[] mockBookmarksExtra = new MockBookmark[] {
335 new MockBookmark(false, "http://mail.google.com/", "Google Mail", nu ll, null, null,
336 null),
337 new MockBookmark(false, "http://maps.google.com/", "Google Maps", nu ll, null, null,
338 null)
339 };
340
341 // The new bookmark should have been added.
342 assertTrue(importNewBookmarks(mockBookmarksExtra, source));
343 assertEquals(2, countImportedNonFolderBookmarks());
344 }
345
346 private void internalTestNoBookmarks(BookmarksSource source) throws Interrup tedException {
347 MockBookmark[] mockBookmarks = new MockBookmark[] {};
348 assertFalse(importNewBookmarks(mockBookmarks, source));
349 }
350
351 @MediumTest
352 @Feature({"Bookmarks", "Import"})
353 public void testBookmarkFieldsFromPublicAPI() throws InterruptedException {
354 internalTestBookmarkFields(BookmarksSource.PUBLIC_API);
355 }
356
357 @MediumTest
358 @Feature({"Bookmarks", "Import"})
359 public void testIncompleteBookmarksFromPublicAPI() throws InterruptedExcepti on {
360 internalTestIncompleteBookmarks(BookmarksSource.PUBLIC_API);
361 }
362
363 @MediumTest
364 @Feature({"Bookmarks", "Import"})
365 public void testImportExistingFromPublicAPI() throws InterruptedException {
366 internalTestImportExisting(BookmarksSource.PUBLIC_API);
367 }
368
369 @MediumTest
370 @Feature({"Bookmarks", "Import"})
371 public void testNoBookmarksInPublicAPI() throws InterruptedException {
372 internalTestNoBookmarks(BookmarksSource.PUBLIC_API);
373 }
374
375 @MediumTest
376 @Feature({"Bookmarks", "Import"})
377 public void testBookmarksAreAccessible() {
378 MockBookmark[] mockBookmarks = new MockBookmark[] {
379 new MockBookmark(false, "http://www.google.com/", "Google", null, nu ll, null, null),
380 };
381
382 // Should not be accessible if no provider is reachable.
383 assertFalse(getBookmarkImporter(mockBookmarks, BookmarksSource.NONE)
384 .areBookmarksAccessible());
385
386 // Should be accessible in all other cases.
387 assertTrue(getBookmarkImporter(mockBookmarks, BookmarksSource.PUBLIC_API )
388 .areBookmarksAccessible());
389 }
390
391 @MediumTest
392 @Feature({"Bookmarks", "Import"})
393 public void testTimestampCorrection() throws InterruptedException {
394 // Set the past and the future 1 hour away from now.
395 final long lapse = 3600000;
396 final Long now = Long.valueOf(System.currentTimeMillis());
397 final Long past = Long.valueOf(Math.max(now.longValue() - lapse, 0));
398 final Long future = Long.valueOf(now.longValue() + lapse);
399
400 // Setting a maximum of 10 visits. Each visit means a database row
401 // insertion in the provider, so very high values will make the test
402 // fail because of the importing event timeout.
403 final long maxVisits = 10;
404 final Long pastForVisits = Long.valueOf(now.longValue() - maxVisits);
405 final Long visitsValue = Long.valueOf(maxVisits * 100);
406
407 MockBookmark[] mockBookmarks = new MockBookmark[] {
408 // Last visit is in the future.
409 new MockBookmark(false, "http://www.someweb1.com/", "Foo", future, n ull,
410 null, null),
411 // Creation date is in the future.
412 new MockBookmark(false, "http://www.someweb2.com/", "Foo", null, fut ure,
413 null, null),
414 // Creation date after last visit, both valid.
415 new MockBookmark(false, "http://www.someweb3.com/", "Foo", past, now ,
416 null, null),
417 // Invalid number of visits for the lapse between creation and last visit.
418 new MockBookmark(false, "http://www.someweb4.com/", "Foo", now, past ForVisits,
419 visitsValue, null),
420 };
421
422 // Timestamp correction should be independent to the source.
423 assertTrue(importNewBookmarks(mockBookmarks, BookmarksSource.PUBLIC_API) );
424 assertEquals(mockBookmarks.length, countImportedNonFolderBookmarks());
425 assertFalse(matchFlattenedImportedBookmarks(mockBookmarks));
426 }
427
428 @MediumTest
429 @Feature({"Bookmarks", "Import"})
430 public void testVisitCountZeroAdjustment() throws InterruptedException {
431 final Long now = Long.valueOf(System.currentTimeMillis());
432
433 MockBookmark[] mockBookmarks = new MockBookmark[] {
434 // Visit count is set to zero (required to be supported by the Brows erTest CTS).
435 new MockBookmark(false, "http://www.zerovisits.com/", "Foo", now, no w,
436 0L, null),
437 };
438
439 assertTrue(importNewBookmarks(mockBookmarks, BookmarksSource.PUBLIC_API) );
440 assertEquals(mockBookmarks.length, countImportedNonFolderBookmarks());
441 // The imported bookmark should differ from mockBookmarks as visit count was ignored.
442 assertFalse(matchFlattenedImportedBookmarks(mockBookmarks));
443 }
444
445 @MediumTest
446 @Feature({"Bookmarks", "Import"})
447 public void testVisitCountOneAdjustment() throws InterruptedException {
448 final Long now = Long.valueOf(System.currentTimeMillis());
449
450 MockBookmark[] mockBookmarks = new MockBookmark[] {
451 // Visit count is set to one (will be ignored).
452 // See http://crbug.com/149376 for more details.
453 new MockBookmark(false, "http://www.onevisit.com/", "Foo", now, now,
454 1L, null),
455 };
456
457 assertTrue(importNewBookmarks(mockBookmarks, BookmarksSource.PUBLIC_API) );
458 assertEquals(mockBookmarks.length, countImportedNonFolderBookmarks());
459 assertTrue(matchFlattenedImportedBookmarks(mockBookmarks));
460 }
461
462 @MediumTest
463 @Feature({"Bookmarks", "Import"})
464 public void testVisitCountTwoAdjustment() throws InterruptedException {
465 final Long now = Long.valueOf(System.currentTimeMillis());
466
467 MockBookmark[] mockBookmarks = new MockBookmark[] {
468 // Visit count is set to one (will be ignored).
469 // See http://crbug.com/149376 for more details.
470 new MockBookmark(false, "http://www.onevisit.com/", "Foo", now + 100 , now,
471 1L, null),
472 };
473
474 assertTrue(importNewBookmarks(mockBookmarks, BookmarksSource.PUBLIC_API) );
475 assertEquals(mockBookmarks.length, countImportedNonFolderBookmarks());
476 // The imported bookmark should differ from mockBookmarks as visit count was ignored.
477 assertFalse(matchFlattenedImportedBookmarks(mockBookmarks));
478 }
479
480 private static class ImportMockContentProvider extends MockContentProvider {
481 private final MockBookmark[] mMockData;
482 private final boolean mFlatten;
483
484 ImportMockContentProvider(Context context, MockBookmark[] data, boolean flatten) {
485 super(context);
486 mMockData = data;
487 mFlatten = flatten;
488 }
489
490 @Override
491 public Cursor query(Uri uri, String[] projection, String selection,
492 String[] selectionArgs, String sortOrder) {
493 // Provide a new cursor with the mock data.
494 MatrixCursor cursor = new MatrixCursor(COLUMN_NAMES);
495 for (MockBookmark bookmark : mMockData) {
496 if (mFlatten && bookmark.isFolder) continue;
497
498 // There are 3 kinds of queries performed by the iterators:
499 // 1. Retrieving all bookmarks.
500 // 2. Querying a specific URL in history.
501 // 3. Looking for matching bookmarks (url and title) in the publ ic API.
502 if (selection.equals(HAS_URL)) {
503 assertTrue(selectionArgs != null);
504 assertEquals(1, selectionArgs.length);
505 if (bookmark.url != null && !bookmark.url.equals(selectionAr gs[0])) {
506 continue;
507 }
508 } else if (selection.equals(BOOKMARK_MATCHES)) {
509 assertTrue(selectionArgs != null);
510 assertEquals(2, selectionArgs.length);
511 if ((bookmark.url != null && !bookmark.url.equals(selectionA rgs[0]))
512 || (bookmark.title != null
513 && !bookmark.title.equals(selectionArgs[1])) ) {
514 continue;
515 }
516 }
517
518 cursor.addRow(new Object[] {
519 1, bookmark.url, bookmark.title, bookmark.lastVisit,
520 bookmark.created, bookmark.visits, bookmark.favicon
521 });
522
523 // Should there be only one history URL result. No need to look for more.
524 if (selection.equals(HAS_URL)) break;
525 }
526 cursor.move(0);
527 return cursor;
528 }
529
530 @Override
531 public Uri insert(Uri uri, ContentValues values) {
532 // Proxy to the original content resolver.
533 return getContext().getContentResolver().insert(uri, values);
534 }
535 }
536
537 private MockContentResolver createMockResolver(MockBookmark[] mockBookmarks,
538 BookmarksSource source) {
539 MockContentResolver mockResolver = new MockContentResolver();
540 switch (source) {
541 case NONE:
542 // No provider added. Used to test bookmark availability.
543 break;
544
545 case PUBLIC_API:
546 mockResolver.addProvider(PUBLIC_PROVIDER,
547 new ImportMockContentProvider(getInstrumentation().getTa rgetContext(),
548 mockBookmarks, true));
549 break;
550 }
551 return mockResolver;
552 }
553 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698