/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.tests; import java.util.ArrayList; import java.util.Random; import org.mozilla.gecko.background.db.CursorDumper; import org.mozilla.gecko.db.BrowserContract; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentUris; import android.content.ContentValues; import android.content.OperationApplicationException; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.util.Log; /* * This test is meant to exercise all operations exposed by Fennec's * history and bookmarks content provider. It does so in an isolated * environment (see ContentProviderTest) without affecting any UI-related * code. */ public class testBrowserProvider extends ContentProviderTest { private long mMobileFolderId; private void loadMobileFolderId() throws Exception { Cursor c = null; try { c = getBookmarkByGuid(BrowserContract.Bookmarks.MOBILE_FOLDER_GUID); mAsserter.is(c.moveToFirst(), true, "Mobile bookmarks folder is present"); mMobileFolderId = c.getLong(c.getColumnIndex(BrowserContract.Bookmarks._ID)); } finally { if (c != null) { c.close(); } } } private void ensureEmptyDatabase() throws Exception { Cursor c; String guid = BrowserContract.Bookmarks.GUID; mProvider.delete(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), guid + " != ? AND " + guid + " != ? AND " + guid + " != ? AND " + guid + " != ? AND " + guid + " != ? AND " + guid + " != ?", new String[] { BrowserContract.Bookmarks.PLACES_FOLDER_GUID, BrowserContract.Bookmarks.MOBILE_FOLDER_GUID, BrowserContract.Bookmarks.MENU_FOLDER_GUID, BrowserContract.Bookmarks.TAGS_FOLDER_GUID, BrowserContract.Bookmarks.TOOLBAR_FOLDER_GUID, BrowserContract.Bookmarks.UNFILED_FOLDER_GUID }); c = mProvider.query(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), null, null, null, null); assertCountIsAndClose(c, 6, "All non-special bookmarks and folders were deleted"); mProvider.delete(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), null, null); c = mProvider.query(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), null, null, null, null); assertCountIsAndClose(c, 0, "All history entries were deleted"); /** * There's no reason why the following two parts should fail. * But sometimes they do, and I'm not going to spend the time * to figure out why in an unrelated bug. */ mProvider.delete(appendUriParam(BrowserContract.Favicons.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), null, null); c = mProvider.query(appendUriParam(BrowserContract.Favicons.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), null, null, null, null); if (c.getCount() > 0) { mAsserter.dumpLog("Unexpected favicons in ensureEmptyDatabase."); } c.close(); mAsserter.dumpLog("ensureEmptyDatabase: Favicon deletion completed."); // Bug 968951 debug. // assertCountIsAndClose(c, 0, "All favicons were deleted"); mProvider.delete(appendUriParam(BrowserContract.Thumbnails.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), null, null); c = mProvider.query(appendUriParam(BrowserContract.Thumbnails.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), null, null, null, null); if (c.getCount() > 0) { mAsserter.dumpLog("Unexpected thumbnails in ensureEmptyDatabase."); } c.close(); mAsserter.dumpLog("ensureEmptyDatabase: Thumbnail deletion completed."); // Bug 968951 debug. // assertCountIsAndClose(c, 0, "All thumbnails were deleted"); } private ContentValues createBookmark(String title, String url, long parentId, int type, int position, String tags, String description, String keyword) throws Exception { ContentValues bookmark = new ContentValues(); bookmark.put(BrowserContract.Bookmarks.TITLE, title); bookmark.put(BrowserContract.Bookmarks.URL, url); bookmark.put(BrowserContract.Bookmarks.PARENT, parentId); bookmark.put(BrowserContract.Bookmarks.TYPE, type); bookmark.put(BrowserContract.Bookmarks.POSITION, position); bookmark.put(BrowserContract.Bookmarks.TAGS, tags); bookmark.put(BrowserContract.Bookmarks.DESCRIPTION, description); bookmark.put(BrowserContract.Bookmarks.KEYWORD, keyword); return bookmark; } private ContentValues createOneBookmark() throws Exception { return createBookmark("Example", "http://example.com", mMobileFolderId, BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword"); } private Cursor getBookmarksByParent(long parent) throws Exception { // Order by position. return mProvider.query(BrowserContract.Bookmarks.CONTENT_URI, null, BrowserContract.Bookmarks.PARENT + " = ?", new String[] { String.valueOf(parent) }, BrowserContract.Bookmarks.POSITION); } private Cursor getBookmarkByGuid(String guid) throws Exception { return mProvider.query(BrowserContract.Bookmarks.CONTENT_URI, null, BrowserContract.Bookmarks.GUID + " = ?", new String[] { guid }, null); } private Cursor getBookmarkById(long id) throws Exception { return getBookmarkById(BrowserContract.Bookmarks.CONTENT_URI, id, null); } private Cursor getBookmarkById(long id, String[] projection) throws Exception { return getBookmarkById(BrowserContract.Bookmarks.CONTENT_URI, id, projection); } private Cursor getBookmarkById(Uri bookmarksUri, long id) throws Exception { return getBookmarkById(bookmarksUri, id, null); } private Cursor getBookmarkById(Uri bookmarksUri, long id, String[] projection) throws Exception { return mProvider.query(bookmarksUri, projection, BrowserContract.Bookmarks._ID + " = ?", new String[] { String.valueOf(id) }, null); } private ContentValues createHistoryEntry(String title, String url, int visits, long lastVisited) throws Exception { ContentValues historyEntry = new ContentValues(); historyEntry.put(BrowserContract.History.TITLE, title); historyEntry.put(BrowserContract.History.URL, url); historyEntry.put(BrowserContract.History.VISITS, visits); historyEntry.put(BrowserContract.History.DATE_LAST_VISITED, lastVisited); return historyEntry; } private ContentValues createFaviconEntry(String pageUrl, String data) throws Exception { ContentValues faviconEntry = new ContentValues(); faviconEntry.put(BrowserContract.Favicons.PAGE_URL, pageUrl); faviconEntry.put(BrowserContract.Favicons.URL, pageUrl + "/favicon.ico"); faviconEntry.put(BrowserContract.Favicons.DATA, data.getBytes("UTF8")); return faviconEntry; } private ContentValues createThumbnailEntry(String pageUrl, String data) throws Exception { ContentValues thumbnailEntry = new ContentValues(); thumbnailEntry.put(BrowserContract.Thumbnails.URL, pageUrl); thumbnailEntry.put(BrowserContract.Thumbnails.DATA, data.getBytes("UTF8")); return thumbnailEntry; } private ContentValues createOneHistoryEntry() throws Exception { return createHistoryEntry("Example", "http://example.com", 10, System.currentTimeMillis()); } private Cursor getHistoryEntryById(long id) throws Exception { return getHistoryEntryById(BrowserContract.History.CONTENT_URI, id, null); } private Cursor getHistoryEntryById(long id, String[] projection) throws Exception { return getHistoryEntryById(BrowserContract.History.CONTENT_URI, id, projection); } private Cursor getHistoryEntryById(Uri historyUri, long id) throws Exception { return getHistoryEntryById(historyUri, id, null); } private Cursor getHistoryEntryById(Uri historyUri, long id, String[] projection) throws Exception { return mProvider.query(historyUri, projection, BrowserContract.History._ID + " = ?", new String[] { String.valueOf(id) }, null); } private Cursor getFaviconsByUrl(String url) throws Exception { return mProvider.query(BrowserContract.Combined.CONTENT_URI, null, BrowserContract.Combined.URL + " = ?", new String[] { url }, null); } private Cursor getThumbnailByUrl(String url) throws Exception { return mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, BrowserContract.Thumbnails.URL + " = ?", new String[] { url }, null); } @Override public void setUp() throws Exception { super.setUp(sBrowserProviderCallable, BrowserContract.AUTHORITY, "browser.db"); mTests.add(new TestSpecialFolders()); mTests.add(new TestInsertBookmarks()); mTests.add(new TestInsertBookmarksFavicons()); mTests.add(new TestDeleteBookmarks()); mTests.add(new TestDeleteBookmarksFavicons()); mTests.add(new TestUpdateBookmarks()); mTests.add(new TestUpdateBookmarksFavicons()); mTests.add(new TestPositionBookmarks()); mTests.add(new TestInsertHistory()); mTests.add(new TestInsertHistoryFavicons()); mTests.add(new TestDeleteHistory()); mTests.add(new TestDeleteHistoryFavicons()); mTests.add(new TestUpdateHistory()); mTests.add(new TestUpdateHistoryFavicons()); mTests.add(new TestUpdateOrInsertHistory()); mTests.add(new TestInsertHistoryThumbnails()); mTests.add(new TestUpdateHistoryThumbnails()); mTests.add(new TestDeleteHistoryThumbnails()); mTests.add(new TestBatchOperations()); mTests.add(new TestCombinedView()); mTests.add(new TestCombinedViewDisplay()); mTests.add(new TestCombinedViewWithDeletedBookmark()); mTests.add(new TestExpireHistory()); mTests.add(new TestBrowserProviderNotifications()); } public void testBrowserProvider() throws Exception { loadMobileFolderId(); for (int i = 0; i < mTests.size(); i++) { Runnable test = mTests.get(i); final String testName = test.getClass().getSimpleName(); setTestName(testName); ensureEmptyDatabase(); mAsserter.dumpLog("testBrowserProvider: Database empty - Starting " + testName + "."); test.run(); } } private class TestBatchOperations extends TestCase { static final int TESTCOUNT = 100; public void testApplyBatch() throws Exception { ArrayList mOperations = new ArrayList(); // Test a bunch of inserts with applyBatch ContentValues values = new ContentValues(); ContentProviderOperation.Builder builder = null; for (int i = 0; i < TESTCOUNT; i++) { values.clear(); values.put(BrowserContract.History.VISITS, i); values.put(BrowserContract.History.TITLE, "Test" + i); values.put(BrowserContract.History.URL, "http://www.test.org/" + i); // Insert builder = ContentProviderOperation.newInsert(BrowserContract.History.CONTENT_URI); builder.withValues(values); // Queue the operation mOperations.add(builder.build()); } ContentProviderResult[] applyResult = mProvider.applyBatch(mOperations); boolean allFound = true; for (int i = 0; i < TESTCOUNT; i++) { Cursor cursor = mProvider.query(BrowserContract.History.CONTENT_URI, null, BrowserContract.History.URL + " = ?", new String[] { "http://www.test.org/" + i }, null); if (!cursor.moveToFirst()) allFound = false; cursor.close(); } mAsserter.is(allFound, true, "Found all batchApply entries"); mOperations.clear(); // Update all visits to 1 values.clear(); values.put(BrowserContract.History.VISITS, 1); for (int i = 0; i < TESTCOUNT; i++) { builder = ContentProviderOperation.newUpdate(BrowserContract.History.CONTENT_URI); builder.withSelection(BrowserContract.History.URL + " = ?", new String[] {"http://www.test.org/" + i}); builder.withValues(values); builder.withExpectedCount(1); // Queue the operation mOperations.add(builder.build()); } boolean seenException = false; try { applyResult = mProvider.applyBatch(mOperations); } catch (OperationApplicationException ex) { seenException = true; } mAsserter.is(seenException, false, "Batch updating succeeded"); mOperations.clear(); // Delete all visits for (int i = 0; i < TESTCOUNT; i++) { builder = ContentProviderOperation.newDelete(BrowserContract.History.CONTENT_URI); builder.withSelection(BrowserContract.History.URL + " = ?", new String[] {"http://www.test.org/" + i}); builder.withExpectedCount(1); // Queue the operation mOperations.add(builder.build()); } try { applyResult = mProvider.applyBatch(mOperations); } catch (OperationApplicationException ex) { seenException = true; } mAsserter.is(seenException, false, "Batch deletion succeeded"); } // Force a Constraint error, see if later operations still apply correctly public void testApplyBatchErrors() throws Exception { ArrayList mOperations = new ArrayList(); // Test a bunch of inserts with applyBatch ContentProviderOperation.Builder builder = null; ContentValues values = createFaviconEntry("http://www.test.org", "FAVICON"); builder = ContentProviderOperation.newInsert(BrowserContract.Favicons.CONTENT_URI); builder.withValues(values); mOperations.add(builder.build()); // Make a duplicate, this will fail because of a UNIQUE constraint builder = ContentProviderOperation.newInsert(BrowserContract.Favicons.CONTENT_URI); builder.withValues(values); mOperations.add(builder.build()); // This is valid and should be in the table afterwards values.put(BrowserContract.Favicons.URL, "http://www.test.org/valid.ico"); builder = ContentProviderOperation.newInsert(BrowserContract.Favicons.CONTENT_URI); builder.withValues(values); mOperations.add(builder.build()); boolean seenException = false; try { ContentProviderResult[] applyResult = mProvider.applyBatch(mOperations); } catch (OperationApplicationException ex) { seenException = true; } // This test may need to go away if Bug 717428 is fixed. mAsserter.is(seenException, true, "Expected failure in favicons table"); boolean allFound = true; Cursor cursor = mProvider.query(BrowserContract.Favicons.CONTENT_URI, null, BrowserContract.Favicons.URL + " = ?", new String[] { "http://www.test.org/valid.ico" }, null); if (!cursor.moveToFirst()) allFound = false; cursor.close(); mAsserter.is(allFound, true, "Found all applyBatch (with error) entries"); } public void testBulkInsert() throws Exception { // Test a bunch of inserts with bulkInsert ContentValues allVals[] = new ContentValues[TESTCOUNT]; for (int i = 0; i < TESTCOUNT; i++) { allVals[i] = new ContentValues(); allVals[i].put(BrowserContract.History.URL, i); allVals[i].put(BrowserContract.History.TITLE, "Test" + i); allVals[i].put(BrowserContract.History.URL, "http://www.test.org/" + i); } int inserts = mProvider.bulkInsert(BrowserContract.History.CONTENT_URI, allVals); mAsserter.is(inserts, TESTCOUNT, "Excepted number of inserts matches"); boolean allFound = true; for (int i = 0; i < TESTCOUNT; i++) { Cursor cursor = mProvider.query(BrowserContract.History.CONTENT_URI, null, BrowserContract.History.URL + " = ?", new String[] { "http://www.test.org/" + i }, null); if (!cursor.moveToFirst()) allFound = false; cursor.close(); } mAsserter.is(allFound, true, "Found all bulkInsert entries"); } @Override public void test() throws Exception { testApplyBatch(); // Clean up ensureEmptyDatabase(); testBulkInsert(); ensureEmptyDatabase(); testApplyBatchErrors(); } } private class TestSpecialFolders extends TestCase { @Override public void test() throws Exception { Cursor c = mProvider.query(BrowserContract.Bookmarks.CONTENT_URI, new String[] { BrowserContract.Bookmarks._ID, BrowserContract.Bookmarks.GUID, BrowserContract.Bookmarks.PARENT }, BrowserContract.Bookmarks.GUID + " = ? OR " + BrowserContract.Bookmarks.GUID + " = ? OR " + BrowserContract.Bookmarks.GUID + " = ? OR " + BrowserContract.Bookmarks.GUID + " = ? OR " + BrowserContract.Bookmarks.GUID + " = ? OR " + BrowserContract.Bookmarks.GUID + " = ?", new String[] { BrowserContract.Bookmarks.PLACES_FOLDER_GUID, BrowserContract.Bookmarks.MOBILE_FOLDER_GUID, BrowserContract.Bookmarks.MENU_FOLDER_GUID, BrowserContract.Bookmarks.TAGS_FOLDER_GUID, BrowserContract.Bookmarks.TOOLBAR_FOLDER_GUID, BrowserContract.Bookmarks.UNFILED_FOLDER_GUID}, null); mAsserter.is(c.getCount(), 6, "Right number of special folders"); int rootId = BrowserContract.Bookmarks.FIXED_ROOT_ID; while (c.moveToNext()) { int id = c.getInt(c.getColumnIndex(BrowserContract.Bookmarks._ID)); String guid = c.getString(c.getColumnIndex(BrowserContract.Bookmarks.GUID)); int parentId = c.getInt(c.getColumnIndex(BrowserContract.Bookmarks.PARENT)); if (guid.equals(BrowserContract.Bookmarks.PLACES_FOLDER_GUID)) { mAsserter.is(id, rootId, "The id of places folder is correct"); } mAsserter.is(parentId, rootId, "The PARENT of the " + guid + " special folder is correct"); } c.close(); } } private class TestInsertBookmarks extends TestCase { private long insertWithNullCol(String colName) throws Exception { ContentValues b = createOneBookmark(); b.putNull(colName); long id = -1; try { id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b)); } catch (Exception e) {} return id; } @Override public void test() throws Exception { ContentValues b = createOneBookmark(); long id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b)); final Cursor c = getBookmarkById(id); try { mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TITLE)), b.getAsString(BrowserContract.Bookmarks.TITLE), "Inserted bookmark has correct title"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.URL)), b.getAsString(BrowserContract.Bookmarks.URL), "Inserted bookmark has correct URL"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TAGS)), b.getAsString(BrowserContract.Bookmarks.TAGS), "Inserted bookmark has correct tags"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.KEYWORD)), b.getAsString(BrowserContract.Bookmarks.KEYWORD), "Inserted bookmark has correct keyword"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.DESCRIPTION)), b.getAsString(BrowserContract.Bookmarks.DESCRIPTION), "Inserted bookmark has correct description"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.POSITION)), b.getAsString(BrowserContract.Bookmarks.POSITION), "Inserted bookmark has correct position"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TYPE)), b.getAsString(BrowserContract.Bookmarks.TYPE), "Inserted bookmark has correct type"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.PARENT)), b.getAsString(BrowserContract.Bookmarks.PARENT), "Inserted bookmark has correct parent ID"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.IS_DELETED)), String.valueOf(0), "Inserted bookmark has correct is-deleted state"); id = insertWithNullCol(BrowserContract.Bookmarks.POSITION); mAsserter.is(id, -1L, "Should not be able to insert bookmark with null position"); id = insertWithNullCol(BrowserContract.Bookmarks.TYPE); mAsserter.is(id, -1L, "Should not be able to insert bookmark with null type"); if (Build.VERSION.SDK_INT >= 8 && Build.VERSION.SDK_INT < 16) { b = createOneBookmark(); b.put(BrowserContract.Bookmarks.PARENT, -1); id = -1; try { id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b)); } catch (Exception e) {} mAsserter.is(id, -1L, "Should not be able to insert bookmark with invalid parent"); } b = createOneBookmark(); b.remove(BrowserContract.Bookmarks.TYPE); id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b)); final Cursor c2 = getBookmarkById(id); try { mAsserter.is(c2.moveToFirst(), true, "Inserted bookmark found"); mAsserter.is(c2.getString(c2.getColumnIndex(BrowserContract.Bookmarks.TYPE)), String.valueOf(BrowserContract.Bookmarks.TYPE_BOOKMARK), "Inserted bookmark has correct default type"); } finally { c2.close(); } } finally { c.close(); } } } private class TestInsertBookmarksFavicons extends TestCase { @Override public void test() throws Exception { ContentValues b = createOneBookmark(); final String favicon = "FAVICON"; final String pageUrl = b.getAsString(BrowserContract.Bookmarks.URL); long id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b)); // Insert the favicon into the favicons table mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, favicon)); Cursor c = getBookmarkById(id, new String[] { BrowserContract.Bookmarks.FAVICON }); mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found"); mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Bookmarks.FAVICON)), "UTF8"), favicon, "Inserted bookmark has corresponding favicon image"); c.close(); c = getFaviconsByUrl(pageUrl); mAsserter.is(c.moveToFirst(), true, "Inserted favicon found"); mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"), favicon, "Inserted favicon has corresponding favicon image"); c.close(); } } private class TestDeleteBookmarks extends TestCase { private long insertOneBookmark() throws Exception { ContentValues b = createOneBookmark(); long id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b)); Cursor c = getBookmarkById(id); mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found"); c.close(); return id; } @Override public void test() throws Exception { long id = insertOneBookmark(); int deleted = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.Bookmarks._ID + " = ?", new String[] { String.valueOf(id) }); mAsserter.is((deleted == 1), true, "Inserted bookmark was deleted"); Cursor c = getBookmarkById(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id); mAsserter.is(c.moveToFirst(), true, "Deleted bookmark was only marked as deleted"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TITLE)), null, "Deleted bookmark title is null"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.URL)), null, "Deleted bookmark URL is null"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TAGS)), null, "Deleted bookmark tags is null"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.KEYWORD)), null, "Deleted bookmark keyword is null"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.DESCRIPTION)), null, "Deleted bookmark description is null"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.POSITION)), String.valueOf(0), "Deleted bookmark has correct position"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.PARENT)), null, "Deleted bookmark parent ID is null"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.IS_DELETED)), String.valueOf(1), "Deleted bookmark has correct is-deleted state"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.FAVICON_ID)), null, "Deleted bookmark Favicon ID is null"); mAsserter.isnot(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.GUID)), null, "Deleted bookmark GUID is not null"); c.close(); deleted = mProvider.delete(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), BrowserContract.Bookmarks._ID + " = ?", new String[] { String.valueOf(id) }); mAsserter.is((deleted == 1), true, "Inserted bookmark was deleted"); c = getBookmarkById(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id); mAsserter.is(c.moveToFirst(), false, "Inserted bookmark is now actually deleted"); c.close(); id = insertOneBookmark(); deleted = mProvider.delete(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), null, null); mAsserter.is((deleted == 1), true, "Inserted bookmark was deleted using URI with id"); c = getBookmarkById(id); mAsserter.is(c.moveToFirst(), false, "Inserted bookmark can't be found after deletion using URI with ID"); c.close(); if (Build.VERSION.SDK_INT >= 8 && Build.VERSION.SDK_INT < 16) { ContentValues b = createBookmark("Folder", null, mMobileFolderId, BrowserContract.Bookmarks.TYPE_FOLDER, 0, "folderTags", "folderDescription", "folderKeyword"); long parentId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b)); c = getBookmarkById(parentId); mAsserter.is(c.moveToFirst(), true, "Inserted bookmarks folder found"); c.close(); b = createBookmark("Example", "http://example.com", parentId, BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword"); id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b)); c = getBookmarkById(id); mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found"); c.close(); deleted = 0; try { Uri uri = ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, parentId); deleted = mProvider.delete(appendUriParam(uri, BrowserContract.PARAM_IS_SYNC, "1"), null, null); } catch(Exception e) {} mAsserter.is((deleted == 0), true, "Should not be able to delete folder that causes orphan bookmarks"); } } } private class TestDeleteBookmarksFavicons extends TestCase { @Override public void test() throws Exception { ContentValues b = createOneBookmark(); final String pageUrl = b.getAsString(BrowserContract.Bookmarks.URL); long id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b)); // Insert the favicon into the favicons table mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, "FAVICON")); Cursor c = getFaviconsByUrl(pageUrl); mAsserter.is(c.moveToFirst(), true, "Inserted favicon found"); c.close(); mProvider.delete(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), null, null); c = getFaviconsByUrl(pageUrl); mAsserter.is(c.moveToFirst(), false, "Favicon is deleted with last reference to it"); c.close(); } } private class TestUpdateBookmarks extends TestCase { private int updateWithNullCol(long id, String colName) throws Exception { ContentValues u = new ContentValues(); u.putNull(colName); int updated = 0; try { updated = mProvider.update(BrowserContract.Bookmarks.CONTENT_URI, u, BrowserContract.Bookmarks._ID + " = ?", new String[] { String.valueOf(id) }); } catch (Exception e) {} return updated; } @Override public void test() throws Exception { ContentValues b = createOneBookmark(); long id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b)); Cursor c = getBookmarkById(id); mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found"); long dateCreated = c.getLong(c.getColumnIndex(BrowserContract.Bookmarks.DATE_CREATED)); long dateModified = c.getLong(c.getColumnIndex(BrowserContract.Bookmarks.DATE_MODIFIED)); ContentValues u = new ContentValues(); u.put(BrowserContract.Bookmarks.TITLE, b.getAsString(BrowserContract.Bookmarks.TITLE) + "CHANGED"); u.put(BrowserContract.Bookmarks.URL, b.getAsString(BrowserContract.Bookmarks.URL) + "/more/stuff"); u.put(BrowserContract.Bookmarks.TAGS, b.getAsString(BrowserContract.Bookmarks.TAGS) + "CHANGED"); u.put(BrowserContract.Bookmarks.DESCRIPTION, b.getAsString(BrowserContract.Bookmarks.DESCRIPTION) + "CHANGED"); u.put(BrowserContract.Bookmarks.KEYWORD, b.getAsString(BrowserContract.Bookmarks.KEYWORD) + "CHANGED"); u.put(BrowserContract.Bookmarks.TYPE, BrowserContract.Bookmarks.TYPE_FOLDER); u.put(BrowserContract.Bookmarks.POSITION, 10); int updated = mProvider.update(BrowserContract.Bookmarks.CONTENT_URI, u, BrowserContract.Bookmarks._ID + " = ?", new String[] { String.valueOf(id) }); mAsserter.is((updated == 1), true, "Inserted bookmark was updated"); c.close(); c = getBookmarkById(id); mAsserter.is(c.moveToFirst(), true, "Updated bookmark found"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TITLE)), u.getAsString(BrowserContract.Bookmarks.TITLE), "Inserted bookmark has correct title"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.URL)), u.getAsString(BrowserContract.Bookmarks.URL), "Inserted bookmark has correct URL"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TAGS)), u.getAsString(BrowserContract.Bookmarks.TAGS), "Inserted bookmark has correct tags"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.KEYWORD)), u.getAsString(BrowserContract.Bookmarks.KEYWORD), "Inserted bookmark has correct keyword"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.DESCRIPTION)), u.getAsString(BrowserContract.Bookmarks.DESCRIPTION), "Inserted bookmark has correct description"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.POSITION)), u.getAsString(BrowserContract.Bookmarks.POSITION), "Inserted bookmark has correct position"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TYPE)), u.getAsString(BrowserContract.Bookmarks.TYPE), "Inserted bookmark has correct type"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Bookmarks.DATE_CREATED)), dateCreated, "Updated bookmark has same creation date"); mAsserter.isnot(c.getLong(c.getColumnIndex(BrowserContract.Bookmarks.DATE_MODIFIED)), dateModified, "Updated bookmark has new modification date"); updated = updateWithNullCol(id, BrowserContract.Bookmarks.POSITION); mAsserter.is((updated > 0), false, "Should not be able to update bookmark with null position"); updated = updateWithNullCol(id, BrowserContract.Bookmarks.TYPE); mAsserter.is((updated > 0), false, "Should not be able to update bookmark with null type"); u = new ContentValues(); u.put(BrowserContract.Bookmarks.URL, "http://examples2.com"); updated = mProvider.update(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), u, null, null); c.close(); c = getBookmarkById(id); mAsserter.is(c.moveToFirst(), true, "Updated bookmark found"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.URL)), u.getAsString(BrowserContract.Bookmarks.URL), "Updated bookmark has correct URL using URI with id"); c.close(); } } private class TestUpdateBookmarksFavicons extends TestCase { @Override public void test() throws Exception { ContentValues b = createOneBookmark(); final String favicon = "FAVICON"; final String newFavicon = "NEW_FAVICON"; final String pageUrl = b.getAsString(BrowserContract.Bookmarks.URL); mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b); // Insert the favicon into the favicons table ContentValues f = createFaviconEntry(pageUrl, favicon); long faviconId = ContentUris.parseId(mProvider.insert(BrowserContract.Favicons.CONTENT_URI, f)); Cursor c = getFaviconsByUrl(pageUrl); mAsserter.is(c.moveToFirst(), true, "Inserted favicon found"); mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"), favicon, "Inserted favicon has corresponding favicon image"); ContentValues u = createFaviconEntry(pageUrl, newFavicon); mProvider.update(BrowserContract.Favicons.CONTENT_URI, u, null, null); c.close(); c = getFaviconsByUrl(pageUrl); mAsserter.is(c.moveToFirst(), true, "Updated favicon found"); mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"), newFavicon, "Updated favicon has corresponding favicon image"); c.close(); } } /** * Create a folder of one thousand and one bookmarks, then impose an order * on them. * * Verify that the reordering worked by querying. */ private class TestPositionBookmarks extends TestCase { public String makeGUID(final long in) { String part = String.valueOf(in); return "aaaaaaaaaaaa".substring(0, (12 - part.length())) + part; } public void compareCursorToItems(final Cursor c, final String[] items, final int count) { mAsserter.is(c.moveToFirst(), true, "Folder has children."); int posColumn = c.getColumnIndex(BrowserContract.Bookmarks.POSITION); int guidColumn = c.getColumnIndex(BrowserContract.Bookmarks.GUID); int i = 0; while (!c.isAfterLast()) { String guid = c.getString(guidColumn); long pos = c.getLong(posColumn); if ((pos != i) || (guid == null) || (!guid.equals(items[i]))) { mAsserter.is(pos, (long) i, "Position matches sequence."); mAsserter.is(guid, items[i], "GUID matches sequence."); } ++i; c.moveToNext(); } mAsserter.is(i, count, "Folder has the right number of children."); c.close(); } public static final int NUMBER_OF_CHILDREN = 1001; @Override public void test() throws Exception { // Create the containing folder. ContentValues folder = createBookmark("FolderFolder", "", mMobileFolderId, BrowserContract.Bookmarks.TYPE_FOLDER, 0, "", "description", "keyword"); folder.put(BrowserContract.Bookmarks.GUID, "folderfolder"); long folderId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, folder)); mAsserter.dumpLog("TestPositionBookmarks: Folder inserted"); // Bug 968951 debug. // Create the children. String[] items = new String[NUMBER_OF_CHILDREN]; // Reuse the same ContentValues. ContentValues item = createBookmark("Test Bookmark", "http://example.com", folderId, BrowserContract.Bookmarks.TYPE_FOLDER, 0, "", "description", "keyword"); for (int i = 0; i < NUMBER_OF_CHILDREN; ++i) { String guid = makeGUID(i); items[i] = guid; item.put(BrowserContract.Bookmarks.GUID, guid); item.put(BrowserContract.Bookmarks.POSITION, i); item.put(BrowserContract.Bookmarks.URL, "http://example.com/" + guid); item.put(BrowserContract.Bookmarks.TITLE, "Test Bookmark " + guid); mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, item); } mAsserter.dumpLog("TestPositionBookmarks: Bookmarks inserted"); // Bug 968951 debug. Cursor c; // Verify insertion. c = getBookmarksByParent(folderId); mAsserter.dumpLog("TestPositionBookmarks: Got bookmarks by parent"); // Bug 968951 debug. compareCursorToItems(c, items, NUMBER_OF_CHILDREN); c.close(); // Now permute the items array. Random rand = new Random(); for (int i = 0; i < NUMBER_OF_CHILDREN; ++i) { final int newPosition = rand.nextInt(NUMBER_OF_CHILDREN); final String switched = items[newPosition]; items[newPosition] = items[i]; items[i] = switched; } // Impose the positions. long updated = mProvider.update(BrowserContract.Bookmarks.POSITIONS_CONTENT_URI, null, null, items); mAsserter.is(updated, (long) NUMBER_OF_CHILDREN, "Updated " + NUMBER_OF_CHILDREN + " positions."); // Verify that the database was updated. c = getBookmarksByParent(folderId); compareCursorToItems(c, items, NUMBER_OF_CHILDREN); c.close(); } } private class TestInsertHistory extends TestCase { private long insertWithNullCol(String colName) throws Exception { ContentValues h = createOneHistoryEntry(); h.putNull(colName); long id = -1; try { id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h)); } catch (Exception e) {} return id; } @Override public void test() throws Exception { ContentValues h = createOneHistoryEntry(); long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h)); Cursor c = getHistoryEntryById(id); mAsserter.is(c.moveToFirst(), true, "Inserted history entry found"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), h.getAsString(BrowserContract.History.TITLE), "Inserted history entry has correct title"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), h.getAsString(BrowserContract.History.URL), "Inserted history entry has correct URL"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.VISITS)), h.getAsString(BrowserContract.History.VISITS), "Inserted history entry has correct number of visits"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.DATE_LAST_VISITED)), h.getAsString(BrowserContract.History.DATE_LAST_VISITED), "Inserted history entry has correct last visited date"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.IS_DELETED)), String.valueOf(0), "Inserted history entry has correct is-deleted state"); id = insertWithNullCol(BrowserContract.History.URL); mAsserter.is(id, -1L, "Should not be able to insert history with null URL"); id = insertWithNullCol(BrowserContract.History.VISITS); mAsserter.is(id, -1L, "Should not be able to insert history with null number of visits"); c.close(); } } private class TestInsertHistoryFavicons extends TestCase { @Override public void test() throws Exception { ContentValues h = createOneHistoryEntry(); final String favicon = "FAVICON"; final String pageUrl = h.getAsString(BrowserContract.History.URL); long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h)); // Insert the favicon into the favicons table mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, favicon)); Cursor c = getHistoryEntryById(id, new String[] { BrowserContract.History.FAVICON }); mAsserter.is(c.moveToFirst(), true, "Inserted history entry found"); mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.History.FAVICON)), "UTF8"), favicon, "Inserted history entry has corresponding favicon image"); c.close(); c = getFaviconsByUrl(pageUrl); mAsserter.is(c.moveToFirst(), true, "Inserted favicon found"); mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"), favicon, "Inserted favicon has corresponding favicon image"); c.close(); } } private class TestDeleteHistory extends TestCase { private long insertOneHistoryEntry() throws Exception { ContentValues h = createOneHistoryEntry(); long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h)); Cursor c = getHistoryEntryById(id); mAsserter.is(c.moveToFirst(), true, "Inserted history entry found"); c.close(); return id; } @Override public void test() throws Exception { long id = insertOneHistoryEntry(); int deleted = mProvider.delete(BrowserContract.History.CONTENT_URI, BrowserContract.History._ID + " = ?", new String[] { String.valueOf(id) }); mAsserter.is((deleted == 1), true, "Inserted history entry was deleted"); Cursor c = getHistoryEntryById(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id); mAsserter.is(c.moveToFirst(), true, "Deleted history entry was only marked as deleted"); deleted = mProvider.delete(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), BrowserContract.History._ID + " = ?", new String[] { String.valueOf(id) }); mAsserter.is((deleted == 1), true, "Inserted history entry was deleted"); c.close(); c = getHistoryEntryById(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id); mAsserter.is(c.moveToFirst(), false, "Inserted history is now actually deleted"); id = insertOneHistoryEntry(); deleted = mProvider.delete(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), null, null); mAsserter.is((deleted == 1), true, "Inserted history entry was deleted using URI with id"); c.close(); c = getHistoryEntryById(id); mAsserter.is(c.moveToFirst(), false, "Inserted history entry can't be found after deletion using URI with ID"); c.close(); } } private class TestDeleteHistoryFavicons extends TestCase { @Override public void test() throws Exception { ContentValues h = createOneHistoryEntry(); long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h)); final String pageUrl = h.getAsString(BrowserContract.History.URL); // Insert the favicon into the favicons table mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, "FAVICON")); Cursor c = getFaviconsByUrl(pageUrl); mAsserter.is(c.moveToFirst(), true, "Inserted favicon found"); mProvider.delete(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), null, null); c.close(); c = getFaviconsByUrl(pageUrl); mAsserter.is(c.moveToFirst(), false, "Favicon is deleted with last reference to it"); c.close(); } } private class TestUpdateHistory extends TestCase { private int updateWithNullCol(long id, String colName) throws Exception { ContentValues u = new ContentValues(); u.putNull(colName); int updated = 0; try { updated = mProvider.update(BrowserContract.History.CONTENT_URI, u, BrowserContract.History._ID + " = ?", new String[] { String.valueOf(id) }); } catch (Exception e) {} return updated; } @Override public void test() throws Exception { ContentValues h = createOneHistoryEntry(); long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h)); Cursor c = getHistoryEntryById(id); mAsserter.is(c.moveToFirst(), true, "Inserted history entry found"); long dateCreated = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED)); long dateModified = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED)); ContentValues u = new ContentValues(); u.put(BrowserContract.History.VISITS, h.getAsInteger(BrowserContract.History.VISITS) + 1); u.put(BrowserContract.History.DATE_LAST_VISITED, System.currentTimeMillis()); u.put(BrowserContract.History.TITLE, h.getAsString(BrowserContract.History.TITLE) + "CHANGED"); u.put(BrowserContract.History.URL, h.getAsString(BrowserContract.History.URL) + "/more/stuff"); int updated = mProvider.update(BrowserContract.History.CONTENT_URI, u, BrowserContract.History._ID + " = ?", new String[] { String.valueOf(id) }); mAsserter.is((updated == 1), true, "Inserted history entry was updated"); c.close(); c = getHistoryEntryById(id); mAsserter.is(c.moveToFirst(), true, "Updated history entry found"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), u.getAsString(BrowserContract.History.TITLE), "Updated history entry has correct title"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), u.getAsString(BrowserContract.History.URL), "Updated history entry has correct URL"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.VISITS)), u.getAsString(BrowserContract.History.VISITS), "Updated history entry has correct number of visits"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.DATE_LAST_VISITED)), u.getAsString(BrowserContract.History.DATE_LAST_VISITED), "Updated history entry has correct last visited date"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED)), dateCreated, "Updated history entry has same creation date"); mAsserter.isnot(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED)), dateModified, "Updated history entry has new modification date"); updated = updateWithNullCol(id, BrowserContract.History.URL); mAsserter.is((updated > 0), false, "Should not be able to update history with null URL"); updated = updateWithNullCol(id, BrowserContract.History.VISITS); mAsserter.is((updated > 0), false, "Should not be able to update history with null number of visits"); u = new ContentValues(); u.put(BrowserContract.History.URL, "http://examples2.com"); updated = mProvider.update(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), u, null, null); c.close(); c = getHistoryEntryById(id); mAsserter.is(c.moveToFirst(), true, "Updated history entry found"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), u.getAsString(BrowserContract.History.URL), "Updated history entry has correct URL using URI with id"); c.close(); } } private class TestUpdateHistoryFavicons extends TestCase { @Override public void test() throws Exception { ContentValues h = createOneHistoryEntry(); final String favicon = "FAVICON"; final String newFavicon = "NEW_FAVICON"; final String pageUrl = h.getAsString(BrowserContract.History.URL); mProvider.insert(BrowserContract.History.CONTENT_URI, h); // Insert the favicon into the favicons table mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, favicon)); Cursor c = getFaviconsByUrl(pageUrl); mAsserter.is(c.moveToFirst(), true, "Inserted favicon found"); mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"), favicon, "Inserted favicon has corresponding favicon image"); ContentValues u = createFaviconEntry(pageUrl, newFavicon); mProvider.update(BrowserContract.Favicons.CONTENT_URI, u, null, null); c.close(); c = getFaviconsByUrl(pageUrl); mAsserter.is(c.moveToFirst(), true, "Updated favicon found"); mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"), newFavicon, "Updated favicon has corresponding favicon image"); c.close(); } } private class TestUpdateOrInsertHistory extends TestCase { private final String TEST_URL_1 = "http://example.com"; private final String TEST_URL_2 = "http://example.org"; private final String TEST_TITLE = "Example"; private long getHistoryEntryIdByUrl(String url) { Cursor c = mProvider.query(BrowserContract.History.CONTENT_URI, new String[] { BrowserContract.History._ID }, BrowserContract.History.URL + " = ?", new String[] { url }, null); c.moveToFirst(); long id = c.getLong(0); c.close(); return id; } @Override public void test() throws Exception { Uri updateHistoryUri = BrowserContract.History.CONTENT_URI.buildUpon(). appendQueryParameter("increment_visits", "true").build(); Uri updateOrInsertHistoryUri = BrowserContract.History.CONTENT_URI.buildUpon(). appendQueryParameter("insert_if_needed", "true"). appendQueryParameter("increment_visits", "true").build(); // Update a non-existent history entry, without specifying visits or title ContentValues values = new ContentValues(); values.put(BrowserContract.History.URL, TEST_URL_1); int updated = mProvider.update(updateHistoryUri, values, BrowserContract.History.URL + " = ?", new String[] { TEST_URL_1 }); mAsserter.is((updated == 0), true, "History entry was not updated"); Cursor c = mProvider.query(BrowserContract.History.CONTENT_URI, null, null, null, null); mAsserter.is(c.moveToFirst(), false, "History entry was not inserted"); c.close(); // Now let's try with update-or-insert. updated = mProvider.update(updateOrInsertHistoryUri, values, BrowserContract.History.URL + " = ?", new String[] { TEST_URL_1 }); mAsserter.is((updated == 1), true, "History entry was inserted"); long id = getHistoryEntryIdByUrl(TEST_URL_1); c = getHistoryEntryById(id); mAsserter.is(c.moveToFirst(), true, "History entry was inserted"); long dateCreated = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED)); long dateModified = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED)); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS)), 1L, "Inserted history entry has correct default number of visits"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), TEST_URL_1, "Inserted history entry has correct default title"); // Update the history entry, without specifying an additional visit count values = new ContentValues(); values.put(BrowserContract.History.DATE_LAST_VISITED, System.currentTimeMillis()); values.put(BrowserContract.History.TITLE, TEST_TITLE); updated = mProvider.update(updateOrInsertHistoryUri, values, BrowserContract.History._ID + " = ?", new String[] { String.valueOf(id) }); mAsserter.is((updated == 1), true, "Inserted history entry was updated"); c.close(); c = getHistoryEntryById(id); mAsserter.is(c.moveToFirst(), true, "Updated history entry found"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), TEST_TITLE, "Updated history entry has correct title"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS)), 2L, "Updated history entry has correct number of visits"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED)), dateCreated, "Updated history entry has same creation date"); mAsserter.isnot(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED)), dateModified, "Updated history entry has new modification date"); // Create a new history entry, specifying visits and history values = new ContentValues(); values.put(BrowserContract.History.URL, TEST_URL_2); values.put(BrowserContract.History.TITLE, TEST_TITLE); values.put(BrowserContract.History.VISITS, 10); updated = mProvider.update(updateOrInsertHistoryUri, values, BrowserContract.History.URL + " = ?", new String[] { values.getAsString(BrowserContract.History.URL) }); mAsserter.is((updated == 1), true, "History entry was inserted"); id = getHistoryEntryIdByUrl(TEST_URL_2); c.close(); c = getHistoryEntryById(id); mAsserter.is(c.moveToFirst(), true, "History entry was inserted"); dateCreated = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED)); dateModified = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED)); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS)), 10L, "Inserted history entry has correct specified number of visits"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), TEST_TITLE, "Inserted history entry has correct specified title"); // Update the history entry, specifying additional visit count values = new ContentValues(); values.put(BrowserContract.History.VISITS, 10); updated = mProvider.update(updateOrInsertHistoryUri, values, BrowserContract.History._ID + " = ?", new String[] { String.valueOf(id) }); mAsserter.is((updated == 1), true, "Inserted history entry was updated"); c.close(); c = getHistoryEntryById(id); mAsserter.is(c.moveToFirst(), true, "Updated history entry found"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), TEST_TITLE, "Updated history entry has correct unchanged title"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), TEST_URL_2, "Updated history entry has correct unchanged URL"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS)), 20L, "Updated history entry has correct number of visits"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED)), dateCreated, "Updated history entry has same creation date"); mAsserter.isnot(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED)), dateModified, "Updated history entry has new modification date"); c.close(); } } private class TestInsertHistoryThumbnails extends TestCase { @Override public void test() throws Exception { ContentValues h = createOneHistoryEntry(); final String thumbnail = "THUMBNAIL"; final String pageUrl = h.getAsString(BrowserContract.History.URL); long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h)); // Insert the thumbnail into the thumbnails table mProvider.insert(BrowserContract.Thumbnails.CONTENT_URI, createThumbnailEntry(pageUrl, thumbnail)); Cursor c = getThumbnailByUrl(pageUrl); mAsserter.is(c.moveToFirst(), true, "Inserted thumbnail found"); mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Thumbnails.DATA)), "UTF8"), thumbnail, "Inserted thumbnail has corresponding thumbnail image"); c.close(); } } private class TestUpdateHistoryThumbnails extends TestCase { @Override public void test() throws Exception { ContentValues h = createOneHistoryEntry(); final String thumbnail = "THUMBNAIL"; final String newThumbnail = "NEW_THUMBNAIL"; final String pageUrl = h.getAsString(BrowserContract.History.URL); mProvider.insert(BrowserContract.History.CONTENT_URI, h); // Insert the thumbnail into the thumbnails table mProvider.insert(BrowserContract.Thumbnails.CONTENT_URI, createThumbnailEntry(pageUrl, thumbnail)); Cursor c = getThumbnailByUrl(pageUrl); mAsserter.is(c.moveToFirst(), true, "Inserted thumbnail found"); mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Thumbnails.DATA)), "UTF8"), thumbnail, "Inserted thumbnail has corresponding thumbnail image"); ContentValues u = createThumbnailEntry(pageUrl, newThumbnail); mProvider.update(BrowserContract.Thumbnails.CONTENT_URI, u, null, null); c.close(); c = getThumbnailByUrl(pageUrl); mAsserter.is(c.moveToFirst(), true, "Updated thumbnail found"); mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Thumbnails.DATA)), "UTF8"), newThumbnail, "Updated thumbnail has corresponding thumbnail image"); c.close(); } } private class TestDeleteHistoryThumbnails extends TestCase { @Override public void test() throws Exception { ContentValues h = createOneHistoryEntry(); long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h)); final String pageUrl = h.getAsString(BrowserContract.History.URL); // Insert the thumbnail into the thumbnails table mProvider.insert(BrowserContract.Thumbnails.CONTENT_URI, createThumbnailEntry(pageUrl, "THUMBNAIL")); Cursor c = getThumbnailByUrl(pageUrl); mAsserter.is(c.moveToFirst(), true, "Inserted thumbnail found"); mProvider.delete(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), null, null); c.close(); c = getThumbnailByUrl(pageUrl); mAsserter.is(c.moveToFirst(), false, "Thumbnail is deleted with last reference to it"); c.close(); } } private class TestCombinedView extends TestCase { @Override public void test() throws Exception { final String TITLE_1 = "Test Page 1"; final String TITLE_2 = "Test Page 2"; final String TITLE_3_HISTORY = "Test Page 3 (History Entry)"; final String TITLE_3_BOOKMARK = "Test Page 3 (Bookmark Entry)"; final String TITLE_3_BOOKMARK2 = "Test Page 3 (Bookmark Entry 2)"; final String URL_1 = "http://example1.com"; final String URL_2 = "http://example2.com"; final String URL_3 = "http://example3.com"; final int VISITS = 10; final long LAST_VISITED = System.currentTimeMillis(); // Create a basic history entry ContentValues basicHistory = createHistoryEntry(TITLE_1, URL_1, VISITS, LAST_VISITED); long basicHistoryId = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, basicHistory)); // Create a basic bookmark entry ContentValues basicBookmark = createBookmark(TITLE_2, URL_2, mMobileFolderId, BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword"); long basicBookmarkId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, basicBookmark)); // Create a history entry and bookmark entry with the same URL to // represent a visited bookmark ContentValues combinedHistory = createHistoryEntry(TITLE_3_HISTORY, URL_3, VISITS, LAST_VISITED); long combinedHistoryId = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, combinedHistory)); ContentValues combinedBookmark = createBookmark(TITLE_3_BOOKMARK, URL_3, mMobileFolderId, BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword"); long combinedBookmarkId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, combinedBookmark)); ContentValues combinedBookmark2 = createBookmark(TITLE_3_BOOKMARK2, URL_3, mMobileFolderId, BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword"); long combinedBookmarkId2 = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, combinedBookmark2)); // Create a bookmark folder to make sure it _doesn't_ show up in the results ContentValues folderBookmark = createBookmark("", "", mMobileFolderId, BrowserContract.Bookmarks.TYPE_FOLDER, 0, "tags", "description", "keyword"); mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, folderBookmark); // Sort entries by url so we can check them individually final Cursor c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, BrowserContract.Combined.URL); try { mAsserter.is(c.getCount(), 3, "3 combined entries found"); // First combined entry is basic history entry mAsserter.is(c.moveToFirst(), true, "Found basic history entry"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined._ID)), 0L, "Combined _id column should always be 0"); // TODO: Should we change BrowserProvider to make this return -1, not 0? mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)), 0L, "Bookmark id should be 0 for basic history entry"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.HISTORY_ID)), basicHistoryId, "Basic history entry has correct history id"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)), TITLE_1, "Basic history entry has correct title"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.URL)), URL_1, "Basic history entry has correct url"); mAsserter.is(c.getInt(c.getColumnIndex(BrowserContract.Combined.VISITS)), VISITS, "Basic history entry has correct number of visits"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.DATE_LAST_VISITED)), LAST_VISITED, "Basic history entry has correct last visit time"); // Second combined entry is basic bookmark entry mAsserter.is(c.moveToNext(), true, "Found basic bookmark entry"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined._ID)), 0L, "Combined _id column should always be 0"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)), basicBookmarkId, "Basic bookmark entry has correct bookmark id"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.HISTORY_ID)), -1L, "History id should be -1 for basic bookmark entry"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)), TITLE_2, "Basic bookmark entry has correct title"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.URL)), URL_2, "Basic bookmark entry has correct url"); mAsserter.is(c.getInt(c.getColumnIndex(BrowserContract.Combined.VISITS)), -1, "Visits should be -1 for basic bookmark entry"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.DATE_LAST_VISITED)), -1L, "Basic entry has correct last visit time"); // Third combined entry is a combined history/bookmark entry mAsserter.is(c.moveToNext(), true, "Found third combined entry"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined._ID)), 0L, "Combined _id column should always be 0"); // The bookmark data (bookmark_id and title) associated with the combined entry is non-deterministic, // it might end up with data coming from any of the matching bookmark entries. mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)) == combinedBookmarkId || c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)) == combinedBookmarkId2, true, "Combined entry has correct bookmark id"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)).equals(TITLE_3_BOOKMARK) || c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)).equals(TITLE_3_BOOKMARK2), true, "Combined entry has title corresponding to bookmark entry"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.HISTORY_ID)), combinedHistoryId, "Combined entry has correct history id"); mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.URL)), URL_3, "Combined entry has correct url"); mAsserter.is(c.getInt(c.getColumnIndex(BrowserContract.Combined.VISITS)), VISITS, "Combined entry has correct number of visits"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.DATE_LAST_VISITED)), LAST_VISITED, "Combined entry has correct last visit time"); } finally { c.close(); } } } private class TestCombinedViewDisplay extends TestCase { @Override public void test() throws Exception { final String TITLE_1 = "Test Page 1"; final String TITLE_2 = "Test Page 2"; final String TITLE_3_HISTORY = "Test Page 3 (History Entry)"; final String TITLE_3_BOOKMARK = "Test Page 3 (Bookmark Entry)"; final String URL_1 = "http://example.com"; final String URL_2 = "http://example.org"; final String URL_3 = "http://examples2.com"; final int VISITS = 10; final long LAST_VISITED = System.currentTimeMillis(); // Create a basic history entry ContentValues basicHistory = createHistoryEntry(TITLE_1, URL_1, VISITS, LAST_VISITED); ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, basicHistory)); // Create a basic bookmark entry ContentValues basicBookmark = createBookmark(TITLE_2, URL_2, mMobileFolderId, BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword"); mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, basicBookmark); // Create a history entry and bookmark entry with the same URL to // represent a visited bookmark ContentValues combinedHistory = createHistoryEntry(TITLE_3_HISTORY, URL_3, VISITS, LAST_VISITED); mProvider.insert(BrowserContract.History.CONTENT_URI, combinedHistory); ContentValues combinedBookmark = createBookmark(TITLE_3_BOOKMARK, URL_3, mMobileFolderId, BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword"); mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, combinedBookmark); final Cursor c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null); try { mAsserter.is(c.getCount(), 3, "3 combined entries found"); } finally { c.close(); } } } private class TestCombinedViewWithDeletedBookmark extends TestCase { @Override public void test() throws Exception { final String TITLE = "Test Page 1"; final String URL = "http://example.com"; final int VISITS = 10; final long LAST_VISITED = System.currentTimeMillis(); // Create a combined history entry ContentValues combinedHistory = createHistoryEntry(TITLE, URL, VISITS, LAST_VISITED); mProvider.insert(BrowserContract.History.CONTENT_URI, combinedHistory); // Create a combined bookmark entry ContentValues combinedBookmark = createBookmark(TITLE, URL, mMobileFolderId, BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword"); long combinedBookmarkId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, combinedBookmark)); Cursor c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null); mAsserter.is(c.getCount(), 1, "1 combined entry found"); mAsserter.is(c.moveToFirst(), true, "Found combined entry with bookmark id"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)), combinedBookmarkId, "Bookmark id should be set correctly on combined entry"); int deleted = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.Bookmarks._ID + " = ?", new String[] { String.valueOf(combinedBookmarkId) }); mAsserter.is((deleted == 1), true, "Inserted combined bookmark was deleted"); c.close(); c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null); mAsserter.is(c.getCount(), 1, "1 combined entry found"); mAsserter.is(c.moveToFirst(), true, "Found combined entry without bookmark id"); mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)), 0L, "Bookmark id should not be set to removed bookmark id"); c.close(); } } private class TestExpireHistory extends TestCase { private void createFakeHistory(long timeShift, int count) { // Insert a bunch of very new entries ContentValues[] allVals = new ContentValues[count]; long time = System.currentTimeMillis() - timeShift; for (int i = 0; i < count; i++) { allVals[i] = new ContentValues(); allVals[i].put(BrowserContract.History.TITLE, "Test " + i); allVals[i].put(BrowserContract.History.URL, "http://www.test.org/" + i); allVals[i].put(BrowserContract.History.VISITS, i); allVals[i].put(BrowserContract.History.DATE_LAST_VISITED, time); } int inserts = mProvider.bulkInsert(BrowserContract.History.CONTENT_URI, allVals); mAsserter.is(inserts, count, "Expected number of inserts matches"); // inserting a new entry sets the date created and modified automatically // reset all of them for (int i = 0; i < count; i++) { ContentValues cv = new ContentValues(); cv.put(BrowserContract.History.DATE_CREATED, time); cv.put(BrowserContract.History.DATE_MODIFIED, time); mProvider.update(BrowserContract.History.CONTENT_URI, cv, BrowserContract.History.URL + " = ?", new String[] { "http://www.test.org/" + i }); } Cursor c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null); assertCountIsAndClose(c, count, count + " history entries found"); // add thumbnails for each entry allVals = new ContentValues[count]; for (int i = 0; i < count; i++) { allVals[i] = new ContentValues(); allVals[i].put(BrowserContract.Thumbnails.DATA, i); allVals[i].put(BrowserContract.Thumbnails.URL, "http://www.test.org/" + i); } inserts = mProvider.bulkInsert(BrowserContract.Thumbnails.CONTENT_URI, allVals); mAsserter.is(inserts, count, "Expected number of inserts matches"); c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null); assertCountIsAndClose(c, count, count + " thumbnails entries found"); } @Override public void test() throws Exception { final int count = 3000; final int thumbCount = 15; // insert a bunch of new entries createFakeHistory(0, count); // expiring with a normal priority should not delete new entries Uri url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "NORMAL"); mProvider.delete(url, null, null); Cursor c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null); assertCountIsAndClose(c, count, count + " history entries found"); // expiring with a normal priority should delete all but 10 thumbnails c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null); assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found"); ensureEmptyDatabase(); // Insert a bunch of new entries. createFakeHistory(0, count); // Expiring with a aggressive priority should leave 500 entries. url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "AGGRESSIVE"); mProvider.delete(url, null, null); c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null); assertCountIsAndClose(c, 500, "500 history entries found"); // Expiring with a aggressive priority should delete all but 10 thumbnails. c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null); assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found"); ensureEmptyDatabase(); // Insert a bunch of entries with an old time created/modified. long time = 1000L * 60L * 60L * 24L * 30L * 3L; createFakeHistory(time, count); // Expiring with an normal priority should remove at most 1000 entries, // entries leaving at least 2000. url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "NORMAL"); mProvider.delete(url, null, null); c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null); assertCountIsAndClose(c, 2000, "2000 history entries found"); // Expiring with a normal priority should delete all but 10 thumbnails. c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null); assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found"); ensureEmptyDatabase(); // insert a bunch of entries with an old time created/modified time = 1000L * 60L * 60L * 24L * 30L * 3L; createFakeHistory(time, count); // Expiring with an aggressive priority should remove old // entries, leaving at least 500. url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "AGGRESSIVE"); mProvider.delete(url, null, null); c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null); assertCountIsAndClose(c, 500, "500 history entries found"); // expiring with an aggressive priority should delete all but 10 thumbnails c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null); assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found"); } } /* * Verify that insert, update, delete, and bulkInsert operations * notify the ambient content resolver. Each operation calls the * content resolver notifyChange method synchronously, so it is * okay to test sequentially. */ private class TestBrowserProviderNotifications extends TestCase { public static final String LOGTAG = "TestBPNotifications"; protected void ensureOnlyChangeNotifiedStartsWith(Uri expectedUri, String operation) { if (expectedUri == null) { throw new IllegalArgumentException("expectedUri must not be null"); } if (mResolver.notifyChangeList.size() != 1) { // Log to help post-mortem debugging Log.w(LOGTAG, "after operation, notifyChangeList = " + mResolver.notifyChangeList); } mAsserter.is((long) mResolver.notifyChangeList.size(), 1L, "Content observer was notified exactly once by " + operation); Uri uri = mResolver.notifyChangeList.poll(); mAsserter.isnot(uri, null, "Notification from " + operation + " was valid"); mAsserter.ok(uri.toString().startsWith(expectedUri.toString()), "Content observer was notified exactly once by " + operation, uri.toString() + " starts with expected prefix " + expectedUri); } @Override public void test() throws Exception { // Insert final ContentValues h = createOneHistoryEntry(); mResolver.notifyChangeList.clear(); long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h)); mAsserter.isnot(id, -1L, "Inserted item has valid id"); ensureOnlyChangeNotifiedStartsWith(BrowserContract.History.CONTENT_URI, "insert"); // Update mResolver.notifyChangeList.clear(); h.put(BrowserContract.History.TITLE, "http://newexample.com"); long numUpdated = mProvider.update(BrowserContract.History.CONTENT_URI, h, BrowserContract.History._ID + " = ?", new String[] { String.valueOf(id) }); mAsserter.is(numUpdated, 1L, "Correct number of items are updated"); ensureOnlyChangeNotifiedStartsWith(BrowserContract.History.CONTENT_URI, "update"); // Delete mResolver.notifyChangeList.clear(); long numDeleted = mProvider.delete(BrowserContract.History.CONTENT_URI, null, null); mAsserter.is(numDeleted, 1L, "Correct number of items are deleted"); ensureOnlyChangeNotifiedStartsWith(BrowserContract.History.CONTENT_URI, "delete"); // Bulk insert final ContentValues[] hs = new ContentValues[] { createOneHistoryEntry() }; mResolver.notifyChangeList.clear(); long numBulkInserted = mProvider.bulkInsert(BrowserContract.History.CONTENT_URI, hs); mAsserter.is(numBulkInserted, 1L, "Correct number of items are bulkInserted"); ensureOnlyChangeNotifiedStartsWith(BrowserContract.History.CONTENT_URI, "bulkInsert"); } } /** * Assert that the provided cursor has the expected number of rows, * closing the cursor afterwards. */ private void assertCountIsAndClose(Cursor c, int expectedCount, String message) { try { mAsserter.is(c.getCount(), expectedCount, message); } finally { c.close(); } } }