mobile/android/base/tests/testBrowserProvider.java

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mobile/android/base/tests/testBrowserProvider.java	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1859 @@
     1.4 +package org.mozilla.gecko.tests;
     1.5 +
     1.6 +import java.util.ArrayList;
     1.7 +import java.util.Random;
     1.8 +
     1.9 +import org.mozilla.gecko.db.BrowserContract;
    1.10 +
    1.11 +import android.content.ContentProviderOperation;
    1.12 +import android.content.ContentProviderResult;
    1.13 +import android.content.ContentUris;
    1.14 +import android.content.ContentValues;
    1.15 +import android.content.OperationApplicationException;
    1.16 +import android.database.Cursor;
    1.17 +import android.net.Uri;
    1.18 +import android.os.Build;
    1.19 +import android.util.Log;
    1.20 +
    1.21 +/*
    1.22 + * This test is meant to exercise all operations exposed by Fennec's
    1.23 + * history and bookmarks content provider. It does so in an isolated
    1.24 + * environment (see ContentProviderTest) without affecting any UI-related
    1.25 + * code.
    1.26 + */
    1.27 +public class testBrowserProvider extends ContentProviderTest {
    1.28 +    private long mMobileFolderId;
    1.29 +
    1.30 +    private void loadMobileFolderId() throws Exception {
    1.31 +        Cursor c = null;
    1.32 +        try {
    1.33 +            c = getBookmarkByGuid(BrowserContract.Bookmarks.MOBILE_FOLDER_GUID);
    1.34 +            mAsserter.is(c.moveToFirst(), true, "Mobile bookmarks folder is present");
    1.35 +
    1.36 +            mMobileFolderId = c.getLong(c.getColumnIndex(BrowserContract.Bookmarks._ID));
    1.37 +        } finally {
    1.38 +            if (c != null) {
    1.39 +                c.close();
    1.40 +            }
    1.41 +        }
    1.42 +    }
    1.43 +
    1.44 +    private void ensureEmptyDatabase() throws Exception {
    1.45 +        Cursor c = null;
    1.46 +
    1.47 +        String guid = BrowserContract.Bookmarks.GUID;
    1.48 +
    1.49 +        mProvider.delete(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"),
    1.50 +                         guid + " != ? AND " +
    1.51 +                         guid + " != ? AND " +
    1.52 +                         guid + " != ? AND " +
    1.53 +                         guid + " != ? AND " +
    1.54 +                         guid + " != ? AND " +
    1.55 +                         guid + " != ? AND " +
    1.56 +                         guid + " != ?",
    1.57 +                         new String[] { BrowserContract.Bookmarks.PLACES_FOLDER_GUID,
    1.58 +                                        BrowserContract.Bookmarks.MOBILE_FOLDER_GUID,
    1.59 +                                        BrowserContract.Bookmarks.MENU_FOLDER_GUID,
    1.60 +                                        BrowserContract.Bookmarks.TAGS_FOLDER_GUID,
    1.61 +                                        BrowserContract.Bookmarks.TOOLBAR_FOLDER_GUID,
    1.62 +                                        BrowserContract.Bookmarks.UNFILED_FOLDER_GUID,
    1.63 +                                        BrowserContract.Bookmarks.READING_LIST_FOLDER_GUID });
    1.64 +
    1.65 +        c = mProvider.query(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), null, null, null, null);
    1.66 +        assertCountIsAndClose(c, 7, "All non-special bookmarks and folders were deleted");
    1.67 +
    1.68 +        mProvider.delete(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), null, null);
    1.69 +        c = mProvider.query(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), null, null, null, null);
    1.70 +        assertCountIsAndClose(c, 0, "All history entries were deleted");
    1.71 +
    1.72 +        /**
    1.73 +         * There's no reason why the following two parts should fail.
    1.74 +         * But sometimes they do, and I'm not going to spend the time
    1.75 +         * to figure out why in an unrelated bug.
    1.76 +         */
    1.77 +
    1.78 +        mProvider.delete(appendUriParam(BrowserContract.Favicons.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), null, null);
    1.79 +        c = mProvider.query(appendUriParam(BrowserContract.Favicons.CONTENT_URI,
    1.80 +                                           BrowserContract.PARAM_SHOW_DELETED, "1"),
    1.81 +                                           null, null, null, null);
    1.82 +
    1.83 +        if (c.getCount() > 0) {
    1.84 +          mAsserter.dumpLog("Unexpected favicons in ensureEmptyDatabase.");
    1.85 +        }
    1.86 +        c.close();
    1.87 +
    1.88 +        mAsserter.dumpLog("ensureEmptyDatabase: Favicon deletion completed."); // Bug 968951 debug.
    1.89 +        // assertCountIsAndClose(c, 0, "All favicons were deleted");
    1.90 +
    1.91 +        mProvider.delete(appendUriParam(BrowserContract.Thumbnails.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), null, null);
    1.92 +        c = mProvider.query(appendUriParam(BrowserContract.Thumbnails.CONTENT_URI,
    1.93 +                                           BrowserContract.PARAM_SHOW_DELETED, "1"),
    1.94 +                                           null, null, null, null);
    1.95 +
    1.96 +        if (c.getCount() > 0) {
    1.97 +          mAsserter.dumpLog("Unexpected thumbnails in ensureEmptyDatabase.");
    1.98 +        }
    1.99 +        c.close();
   1.100 +
   1.101 +        mAsserter.dumpLog("ensureEmptyDatabase: Thumbnail deletion completed."); // Bug 968951 debug.
   1.102 +        // assertCountIsAndClose(c, 0, "All thumbnails were deleted");
   1.103 +    }
   1.104 +
   1.105 +    private ContentValues createBookmark(String title, String url, long parentId,
   1.106 +            int type, int position, String tags, String description, String keyword) throws Exception {
   1.107 +        ContentValues bookmark = new ContentValues();
   1.108 +
   1.109 +        bookmark.put(BrowserContract.Bookmarks.TITLE, title);
   1.110 +        bookmark.put(BrowserContract.Bookmarks.URL, url);
   1.111 +        bookmark.put(BrowserContract.Bookmarks.PARENT, parentId);
   1.112 +        bookmark.put(BrowserContract.Bookmarks.TYPE, type);
   1.113 +        bookmark.put(BrowserContract.Bookmarks.POSITION, position);
   1.114 +        bookmark.put(BrowserContract.Bookmarks.TAGS, tags);
   1.115 +        bookmark.put(BrowserContract.Bookmarks.DESCRIPTION, description);
   1.116 +        bookmark.put(BrowserContract.Bookmarks.KEYWORD, keyword);
   1.117 +
   1.118 +        return bookmark;
   1.119 +    }
   1.120 +
   1.121 +    private ContentValues createOneBookmark() throws Exception {
   1.122 +        return createBookmark("Example", "http://example.com", mMobileFolderId,
   1.123 +                BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
   1.124 +    }
   1.125 +
   1.126 +    private Cursor getBookmarksByParent(long parent) throws Exception {
   1.127 +        // Order by position.
   1.128 +        return mProvider.query(BrowserContract.Bookmarks.CONTENT_URI, null,
   1.129 +                               BrowserContract.Bookmarks.PARENT + " = ?",
   1.130 +                               new String[] { String.valueOf(parent) },
   1.131 +                               BrowserContract.Bookmarks.POSITION);
   1.132 +    }
   1.133 +
   1.134 +    private Cursor getBookmarkByGuid(String guid) throws Exception {
   1.135 +        return mProvider.query(BrowserContract.Bookmarks.CONTENT_URI, null,
   1.136 +                               BrowserContract.Bookmarks.GUID + " = ?",
   1.137 +                               new String[] { guid },
   1.138 +                               null);
   1.139 +    }
   1.140 +
   1.141 +    private Cursor getBookmarkById(long id) throws Exception {
   1.142 +        return getBookmarkById(BrowserContract.Bookmarks.CONTENT_URI, id, null);
   1.143 +    }
   1.144 +
   1.145 +    private Cursor getBookmarkById(long id, String[] projection) throws Exception {
   1.146 +        return getBookmarkById(BrowserContract.Bookmarks.CONTENT_URI, id, projection);
   1.147 +    }
   1.148 +
   1.149 +    private Cursor getBookmarkById(Uri bookmarksUri, long id) throws Exception {
   1.150 +        return getBookmarkById(bookmarksUri, id, null);
   1.151 +    }
   1.152 +
   1.153 +    private Cursor getBookmarkById(Uri bookmarksUri, long id, String[] projection) throws Exception {
   1.154 +        return mProvider.query(bookmarksUri, projection,
   1.155 +                               BrowserContract.Bookmarks._ID + " = ?",
   1.156 +                               new String[] { String.valueOf(id) },
   1.157 +                               null);
   1.158 +    }
   1.159 +
   1.160 +    private ContentValues createHistoryEntry(String title, String url, int visits, long lastVisited) throws Exception {
   1.161 +        ContentValues historyEntry = new ContentValues();
   1.162 +
   1.163 +        historyEntry.put(BrowserContract.History.TITLE, title);
   1.164 +        historyEntry.put(BrowserContract.History.URL, url);
   1.165 +        historyEntry.put(BrowserContract.History.VISITS, visits);
   1.166 +        historyEntry.put(BrowserContract.History.DATE_LAST_VISITED, lastVisited);
   1.167 +
   1.168 +        return historyEntry;
   1.169 +    }
   1.170 +
   1.171 +    private ContentValues createFaviconEntry(String pageUrl, String data) throws Exception {
   1.172 +        ContentValues faviconEntry = new ContentValues();
   1.173 +
   1.174 +        faviconEntry.put(BrowserContract.Favicons.PAGE_URL, pageUrl);
   1.175 +        faviconEntry.put(BrowserContract.Favicons.URL, pageUrl + "/favicon.ico");
   1.176 +        faviconEntry.put(BrowserContract.Favicons.DATA, data.getBytes("UTF8"));
   1.177 +
   1.178 +        return faviconEntry;
   1.179 +    }
   1.180 +
   1.181 +    private ContentValues createThumbnailEntry(String pageUrl, String data) throws Exception {
   1.182 +        ContentValues thumbnailEntry = new ContentValues();
   1.183 +
   1.184 +        thumbnailEntry.put(BrowserContract.Thumbnails.URL, pageUrl);
   1.185 +        thumbnailEntry.put(BrowserContract.Thumbnails.DATA, data.getBytes("UTF8"));
   1.186 +
   1.187 +        return thumbnailEntry;
   1.188 +    }
   1.189 +
   1.190 +    private ContentValues createOneHistoryEntry() throws Exception {
   1.191 +        return createHistoryEntry("Example", "http://example.com", 10, System.currentTimeMillis());
   1.192 +    }
   1.193 +
   1.194 +    private Cursor getHistoryEntryById(long id) throws Exception {
   1.195 +        return getHistoryEntryById(BrowserContract.History.CONTENT_URI, id, null);
   1.196 +    }
   1.197 +
   1.198 +    private Cursor getHistoryEntryById(long id, String[] projection) throws Exception {
   1.199 +        return getHistoryEntryById(BrowserContract.History.CONTENT_URI, id, projection);
   1.200 +    }
   1.201 +
   1.202 +    private Cursor getHistoryEntryById(Uri historyUri, long id) throws Exception {
   1.203 +        return getHistoryEntryById(historyUri, id, null);
   1.204 +    }
   1.205 +
   1.206 +    private Cursor getHistoryEntryById(Uri historyUri, long id, String[] projection) throws Exception {
   1.207 +        return mProvider.query(historyUri, projection,
   1.208 +                               BrowserContract.History._ID + " = ?",
   1.209 +                               new String[] { String.valueOf(id) },
   1.210 +                               null);
   1.211 +    }
   1.212 +
   1.213 +    private Cursor getFaviconsByUrl(String url) throws Exception {
   1.214 +        return mProvider.query(BrowserContract.Combined.CONTENT_URI, null,
   1.215 +                               BrowserContract.Combined.URL + " = ?",
   1.216 +                               new String[] { url },
   1.217 +                               null);
   1.218 +    }
   1.219 +
   1.220 +    private Cursor getThumbnailByUrl(String url) throws Exception {
   1.221 +        return mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null,
   1.222 +                               BrowserContract.Thumbnails.URL + " = ?",
   1.223 +                               new String[] { url },
   1.224 +                               null);
   1.225 +    }
   1.226 +
   1.227 +    @Override
   1.228 +    public void setUp() throws Exception {
   1.229 +        super.setUp(sBrowserProviderCallable, BrowserContract.AUTHORITY, "browser.db");
   1.230 +
   1.231 +        mTests.add(new TestSpecialFolders());
   1.232 +
   1.233 +        mTests.add(new TestInsertBookmarks());
   1.234 +        mTests.add(new TestInsertBookmarksFavicons());
   1.235 +        mTests.add(new TestDeleteBookmarks());
   1.236 +        mTests.add(new TestDeleteBookmarksFavicons());
   1.237 +        mTests.add(new TestUpdateBookmarks());
   1.238 +        mTests.add(new TestUpdateBookmarksFavicons());
   1.239 +        mTests.add(new TestPositionBookmarks());
   1.240 +
   1.241 +        mTests.add(new TestInsertHistory());
   1.242 +        mTests.add(new TestInsertHistoryFavicons());
   1.243 +        mTests.add(new TestDeleteHistory());
   1.244 +        mTests.add(new TestDeleteHistoryFavicons());
   1.245 +        mTests.add(new TestUpdateHistory());
   1.246 +        mTests.add(new TestUpdateHistoryFavicons());
   1.247 +        mTests.add(new TestUpdateOrInsertHistory());
   1.248 +        mTests.add(new TestInsertHistoryThumbnails());
   1.249 +        mTests.add(new TestUpdateHistoryThumbnails());
   1.250 +        mTests.add(new TestDeleteHistoryThumbnails());
   1.251 +
   1.252 +        mTests.add(new TestBatchOperations());
   1.253 +
   1.254 +        mTests.add(new TestCombinedView());
   1.255 +        mTests.add(new TestCombinedViewDisplay());
   1.256 +        mTests.add(new TestCombinedViewWithDeletedBookmark());
   1.257 +        mTests.add(new TestCombinedViewWithDeletedReadingListItem());
   1.258 +        mTests.add(new TestExpireHistory());
   1.259 +
   1.260 +        mTests.add(new TestBrowserProviderNotifications());
   1.261 +    }
   1.262 +
   1.263 +    public void testBrowserProvider() throws Exception {
   1.264 +        loadMobileFolderId();
   1.265 +
   1.266 +        for (int i = 0; i < mTests.size(); i++) {
   1.267 +            Runnable test = mTests.get(i);
   1.268 +
   1.269 +            final String testName = test.getClass().getSimpleName();
   1.270 +            setTestName(testName);
   1.271 +            ensureEmptyDatabase();
   1.272 +            mAsserter.dumpLog("testBrowserProvider: Database empty - Starting " + testName + ".");
   1.273 +            test.run();
   1.274 +        }
   1.275 +    }
   1.276 +
   1.277 +    private class TestBatchOperations extends TestCase {
   1.278 +        static final int TESTCOUNT = 100;
   1.279 +
   1.280 +        public void testApplyBatch() throws Exception {
   1.281 +            ArrayList<ContentProviderOperation> mOperations
   1.282 +                = new ArrayList<ContentProviderOperation>();
   1.283 +
   1.284 +            // Test a bunch of inserts with applyBatch
   1.285 +            ContentValues values = new ContentValues();
   1.286 +            ContentProviderOperation.Builder builder = null;
   1.287 +
   1.288 +            for (int i = 0; i < TESTCOUNT; i++) {
   1.289 +                values.clear();
   1.290 +                values.put(BrowserContract.History.VISITS, i);
   1.291 +                values.put(BrowserContract.History.TITLE, "Test" + i);
   1.292 +                values.put(BrowserContract.History.URL, "http://www.test.org/" + i);
   1.293 +
   1.294 +                // Insert
   1.295 +                builder = ContentProviderOperation.newInsert(BrowserContract.History.CONTENT_URI);
   1.296 +                builder.withValues(values);
   1.297 +                // Queue the operation
   1.298 +                mOperations.add(builder.build());
   1.299 +            }
   1.300 +
   1.301 +            ContentProviderResult[] applyResult =
   1.302 +                mProvider.applyBatch(mOperations);
   1.303 +
   1.304 +            boolean allFound = true;
   1.305 +            for (int i = 0; i < TESTCOUNT; i++) {
   1.306 +                Cursor cursor = mProvider.query(BrowserContract.History.CONTENT_URI,
   1.307 +                                                null,
   1.308 +                                                BrowserContract.History.URL + " = ?",
   1.309 +                                                new String[] { "http://www.test.org/" + i },
   1.310 +                                                null);
   1.311 +
   1.312 +                if (!cursor.moveToFirst())
   1.313 +                    allFound = false;
   1.314 +                cursor.close();
   1.315 +            }
   1.316 +            mAsserter.is(allFound, true, "Found all batchApply entries");
   1.317 +            mOperations.clear();
   1.318 +
   1.319 +            // Update all visits to 1
   1.320 +            values.clear();
   1.321 +            values.put(BrowserContract.History.VISITS, 1);
   1.322 +            for (int i = 0; i < TESTCOUNT; i++) {
   1.323 +                builder = ContentProviderOperation.newUpdate(BrowserContract.History.CONTENT_URI);
   1.324 +                builder.withSelection(BrowserContract.History.URL  + " = ?",
   1.325 +                                      new String[] {"http://www.test.org/" + i});
   1.326 +                builder.withValues(values);
   1.327 +                builder.withExpectedCount(1);
   1.328 +                // Queue the operation
   1.329 +                mOperations.add(builder.build());
   1.330 +            }
   1.331 +
   1.332 +            boolean seenException = false;
   1.333 +            try {
   1.334 +                applyResult = mProvider.applyBatch(mOperations);
   1.335 +            } catch (OperationApplicationException ex) {
   1.336 +                seenException = true;
   1.337 +            }
   1.338 +            mAsserter.is(seenException, false, "Batch updating succeded");
   1.339 +            mOperations.clear();
   1.340 +
   1.341 +            // Delete all visits
   1.342 +            for (int i = 0; i < TESTCOUNT; i++) {
   1.343 +                builder = ContentProviderOperation.newDelete(BrowserContract.History.CONTENT_URI);
   1.344 +                builder.withSelection(BrowserContract.History.URL  + " = ?",
   1.345 +                                      new String[] {"http://www.test.org/" + i});
   1.346 +                builder.withExpectedCount(1);
   1.347 +                // Queue the operation
   1.348 +                mOperations.add(builder.build());
   1.349 +            }
   1.350 +            try {
   1.351 +                applyResult = mProvider.applyBatch(mOperations);
   1.352 +            } catch (OperationApplicationException ex) {
   1.353 +                seenException = true;
   1.354 +            }
   1.355 +            mAsserter.is(seenException, false, "Batch deletion succeeded");
   1.356 +        }
   1.357 +
   1.358 +        // Force a Constraint error, see if later operations still apply correctly
   1.359 +        public void testApplyBatchErrors() throws Exception {
   1.360 +            ArrayList<ContentProviderOperation> mOperations
   1.361 +                = new ArrayList<ContentProviderOperation>();
   1.362 +
   1.363 +            // Test a bunch of inserts with applyBatch
   1.364 +            ContentProviderOperation.Builder builder = null;
   1.365 +            ContentValues values = createFaviconEntry("http://www.test.org", "FAVICON");
   1.366 +            builder = ContentProviderOperation.newInsert(BrowserContract.Favicons.CONTENT_URI);
   1.367 +            builder.withValues(values);
   1.368 +            mOperations.add(builder.build());
   1.369 +
   1.370 +            // Make a duplicate, this will fail because of a UNIQUE constraint
   1.371 +            builder = ContentProviderOperation.newInsert(BrowserContract.Favicons.CONTENT_URI);
   1.372 +            builder.withValues(values);
   1.373 +            mOperations.add(builder.build());
   1.374 +
   1.375 +            // This is valid and should be in the table afterwards
   1.376 +            values.put(BrowserContract.Favicons.URL, "http://www.test.org/valid.ico");
   1.377 +            builder = ContentProviderOperation.newInsert(BrowserContract.Favicons.CONTENT_URI);
   1.378 +            builder.withValues(values);
   1.379 +            mOperations.add(builder.build());
   1.380 +
   1.381 +            boolean seenException = false;
   1.382 +
   1.383 +            try {
   1.384 +                ContentProviderResult[] applyResult =
   1.385 +                    mProvider.applyBatch(mOperations);
   1.386 +            } catch (OperationApplicationException ex) {
   1.387 +                seenException = true;
   1.388 +            }
   1.389 +
   1.390 +            // This test may need to go away if Bug 717428 is fixed.
   1.391 +            mAsserter.is(seenException, true, "Expected failure in favicons table");
   1.392 +
   1.393 +            boolean allFound = true;
   1.394 +            Cursor cursor = mProvider.query(BrowserContract.Favicons.CONTENT_URI,
   1.395 +                                            null,
   1.396 +                                            BrowserContract.Favicons.URL + " = ?",
   1.397 +                                            new String[] { "http://www.test.org/valid.ico" },
   1.398 +                                            null);
   1.399 +
   1.400 +            if (!cursor.moveToFirst())
   1.401 +                allFound = false;
   1.402 +            cursor.close();
   1.403 +
   1.404 +            mAsserter.is(allFound, true, "Found all applyBatch (with error) entries");
   1.405 +        }
   1.406 +
   1.407 +        public void testBulkInsert() throws Exception {
   1.408 +            // Test a bunch of inserts with bulkInsert
   1.409 +            ContentValues allVals[] = new ContentValues[TESTCOUNT];
   1.410 +            for (int i = 0; i < TESTCOUNT; i++) {
   1.411 +                allVals[i] = new ContentValues();
   1.412 +                allVals[i].put(BrowserContract.History.URL, i);
   1.413 +                allVals[i].put(BrowserContract.History.TITLE, "Test" + i);
   1.414 +                allVals[i].put(BrowserContract.History.URL, "http://www.test.org/" + i);
   1.415 +            }
   1.416 +
   1.417 +            int inserts = mProvider.bulkInsert(BrowserContract.History.CONTENT_URI, allVals);
   1.418 +            mAsserter.is(inserts, TESTCOUNT, "Excepted number of inserts matches");
   1.419 +
   1.420 +            boolean allFound = true;
   1.421 +            for (int i = 0; i < TESTCOUNT; i++) {
   1.422 +                Cursor cursor = mProvider.query(BrowserContract.History.CONTENT_URI,
   1.423 +                                                null,
   1.424 +                                                BrowserContract.History.URL + " = ?",
   1.425 +                                                new String[] { "http://www.test.org/" + i },
   1.426 +                                                null);
   1.427 +
   1.428 +                if (!cursor.moveToFirst())
   1.429 +                    allFound = false;
   1.430 +                cursor.close();
   1.431 +            }
   1.432 +            mAsserter.is(allFound, true, "Found all bulkInsert entries");
   1.433 +        }
   1.434 +
   1.435 +        @Override
   1.436 +        public void test() throws Exception {
   1.437 +            testApplyBatch();
   1.438 +            // Clean up
   1.439 +            ensureEmptyDatabase();
   1.440 +
   1.441 +            testBulkInsert();
   1.442 +            ensureEmptyDatabase();
   1.443 +
   1.444 +            testApplyBatchErrors();
   1.445 +        }
   1.446 +    }
   1.447 +
   1.448 +    private class TestSpecialFolders extends TestCase {
   1.449 +        @Override
   1.450 +        public void test() throws Exception {
   1.451 +            Cursor c = mProvider.query(BrowserContract.Bookmarks.CONTENT_URI,
   1.452 +                                       new String[] { BrowserContract.Bookmarks._ID,
   1.453 +                                                      BrowserContract.Bookmarks.GUID,
   1.454 +                                                      BrowserContract.Bookmarks.PARENT },
   1.455 +                                       BrowserContract.Bookmarks.GUID + " = ? OR " +
   1.456 +                                       BrowserContract.Bookmarks.GUID + " = ? OR " +
   1.457 +                                       BrowserContract.Bookmarks.GUID + " = ? OR " +
   1.458 +                                       BrowserContract.Bookmarks.GUID + " = ? OR " +
   1.459 +                                       BrowserContract.Bookmarks.GUID + " = ? OR " +
   1.460 +                                       BrowserContract.Bookmarks.GUID + " = ? OR " +
   1.461 +                                       BrowserContract.Bookmarks.GUID + " = ?",
   1.462 +                                       new String[] { BrowserContract.Bookmarks.PLACES_FOLDER_GUID,
   1.463 +                                                      BrowserContract.Bookmarks.MOBILE_FOLDER_GUID,
   1.464 +                                                      BrowserContract.Bookmarks.MENU_FOLDER_GUID,
   1.465 +                                                      BrowserContract.Bookmarks.TAGS_FOLDER_GUID,
   1.466 +                                                      BrowserContract.Bookmarks.TOOLBAR_FOLDER_GUID,
   1.467 +                                                      BrowserContract.Bookmarks.UNFILED_FOLDER_GUID,
   1.468 +                                                      BrowserContract.Bookmarks.READING_LIST_FOLDER_GUID },
   1.469 +                                       null);
   1.470 +
   1.471 +            mAsserter.is(c.getCount(), 7, "Right number of special folders");
   1.472 +
   1.473 +            int rootId = BrowserContract.Bookmarks.FIXED_ROOT_ID;
   1.474 +            int readingListId = BrowserContract.Bookmarks.FIXED_READING_LIST_ID;
   1.475 +
   1.476 +            while (c.moveToNext()) {
   1.477 +                int id = c.getInt(c.getColumnIndex(BrowserContract.Bookmarks._ID));
   1.478 +                String guid = c.getString(c.getColumnIndex(BrowserContract.Bookmarks.GUID));
   1.479 +                int parentId = c.getInt(c.getColumnIndex(BrowserContract.Bookmarks.PARENT));
   1.480 +
   1.481 +                if (guid.equals(BrowserContract.Bookmarks.PLACES_FOLDER_GUID)) {
   1.482 +                    mAsserter.is(new Integer(id), new Integer(rootId), "The id of places folder is correct");
   1.483 +                } else if (guid.equals(BrowserContract.Bookmarks.READING_LIST_FOLDER_GUID)) {
   1.484 +                    mAsserter.is(new Integer(id), new Integer(readingListId), "The id of reading list folder is correct");
   1.485 +                }
   1.486 +
   1.487 +                mAsserter.is(new Integer(parentId), new Integer(rootId),
   1.488 +                             "The PARENT of the " + guid + " special folder is correct");
   1.489 +            }
   1.490 +
   1.491 +            c.close();
   1.492 +        }
   1.493 +    }
   1.494 +
   1.495 +    private class TestInsertBookmarks extends TestCase {
   1.496 +        private long insertWithNullCol(String colName) throws Exception {
   1.497 +            ContentValues b = createOneBookmark();
   1.498 +            b.putNull(colName);
   1.499 +            long id = -1;
   1.500 +
   1.501 +            try {
   1.502 +                id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
   1.503 +            } catch (Exception e) {}
   1.504 +
   1.505 +            return id;
   1.506 +        }
   1.507 +
   1.508 +        @Override
   1.509 +        public void test() throws Exception {
   1.510 +            ContentValues b = createOneBookmark();
   1.511 +            long id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
   1.512 +            Cursor c = getBookmarkById(id);
   1.513 +
   1.514 +            mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
   1.515 +
   1.516 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TITLE)), b.getAsString(BrowserContract.Bookmarks.TITLE),
   1.517 +                         "Inserted bookmark has correct title");
   1.518 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.URL)), b.getAsString(BrowserContract.Bookmarks.URL),
   1.519 +                         "Inserted bookmark has correct URL");
   1.520 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TAGS)), b.getAsString(BrowserContract.Bookmarks.TAGS),
   1.521 +                         "Inserted bookmark has correct tags");
   1.522 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.KEYWORD)), b.getAsString(BrowserContract.Bookmarks.KEYWORD),
   1.523 +                         "Inserted bookmark has correct keyword");
   1.524 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.DESCRIPTION)), b.getAsString(BrowserContract.Bookmarks.DESCRIPTION),
   1.525 +                         "Inserted bookmark has correct description");
   1.526 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.POSITION)), b.getAsString(BrowserContract.Bookmarks.POSITION),
   1.527 +                         "Inserted bookmark has correct position");
   1.528 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TYPE)), b.getAsString(BrowserContract.Bookmarks.TYPE),
   1.529 +                         "Inserted bookmark has correct type");
   1.530 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.PARENT)), b.getAsString(BrowserContract.Bookmarks.PARENT),
   1.531 +                         "Inserted bookmark has correct parent ID");
   1.532 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.IS_DELETED)), String.valueOf(0),
   1.533 +                         "Inserted bookmark has correct is-deleted state");
   1.534 +
   1.535 +            id = insertWithNullCol(BrowserContract.Bookmarks.POSITION);
   1.536 +            mAsserter.is(new Long(id), new Long(-1),
   1.537 +                         "Should not be able to insert bookmark with null position");
   1.538 +
   1.539 +            id = insertWithNullCol(BrowserContract.Bookmarks.TYPE);
   1.540 +            mAsserter.is(new Long(id), new Long(-1),
   1.541 +                         "Should not be able to insert bookmark with null type");
   1.542 +
   1.543 +            if (Build.VERSION.SDK_INT >= 8 &&
   1.544 +                Build.VERSION.SDK_INT < 16) {
   1.545 +                b = createOneBookmark();
   1.546 +                b.put(BrowserContract.Bookmarks.PARENT, -1);
   1.547 +                id = -1;
   1.548 +
   1.549 +                try {
   1.550 +                    id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
   1.551 +                } catch (Exception e) {}
   1.552 +
   1.553 +                mAsserter.is(new Long(id), new Long(-1),
   1.554 +                             "Should not be able to insert bookmark with invalid parent");
   1.555 +            }
   1.556 +
   1.557 +            b = createOneBookmark();
   1.558 +            b.remove(BrowserContract.Bookmarks.TYPE);
   1.559 +            id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
   1.560 +            c = getBookmarkById(id);
   1.561 +
   1.562 +            mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
   1.563 +
   1.564 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TYPE)), String.valueOf(BrowserContract.Bookmarks.TYPE_BOOKMARK),
   1.565 +                         "Inserted bookmark has correct default type");
   1.566 +            c.close();
   1.567 +        }
   1.568 +    }
   1.569 +
   1.570 +    private class TestInsertBookmarksFavicons extends TestCase {
   1.571 +        @Override
   1.572 +        public void test() throws Exception {
   1.573 +            ContentValues b = createOneBookmark();
   1.574 +
   1.575 +            final String favicon = "FAVICON";
   1.576 +            final String pageUrl = b.getAsString(BrowserContract.Bookmarks.URL);
   1.577 +
   1.578 +            long id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
   1.579 +
   1.580 +            // Insert the favicon into the favicons table
   1.581 +            mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, favicon));
   1.582 +
   1.583 +            Cursor c = getBookmarkById(id, new String[] { BrowserContract.Bookmarks.FAVICON });
   1.584 +
   1.585 +            mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
   1.586 +
   1.587 +            mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Bookmarks.FAVICON)), "UTF8"),
   1.588 +                         favicon, "Inserted bookmark has corresponding favicon image");
   1.589 +            c.close();
   1.590 +
   1.591 +            c = getFaviconsByUrl(pageUrl);
   1.592 +            mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
   1.593 +
   1.594 +            mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
   1.595 +                         favicon, "Inserted favicon has corresponding favicon image");
   1.596 +            c.close();
   1.597 +        }
   1.598 +    }
   1.599 +
   1.600 +    private class TestDeleteBookmarks extends TestCase {
   1.601 +        private long insertOneBookmark() throws Exception {
   1.602 +            ContentValues b = createOneBookmark();
   1.603 +            long id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
   1.604 +
   1.605 +            Cursor c = getBookmarkById(id);
   1.606 +            mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
   1.607 +            c.close();
   1.608 +
   1.609 +            return id;
   1.610 +        }
   1.611 +
   1.612 +        @Override
   1.613 +        public void test() throws Exception {
   1.614 +            long id = insertOneBookmark();
   1.615 +
   1.616 +            int deleted = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI,
   1.617 +                                           BrowserContract.Bookmarks._ID + " = ?",
   1.618 +                                           new String[] { String.valueOf(id) });
   1.619 +
   1.620 +            mAsserter.is((deleted == 1), true, "Inserted bookmark was deleted");
   1.621 +
   1.622 +            Cursor c = getBookmarkById(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
   1.623 +            mAsserter.is(c.moveToFirst(), true, "Deleted bookmark was only marked as deleted");
   1.624 +            c.close();
   1.625 +
   1.626 +            deleted = mProvider.delete(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"),
   1.627 +                                       BrowserContract.Bookmarks._ID + " = ?",
   1.628 +                                       new String[] { String.valueOf(id) });
   1.629 +
   1.630 +            mAsserter.is((deleted == 1), true, "Inserted bookmark was deleted");
   1.631 +
   1.632 +            c = getBookmarkById(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
   1.633 +            mAsserter.is(c.moveToFirst(), false, "Inserted bookmark is now actually deleted");
   1.634 +            c.close();
   1.635 +
   1.636 +            id = insertOneBookmark();
   1.637 +
   1.638 +            deleted = mProvider.delete(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), null, null);
   1.639 +            mAsserter.is((deleted == 1), true,
   1.640 +                         "Inserted bookmark was deleted using URI with id");
   1.641 +
   1.642 +            c = getBookmarkById(id);
   1.643 +            mAsserter.is(c.moveToFirst(), false,
   1.644 +                         "Inserted bookmark can't be found after deletion using URI with ID");
   1.645 +            c.close();
   1.646 +
   1.647 +            if (Build.VERSION.SDK_INT >= 8 &&
   1.648 +                Build.VERSION.SDK_INT < 16) {
   1.649 +                ContentValues b = createBookmark("Folder", null, mMobileFolderId,
   1.650 +                        BrowserContract.Bookmarks.TYPE_FOLDER, 0, "folderTags", "folderDescription", "folderKeyword");
   1.651 +
   1.652 +                long parentId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
   1.653 +                c = getBookmarkById(parentId);
   1.654 +                mAsserter.is(c.moveToFirst(), true, "Inserted bookmarks folder found");
   1.655 +                c.close();
   1.656 +
   1.657 +                b = createBookmark("Example", "http://example.com", parentId,
   1.658 +                        BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
   1.659 +
   1.660 +                id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
   1.661 +                c = getBookmarkById(id);
   1.662 +                mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
   1.663 +                c.close();
   1.664 +
   1.665 +                deleted = 0;
   1.666 +                try {
   1.667 +                    Uri uri = ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, parentId);
   1.668 +                    deleted = mProvider.delete(appendUriParam(uri, BrowserContract.PARAM_IS_SYNC, "1"), null, null);
   1.669 +                } catch(Exception e) {}
   1.670 +
   1.671 +                mAsserter.is((deleted == 0), true,
   1.672 +                             "Should not be able to delete folder that causes orphan bookmarks");
   1.673 +            }
   1.674 +        }
   1.675 +    }
   1.676 +
   1.677 +    private class TestDeleteBookmarksFavicons extends TestCase {
   1.678 +        @Override
   1.679 +        public void test() throws Exception {
   1.680 +            ContentValues b = createOneBookmark();
   1.681 +
   1.682 +            final String pageUrl = b.getAsString(BrowserContract.Bookmarks.URL);
   1.683 +            long id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
   1.684 +
   1.685 +            // Insert the favicon into the favicons table
   1.686 +            mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, "FAVICON"));
   1.687 +
   1.688 +            Cursor c = getFaviconsByUrl(pageUrl);
   1.689 +            mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
   1.690 +            c.close();
   1.691 +
   1.692 +            mProvider.delete(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), null, null);
   1.693 +
   1.694 +            c = getFaviconsByUrl(pageUrl);
   1.695 +            mAsserter.is(c.moveToFirst(), false, "Favicon is deleted with last reference to it");
   1.696 +            c.close();
   1.697 +        }
   1.698 +    }
   1.699 +
   1.700 +    private class TestUpdateBookmarks extends TestCase {
   1.701 +        private int updateWithNullCol(long id, String colName) throws Exception {
   1.702 +            ContentValues u = new ContentValues();
   1.703 +            u.putNull(colName);
   1.704 +
   1.705 +            int updated = 0;
   1.706 +
   1.707 +            try {
   1.708 +                updated = mProvider.update(BrowserContract.Bookmarks.CONTENT_URI, u,
   1.709 +                                           BrowserContract.Bookmarks._ID + " = ?",
   1.710 +                                           new String[] { String.valueOf(id) });
   1.711 +            } catch (Exception e) {}
   1.712 +
   1.713 +            return updated;
   1.714 +        }
   1.715 +
   1.716 +        @Override
   1.717 +        public void test() throws Exception {
   1.718 +            ContentValues b = createOneBookmark();
   1.719 +            long id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
   1.720 +
   1.721 +            Cursor c = getBookmarkById(id);
   1.722 +            mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
   1.723 +
   1.724 +            long dateCreated = c.getLong(c.getColumnIndex(BrowserContract.Bookmarks.DATE_CREATED));
   1.725 +            long dateModified = c.getLong(c.getColumnIndex(BrowserContract.Bookmarks.DATE_MODIFIED));
   1.726 +
   1.727 +            ContentValues u = new ContentValues();
   1.728 +            u.put(BrowserContract.Bookmarks.TITLE, b.getAsString(BrowserContract.Bookmarks.TITLE) + "CHANGED");
   1.729 +            u.put(BrowserContract.Bookmarks.URL, b.getAsString(BrowserContract.Bookmarks.URL) + "/more/stuff");
   1.730 +            u.put(BrowserContract.Bookmarks.TAGS, b.getAsString(BrowserContract.Bookmarks.TAGS) + "CHANGED");
   1.731 +            u.put(BrowserContract.Bookmarks.DESCRIPTION, b.getAsString(BrowserContract.Bookmarks.DESCRIPTION) + "CHANGED");
   1.732 +            u.put(BrowserContract.Bookmarks.KEYWORD, b.getAsString(BrowserContract.Bookmarks.KEYWORD) + "CHANGED");
   1.733 +            u.put(BrowserContract.Bookmarks.TYPE, BrowserContract.Bookmarks.TYPE_FOLDER);
   1.734 +            u.put(BrowserContract.Bookmarks.POSITION, 10);
   1.735 +
   1.736 +            int updated = mProvider.update(BrowserContract.Bookmarks.CONTENT_URI, u,
   1.737 +                                           BrowserContract.Bookmarks._ID + " = ?",
   1.738 +                                           new String[] { String.valueOf(id) });
   1.739 +
   1.740 +            mAsserter.is((updated == 1), true, "Inserted bookmark was updated");
   1.741 +            c.close();
   1.742 +
   1.743 +            c = getBookmarkById(id);
   1.744 +            mAsserter.is(c.moveToFirst(), true, "Updated bookmark found");
   1.745 +
   1.746 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TITLE)), u.getAsString(BrowserContract.Bookmarks.TITLE),
   1.747 +                         "Inserted bookmark has correct title");
   1.748 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.URL)), u.getAsString(BrowserContract.Bookmarks.URL),
   1.749 +                         "Inserted bookmark has correct URL");
   1.750 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TAGS)), u.getAsString(BrowserContract.Bookmarks.TAGS),
   1.751 +                         "Inserted bookmark has correct tags");
   1.752 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.KEYWORD)), u.getAsString(BrowserContract.Bookmarks.KEYWORD),
   1.753 +                         "Inserted bookmark has correct keyword");
   1.754 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.DESCRIPTION)), u.getAsString(BrowserContract.Bookmarks.DESCRIPTION),
   1.755 +                         "Inserted bookmark has correct description");
   1.756 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.POSITION)), u.getAsString(BrowserContract.Bookmarks.POSITION),
   1.757 +                         "Inserted bookmark has correct position");
   1.758 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TYPE)), u.getAsString(BrowserContract.Bookmarks.TYPE),
   1.759 +                         "Inserted bookmark has correct type");
   1.760 +
   1.761 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Bookmarks.DATE_CREATED))),
   1.762 +                         new Long(dateCreated),
   1.763 +                         "Updated bookmark has same creation date");
   1.764 +
   1.765 +            mAsserter.isnot(new Long(c.getLong(c.getColumnIndex(BrowserContract.Bookmarks.DATE_MODIFIED))),
   1.766 +                            new Long(dateModified),
   1.767 +                            "Updated bookmark has new modification date");
   1.768 +
   1.769 +            updated = updateWithNullCol(id, BrowserContract.Bookmarks.POSITION);
   1.770 +            mAsserter.is((updated > 0), false,
   1.771 +                         "Should not be able to update bookmark with null position");
   1.772 +
   1.773 +            updated = updateWithNullCol(id, BrowserContract.Bookmarks.TYPE);
   1.774 +            mAsserter.is((updated > 0), false,
   1.775 +                         "Should not be able to update bookmark with null type");
   1.776 +
   1.777 +            u = new ContentValues();
   1.778 +            u.put(BrowserContract.Bookmarks.URL, "http://examples2.com");
   1.779 +
   1.780 +            updated = mProvider.update(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), u, null, null);
   1.781 +            c.close();
   1.782 +
   1.783 +            c = getBookmarkById(id);
   1.784 +            mAsserter.is(c.moveToFirst(), true, "Updated bookmark found");
   1.785 +
   1.786 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.URL)), u.getAsString(BrowserContract.Bookmarks.URL),
   1.787 +                         "Updated bookmark has correct URL using URI with id");
   1.788 +            c.close();
   1.789 +        }
   1.790 +    }
   1.791 +
   1.792 +    private class TestUpdateBookmarksFavicons extends TestCase {
   1.793 +        @Override
   1.794 +        public void test() throws Exception {
   1.795 +            ContentValues b = createOneBookmark();
   1.796 +
   1.797 +            final String favicon = "FAVICON";
   1.798 +            final String newFavicon = "NEW_FAVICON";
   1.799 +            final String pageUrl = b.getAsString(BrowserContract.Bookmarks.URL);
   1.800 +
   1.801 +            mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b);
   1.802 +
   1.803 +            // Insert the favicon into the favicons table
   1.804 +            ContentValues f = createFaviconEntry(pageUrl, favicon);
   1.805 +            long faviconId = ContentUris.parseId(mProvider.insert(BrowserContract.Favicons.CONTENT_URI, f));
   1.806 +
   1.807 +            Cursor c = getFaviconsByUrl(pageUrl);
   1.808 +            mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
   1.809 +
   1.810 +            mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
   1.811 +                         favicon, "Inserted favicon has corresponding favicon image");
   1.812 +
   1.813 +            ContentValues u = createFaviconEntry(pageUrl, newFavicon);
   1.814 +            mProvider.update(BrowserContract.Favicons.CONTENT_URI, u, null, null);
   1.815 +            c.close();
   1.816 +
   1.817 +            c = getFaviconsByUrl(pageUrl);
   1.818 +            mAsserter.is(c.moveToFirst(), true, "Updated favicon found");
   1.819 +
   1.820 +            mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
   1.821 +                         newFavicon, "Updated favicon has corresponding favicon image");
   1.822 +            c.close();
   1.823 +        }
   1.824 +    }
   1.825 +
   1.826 +    /**
   1.827 +     * Create a folder of one thousand and one bookmarks, then impose an order
   1.828 +     * on them.
   1.829 +     *
   1.830 +     * Verify that the reordering worked by querying.
   1.831 +     */
   1.832 +    private class TestPositionBookmarks extends TestCase {
   1.833 +
   1.834 +        public String makeGUID(final long in) {
   1.835 +            String part = String.valueOf(in);
   1.836 +            return "aaaaaaaaaaaa".substring(0, (12 - part.length())) + part;
   1.837 +        }
   1.838 +
   1.839 +        public void compareCursorToItems(final Cursor c, final String[] items, final int count) {
   1.840 +            mAsserter.is(c.moveToFirst(), true, "Folder has children.");
   1.841 +
   1.842 +            int posColumn = c.getColumnIndex(BrowserContract.Bookmarks.POSITION);
   1.843 +            int guidColumn = c.getColumnIndex(BrowserContract.Bookmarks.GUID);
   1.844 +            int i = 0;
   1.845 +
   1.846 +            while (!c.isAfterLast()) {
   1.847 +                String guid = c.getString(guidColumn);
   1.848 +                long pos = c.getLong(posColumn);
   1.849 +                if ((pos != i) || (guid == null) || (!guid.equals(items[i]))) {
   1.850 +                    mAsserter.is(pos, (long) i, "Position matches sequence.");
   1.851 +                    mAsserter.is(guid, items[i], "GUID matches sequence.");
   1.852 +                }
   1.853 +                ++i;
   1.854 +                c.moveToNext();
   1.855 +            }
   1.856 +
   1.857 +            mAsserter.is(i, count, "Folder has the right number of children.");
   1.858 +            c.close();
   1.859 +        }
   1.860 +
   1.861 +        public static final int NUMBER_OF_CHILDREN = 1001;
   1.862 +        @Override
   1.863 +        public void test() throws Exception {
   1.864 +            // Create the containing folder.
   1.865 +            ContentValues folder = createBookmark("FolderFolder", "", mMobileFolderId,
   1.866 +                                                  BrowserContract.Bookmarks.TYPE_FOLDER, 0, "",
   1.867 +                                                  "description", "keyword");
   1.868 +            folder.put(BrowserContract.Bookmarks.GUID, "folderfolder");
   1.869 +            long folderId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, folder));
   1.870 +
   1.871 +            mAsserter.dumpLog("TestPositionBookmarks: Folder inserted"); // Bug 968951 debug.
   1.872 +
   1.873 +            // Create the children.
   1.874 +            String[] items = new String[NUMBER_OF_CHILDREN];
   1.875 +
   1.876 +            // Reuse the same ContentValues.
   1.877 +            ContentValues item = createBookmark("Test Bookmark", "http://example.com", folderId,
   1.878 +                                                BrowserContract.Bookmarks.TYPE_FOLDER, 0, "",
   1.879 +                                                "description", "keyword");
   1.880 +
   1.881 +            for (int i = 0; i < NUMBER_OF_CHILDREN; ++i) {
   1.882 +                String guid = makeGUID(i);
   1.883 +                items[i] = guid;
   1.884 +                item.put(BrowserContract.Bookmarks.GUID, guid);
   1.885 +                item.put(BrowserContract.Bookmarks.POSITION, i);
   1.886 +                item.put(BrowserContract.Bookmarks.URL, "http://example.com/" + guid);
   1.887 +                item.put(BrowserContract.Bookmarks.TITLE, "Test Bookmark " + guid);
   1.888 +                mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, item);
   1.889 +            }
   1.890 +
   1.891 +            mAsserter.dumpLog("TestPositionBookmarks: Bookmarks inserted"); // Bug 968951 debug.
   1.892 +
   1.893 +            Cursor c;
   1.894 +
   1.895 +            // Verify insertion.
   1.896 +            c = getBookmarksByParent(folderId);
   1.897 +            mAsserter.dumpLog("TestPositionBookmarks: Got bookmarks by parent"); // Bug 968951 debug.
   1.898 +            compareCursorToItems(c, items, NUMBER_OF_CHILDREN);
   1.899 +            c.close();
   1.900 +
   1.901 +            // Now permute the items array.
   1.902 +            Random rand = new Random();
   1.903 +            for (int i = 0; i < NUMBER_OF_CHILDREN; ++i) {
   1.904 +                final int newPosition = rand.nextInt(NUMBER_OF_CHILDREN);
   1.905 +                final String switched = items[newPosition];
   1.906 +                items[newPosition] = items[i];
   1.907 +                items[i] = switched;
   1.908 +            }
   1.909 +
   1.910 +            // Impose the positions.
   1.911 +            long updated = mProvider.update(BrowserContract.Bookmarks.POSITIONS_CONTENT_URI, null, null, items);
   1.912 +            mAsserter.is(updated, (long) NUMBER_OF_CHILDREN, "Updated " + NUMBER_OF_CHILDREN + " positions.");
   1.913 +
   1.914 +            // Verify that the database was updated.
   1.915 +            c = getBookmarksByParent(folderId);
   1.916 +            compareCursorToItems(c, items, NUMBER_OF_CHILDREN);
   1.917 +            c.close();
   1.918 +        }
   1.919 +    }
   1.920 +
   1.921 +    private class TestInsertHistory extends TestCase {
   1.922 +        private long insertWithNullCol(String colName) throws Exception {
   1.923 +            ContentValues h = createOneHistoryEntry();
   1.924 +            h.putNull(colName);
   1.925 +            long id = -1;
   1.926 +
   1.927 +            try {
   1.928 +                id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h));
   1.929 +            } catch (Exception e) {}
   1.930 +
   1.931 +            return id;
   1.932 +        }
   1.933 +
   1.934 +        @Override
   1.935 +        public void test() throws Exception {
   1.936 +            ContentValues h = createOneHistoryEntry();
   1.937 +            long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h));
   1.938 +            Cursor c = getHistoryEntryById(id);
   1.939 +
   1.940 +            mAsserter.is(c.moveToFirst(), true, "Inserted history entry found");
   1.941 +
   1.942 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), h.getAsString(BrowserContract.History.TITLE),
   1.943 +                         "Inserted history entry has correct title");
   1.944 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), h.getAsString(BrowserContract.History.URL),
   1.945 +                         "Inserted history entry has correct URL");
   1.946 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.VISITS)), h.getAsString(BrowserContract.History.VISITS),
   1.947 +                         "Inserted history entry has correct number of visits");
   1.948 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.DATE_LAST_VISITED)), h.getAsString(BrowserContract.History.DATE_LAST_VISITED),
   1.949 +                         "Inserted history entry has correct last visited date");
   1.950 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.IS_DELETED)), String.valueOf(0),
   1.951 +                         "Inserted history entry has correct is-deleted state");
   1.952 +
   1.953 +            id = insertWithNullCol(BrowserContract.History.URL);
   1.954 +            mAsserter.is(new Long(id), new Long(-1),
   1.955 +                         "Should not be able to insert history with null URL");
   1.956 +
   1.957 +            id = insertWithNullCol(BrowserContract.History.VISITS);
   1.958 +            mAsserter.is(new Long(id), new Long(-1),
   1.959 +                         "Should not be able to insert history with null number of visits");
   1.960 +            c.close();
   1.961 +        }
   1.962 +    }
   1.963 +
   1.964 +    private class TestInsertHistoryFavicons extends TestCase {
   1.965 +        @Override
   1.966 +        public void test() throws Exception {
   1.967 +            ContentValues h = createOneHistoryEntry();
   1.968 +
   1.969 +            final String favicon = "FAVICON";
   1.970 +            final String pageUrl = h.getAsString(BrowserContract.History.URL);
   1.971 +
   1.972 +            long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h));
   1.973 +
   1.974 +            // Insert the favicon into the favicons table
   1.975 +            mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, favicon));
   1.976 +
   1.977 +            Cursor c = getHistoryEntryById(id, new String[] { BrowserContract.History.FAVICON });
   1.978 +
   1.979 +            mAsserter.is(c.moveToFirst(), true, "Inserted history entry found");
   1.980 +
   1.981 +            mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.History.FAVICON)), "UTF8"),
   1.982 +                         favicon, "Inserted history entry has corresponding favicon image");
   1.983 +            c.close();
   1.984 +
   1.985 +            c = getFaviconsByUrl(pageUrl);
   1.986 +            mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
   1.987 +
   1.988 +            mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
   1.989 +                         favicon, "Inserted favicon has corresponding favicon image");
   1.990 +            c.close();
   1.991 +        }
   1.992 +    }
   1.993 +
   1.994 +    private class TestDeleteHistory extends TestCase {
   1.995 +        private long insertOneHistoryEntry() throws Exception {
   1.996 +            ContentValues h = createOneHistoryEntry();
   1.997 +            long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h));
   1.998 +
   1.999 +            Cursor c = getHistoryEntryById(id);
  1.1000 +            mAsserter.is(c.moveToFirst(), true, "Inserted history entry found");
  1.1001 +            c.close();
  1.1002 +
  1.1003 +            return id;
  1.1004 +        }
  1.1005 +
  1.1006 +        @Override
  1.1007 +        public void test() throws Exception {
  1.1008 +            long id = insertOneHistoryEntry();
  1.1009 +
  1.1010 +            int deleted = mProvider.delete(BrowserContract.History.CONTENT_URI,
  1.1011 +                                           BrowserContract.History._ID + " = ?",
  1.1012 +                                           new String[] { String.valueOf(id) });
  1.1013 +
  1.1014 +            mAsserter.is((deleted == 1), true, "Inserted history entry was deleted");
  1.1015 +
  1.1016 +            Cursor c = getHistoryEntryById(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
  1.1017 +            mAsserter.is(c.moveToFirst(), true, "Deleted history entry was only marked as deleted");
  1.1018 +
  1.1019 +            deleted = mProvider.delete(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"),
  1.1020 +                                       BrowserContract.History._ID + " = ?",
  1.1021 +                                       new String[] { String.valueOf(id) });
  1.1022 +
  1.1023 +            mAsserter.is((deleted == 1), true, "Inserted history entry was deleted");
  1.1024 +            c.close();
  1.1025 +
  1.1026 +            c = getHistoryEntryById(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
  1.1027 +            mAsserter.is(c.moveToFirst(), false, "Inserted history is now actually deleted");
  1.1028 +
  1.1029 +            id = insertOneHistoryEntry();
  1.1030 +
  1.1031 +            deleted = mProvider.delete(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), null, null);
  1.1032 +            mAsserter.is((deleted == 1), true,
  1.1033 +                         "Inserted history entry was deleted using URI with id");
  1.1034 +            c.close();
  1.1035 +
  1.1036 +            c = getHistoryEntryById(id);
  1.1037 +            mAsserter.is(c.moveToFirst(), false,
  1.1038 +                         "Inserted history entry can't be found after deletion using URI with ID");
  1.1039 +            c.close();
  1.1040 +        }
  1.1041 +    }
  1.1042 +
  1.1043 +    private class TestDeleteHistoryFavicons extends TestCase {
  1.1044 +        @Override
  1.1045 +        public void test() throws Exception {
  1.1046 +            ContentValues h = createOneHistoryEntry();
  1.1047 +
  1.1048 +            long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h));
  1.1049 +            final String pageUrl = h.getAsString(BrowserContract.History.URL);
  1.1050 +
  1.1051 +            // Insert the favicon into the favicons table
  1.1052 +            mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, "FAVICON"));
  1.1053 +
  1.1054 +            Cursor c = getFaviconsByUrl(pageUrl);
  1.1055 +            mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
  1.1056 +
  1.1057 +            mProvider.delete(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), null, null);
  1.1058 +            c.close();
  1.1059 +
  1.1060 +            c = getFaviconsByUrl(pageUrl);
  1.1061 +            mAsserter.is(c.moveToFirst(), false, "Favicon is deleted with last reference to it");
  1.1062 +            c.close();
  1.1063 +        }
  1.1064 +    }
  1.1065 +
  1.1066 +    private class TestUpdateHistory extends TestCase {
  1.1067 +        private int updateWithNullCol(long id, String colName) throws Exception {
  1.1068 +            ContentValues u = new ContentValues();
  1.1069 +            u.putNull(colName);
  1.1070 +
  1.1071 +            int updated = 0;
  1.1072 +
  1.1073 +            try {
  1.1074 +                updated = mProvider.update(BrowserContract.History.CONTENT_URI, u,
  1.1075 +                                           BrowserContract.History._ID + " = ?",
  1.1076 +                                           new String[] { String.valueOf(id) });
  1.1077 +            } catch (Exception e) {}
  1.1078 +
  1.1079 +            return updated;
  1.1080 +        }
  1.1081 +
  1.1082 +        @Override
  1.1083 +        public void test() throws Exception {
  1.1084 +            ContentValues h = createOneHistoryEntry();
  1.1085 +            long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h));
  1.1086 +
  1.1087 +            Cursor c = getHistoryEntryById(id);
  1.1088 +            mAsserter.is(c.moveToFirst(), true, "Inserted history entry found");
  1.1089 +
  1.1090 +            long dateCreated = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED));
  1.1091 +            long dateModified = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED));
  1.1092 +
  1.1093 +            ContentValues u = new ContentValues();
  1.1094 +            u.put(BrowserContract.History.VISITS, h.getAsInteger(BrowserContract.History.VISITS) + 1);
  1.1095 +            u.put(BrowserContract.History.DATE_LAST_VISITED, System.currentTimeMillis());
  1.1096 +            u.put(BrowserContract.History.TITLE, h.getAsString(BrowserContract.History.TITLE) + "CHANGED");
  1.1097 +            u.put(BrowserContract.History.URL, h.getAsString(BrowserContract.History.URL) + "/more/stuff");
  1.1098 +
  1.1099 +            int updated = mProvider.update(BrowserContract.History.CONTENT_URI, u,
  1.1100 +                                           BrowserContract.History._ID + " = ?",
  1.1101 +                                           new String[] { String.valueOf(id) });
  1.1102 +
  1.1103 +            mAsserter.is((updated == 1), true, "Inserted history entry was updated");
  1.1104 +            c.close();
  1.1105 +
  1.1106 +            c = getHistoryEntryById(id);
  1.1107 +            mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
  1.1108 +
  1.1109 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), u.getAsString(BrowserContract.History.TITLE),
  1.1110 +                         "Updated history entry has correct title");
  1.1111 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), u.getAsString(BrowserContract.History.URL),
  1.1112 +                         "Updated history entry has correct URL");
  1.1113 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.VISITS)), u.getAsString(BrowserContract.History.VISITS),
  1.1114 +                         "Updated history entry has correct number of visits");
  1.1115 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.DATE_LAST_VISITED)), u.getAsString(BrowserContract.History.DATE_LAST_VISITED),
  1.1116 +                         "Updated history entry has correct last visited date");
  1.1117 +
  1.1118 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED))),
  1.1119 +                         new Long(dateCreated),
  1.1120 +                         "Updated history entry has same creation date");
  1.1121 +
  1.1122 +            mAsserter.isnot(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED))),
  1.1123 +                            new Long(dateModified),
  1.1124 +                            "Updated history entry has new modification date");
  1.1125 +
  1.1126 +            updated = updateWithNullCol(id, BrowserContract.History.URL);
  1.1127 +            mAsserter.is((updated > 0), false,
  1.1128 +                         "Should not be able to update history with null URL");
  1.1129 +
  1.1130 +            updated = updateWithNullCol(id, BrowserContract.History.VISITS);
  1.1131 +            mAsserter.is((updated > 0), false,
  1.1132 +                         "Should not be able to update history with null number of visits");
  1.1133 +
  1.1134 +            u = new ContentValues();
  1.1135 +            u.put(BrowserContract.History.URL, "http://examples2.com");
  1.1136 +
  1.1137 +            updated = mProvider.update(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), u, null, null);
  1.1138 +            c.close();
  1.1139 +
  1.1140 +            c = getHistoryEntryById(id);
  1.1141 +            mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
  1.1142 +
  1.1143 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), u.getAsString(BrowserContract.History.URL),
  1.1144 +                         "Updated history entry has correct URL using URI with id");
  1.1145 +            c.close();
  1.1146 +        }
  1.1147 +    }
  1.1148 +
  1.1149 +    private class TestUpdateHistoryFavicons extends TestCase {
  1.1150 +        @Override
  1.1151 +        public void test() throws Exception {
  1.1152 +            ContentValues h = createOneHistoryEntry();
  1.1153 +
  1.1154 +            final String favicon = "FAVICON";
  1.1155 +            final String newFavicon = "NEW_FAVICON";
  1.1156 +            final String pageUrl = h.getAsString(BrowserContract.History.URL);
  1.1157 +
  1.1158 +            mProvider.insert(BrowserContract.History.CONTENT_URI, h);
  1.1159 +
  1.1160 +            // Insert the favicon into the favicons table
  1.1161 +            mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, favicon));
  1.1162 +
  1.1163 +            Cursor c = getFaviconsByUrl(pageUrl);
  1.1164 +            mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
  1.1165 +
  1.1166 +            mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
  1.1167 +                         favicon, "Inserted favicon has corresponding favicon image");
  1.1168 +
  1.1169 +            ContentValues u = createFaviconEntry(pageUrl, newFavicon);
  1.1170 +
  1.1171 +            mProvider.update(BrowserContract.Favicons.CONTENT_URI, u, null, null);
  1.1172 +            c.close();
  1.1173 +
  1.1174 +            c = getFaviconsByUrl(pageUrl);
  1.1175 +            mAsserter.is(c.moveToFirst(), true, "Updated favicon found");
  1.1176 +
  1.1177 +            mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
  1.1178 +                         newFavicon, "Updated favicon has corresponding favicon image");
  1.1179 +            c.close();
  1.1180 +        }
  1.1181 +    }
  1.1182 +
  1.1183 +    private class TestUpdateOrInsertHistory extends TestCase {
  1.1184 +        private final String TEST_URL_1 = "http://example.com";
  1.1185 +        private final String TEST_URL_2 = "http://example.org";
  1.1186 +        private final String TEST_TITLE = "Example";
  1.1187 +
  1.1188 +        private long getHistoryEntryIdByUrl(String url) {
  1.1189 +            Cursor c = mProvider.query(BrowserContract.History.CONTENT_URI,
  1.1190 +                                       new String[] { BrowserContract.History._ID },
  1.1191 +                                       BrowserContract.History.URL + " = ?",
  1.1192 +                                       new String[] { url },
  1.1193 +                                       null);
  1.1194 +            c.moveToFirst();
  1.1195 +            long id = c.getLong(0);
  1.1196 +            c.close();
  1.1197 +
  1.1198 +            return id;
  1.1199 +        }
  1.1200 +
  1.1201 +        @Override
  1.1202 +        public void test() throws Exception {
  1.1203 +            Uri updateHistoryUri = BrowserContract.History.CONTENT_URI.buildUpon().
  1.1204 +                appendQueryParameter("increment_visits", "true").build();
  1.1205 +            Uri updateOrInsertHistoryUri = BrowserContract.History.CONTENT_URI.buildUpon().
  1.1206 +                appendQueryParameter("insert_if_needed", "true").
  1.1207 +                appendQueryParameter("increment_visits", "true").build();
  1.1208 +
  1.1209 +            // Update a non-existent history entry, without specifying visits or title
  1.1210 +            ContentValues values = new ContentValues();
  1.1211 +            values.put(BrowserContract.History.URL, TEST_URL_1);
  1.1212 +
  1.1213 +            int updated = mProvider.update(updateHistoryUri, values,
  1.1214 +                                           BrowserContract.History.URL + " = ?",
  1.1215 +                                           new String[] { TEST_URL_1 });
  1.1216 +            mAsserter.is((updated == 0), true, "History entry was not updated");
  1.1217 +            Cursor c = mProvider.query(BrowserContract.History.CONTENT_URI, null, null, null, null);
  1.1218 +            mAsserter.is(c.moveToFirst(), false, "History entry was not inserted");
  1.1219 +            c.close();
  1.1220 +
  1.1221 +            // Now let's try with update-or-insert.
  1.1222 +            updated = mProvider.update(updateOrInsertHistoryUri, values,
  1.1223 +                                           BrowserContract.History.URL + " = ?",
  1.1224 +                                           new String[] { TEST_URL_1 });
  1.1225 +            mAsserter.is((updated == 1), true, "History entry was inserted");
  1.1226 +
  1.1227 +            long id = getHistoryEntryIdByUrl(TEST_URL_1);
  1.1228 +            c = getHistoryEntryById(id);
  1.1229 +            mAsserter.is(c.moveToFirst(), true, "History entry was inserted");
  1.1230 +
  1.1231 +            long dateCreated = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED));
  1.1232 +            long dateModified = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED));
  1.1233 +
  1.1234 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS))), new Long(1),
  1.1235 +                         "Inserted history entry has correct default number of visits");
  1.1236 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), TEST_URL_1,
  1.1237 +                         "Inserted history entry has correct default title");
  1.1238 +
  1.1239 +            // Update the history entry, without specifying an additional visit count
  1.1240 +            values = new ContentValues();
  1.1241 +            values.put(BrowserContract.History.DATE_LAST_VISITED, System.currentTimeMillis());
  1.1242 +            values.put(BrowserContract.History.TITLE, TEST_TITLE);
  1.1243 +
  1.1244 +            updated = mProvider.update(updateOrInsertHistoryUri, values,
  1.1245 +                                       BrowserContract.History._ID + " = ?",
  1.1246 +                                       new String[] { String.valueOf(id) });
  1.1247 +            mAsserter.is((updated == 1), true, "Inserted history entry was updated");
  1.1248 +            c.close();
  1.1249 +
  1.1250 +            c = getHistoryEntryById(id);
  1.1251 +            mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
  1.1252 +
  1.1253 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), TEST_TITLE,
  1.1254 +                         "Updated history entry has correct title");
  1.1255 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS))), new Long(2),
  1.1256 +                         "Updated history entry has correct number of visits");
  1.1257 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED))), new Long(dateCreated),
  1.1258 +                         "Updated history entry has same creation date");
  1.1259 +            mAsserter.isnot(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED))), new Long(dateModified),
  1.1260 +                            "Updated history entry has new modification date");
  1.1261 +
  1.1262 +            // Create a new history entry, specifying visits and history
  1.1263 +            values = new ContentValues();
  1.1264 +            values.put(BrowserContract.History.URL, TEST_URL_2);
  1.1265 +            values.put(BrowserContract.History.TITLE, TEST_TITLE);
  1.1266 +            values.put(BrowserContract.History.VISITS, 10);
  1.1267 +
  1.1268 +            updated = mProvider.update(updateOrInsertHistoryUri, values,
  1.1269 +                                           BrowserContract.History.URL + " = ?",
  1.1270 +                                           new String[] { values.getAsString(BrowserContract.History.URL) });
  1.1271 +            mAsserter.is((updated == 1), true, "History entry was inserted");
  1.1272 +
  1.1273 +            id = getHistoryEntryIdByUrl(TEST_URL_2);
  1.1274 +            c.close();
  1.1275 +
  1.1276 +            c = getHistoryEntryById(id);
  1.1277 +            mAsserter.is(c.moveToFirst(), true, "History entry was inserted");
  1.1278 +
  1.1279 +            dateCreated = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED));
  1.1280 +            dateModified = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED));
  1.1281 +
  1.1282 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS))), new Long(10),
  1.1283 +                         "Inserted history entry has correct specified number of visits");
  1.1284 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), TEST_TITLE,
  1.1285 +                         "Inserted history entry has correct specified title");
  1.1286 +
  1.1287 +            // Update the history entry, specifying additional visit count
  1.1288 +            values = new ContentValues();
  1.1289 +            values.put(BrowserContract.History.VISITS, 10);
  1.1290 +
  1.1291 +            updated = mProvider.update(updateOrInsertHistoryUri, values,
  1.1292 +                                       BrowserContract.History._ID + " = ?",
  1.1293 +                                       new String[] { String.valueOf(id) });
  1.1294 +            mAsserter.is((updated == 1), true, "Inserted history entry was updated");
  1.1295 +            c.close();
  1.1296 +
  1.1297 +            c = getHistoryEntryById(id);
  1.1298 +            mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
  1.1299 +
  1.1300 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), TEST_TITLE,
  1.1301 +                         "Updated history entry has correct unchanged title");
  1.1302 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), TEST_URL_2,
  1.1303 +                         "Updated history entry has correct unchanged URL");
  1.1304 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS))), new Long(20),
  1.1305 +                         "Updated history entry has correct number of visits");
  1.1306 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED))), new Long(dateCreated),
  1.1307 +                         "Updated history entry has same creation date");
  1.1308 +            mAsserter.isnot(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED))), new Long(dateModified),
  1.1309 +                            "Updated history entry has new modification date");
  1.1310 +            c.close();
  1.1311 +
  1.1312 +        }
  1.1313 +    }
  1.1314 +
  1.1315 +    private class TestInsertHistoryThumbnails extends TestCase {
  1.1316 +        @Override
  1.1317 +        public void test() throws Exception {
  1.1318 +            ContentValues h = createOneHistoryEntry();
  1.1319 +
  1.1320 +            final String thumbnail = "THUMBNAIL";
  1.1321 +            final String pageUrl = h.getAsString(BrowserContract.History.URL);
  1.1322 +
  1.1323 +            long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h));
  1.1324 +
  1.1325 +            // Insert the thumbnail into the thumbnails table
  1.1326 +            mProvider.insert(BrowserContract.Thumbnails.CONTENT_URI, createThumbnailEntry(pageUrl, thumbnail));
  1.1327 +
  1.1328 +            Cursor c = getThumbnailByUrl(pageUrl);
  1.1329 +            mAsserter.is(c.moveToFirst(), true, "Inserted thumbnail found");
  1.1330 +
  1.1331 +            mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Thumbnails.DATA)), "UTF8"),
  1.1332 +                         thumbnail, "Inserted thumbnail has corresponding thumbnail image");
  1.1333 +            c.close();
  1.1334 +        }
  1.1335 +    }
  1.1336 +
  1.1337 +    private class TestUpdateHistoryThumbnails extends TestCase {
  1.1338 +        @Override
  1.1339 +        public void test() throws Exception {
  1.1340 +            ContentValues h = createOneHistoryEntry();
  1.1341 +
  1.1342 +            final String thumbnail = "THUMBNAIL";
  1.1343 +            final String newThumbnail = "NEW_THUMBNAIL";
  1.1344 +            final String pageUrl = h.getAsString(BrowserContract.History.URL);
  1.1345 +
  1.1346 +            mProvider.insert(BrowserContract.History.CONTENT_URI, h);
  1.1347 +
  1.1348 +            // Insert the thumbnail into the thumbnails table
  1.1349 +            mProvider.insert(BrowserContract.Thumbnails.CONTENT_URI, createThumbnailEntry(pageUrl, thumbnail));
  1.1350 +
  1.1351 +            Cursor c = getThumbnailByUrl(pageUrl);
  1.1352 +            mAsserter.is(c.moveToFirst(), true, "Inserted thumbnail found");
  1.1353 +
  1.1354 +            mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Thumbnails.DATA)), "UTF8"),
  1.1355 +                         thumbnail, "Inserted thumbnail has corresponding thumbnail image");
  1.1356 +
  1.1357 +            ContentValues u = createThumbnailEntry(pageUrl, newThumbnail);
  1.1358 +
  1.1359 +            mProvider.update(BrowserContract.Thumbnails.CONTENT_URI, u, null, null);
  1.1360 +            c.close();
  1.1361 +
  1.1362 +            c = getThumbnailByUrl(pageUrl);
  1.1363 +            mAsserter.is(c.moveToFirst(), true, "Updated thumbnail found");
  1.1364 +
  1.1365 +            mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Thumbnails.DATA)), "UTF8"),
  1.1366 +                         newThumbnail, "Updated thumbnail has corresponding thumbnail image");
  1.1367 +            c.close();
  1.1368 +        }
  1.1369 +    }
  1.1370 +
  1.1371 +    private class TestDeleteHistoryThumbnails extends TestCase {
  1.1372 +        @Override
  1.1373 +        public void test() throws Exception {
  1.1374 +            ContentValues h = createOneHistoryEntry();
  1.1375 +
  1.1376 +            long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h));
  1.1377 +            final String pageUrl = h.getAsString(BrowserContract.History.URL);
  1.1378 +
  1.1379 +            // Insert the thumbnail into the thumbnails table
  1.1380 +            mProvider.insert(BrowserContract.Thumbnails.CONTENT_URI, createThumbnailEntry(pageUrl, "THUMBNAIL"));
  1.1381 +
  1.1382 +            Cursor c = getThumbnailByUrl(pageUrl);
  1.1383 +            mAsserter.is(c.moveToFirst(), true, "Inserted thumbnail found");
  1.1384 +
  1.1385 +            mProvider.delete(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), null, null);
  1.1386 +            c.close();
  1.1387 +
  1.1388 +            c = getThumbnailByUrl(pageUrl);
  1.1389 +            mAsserter.is(c.moveToFirst(), false, "Thumbnail is deleted with last reference to it");
  1.1390 +            c.close();
  1.1391 +        }
  1.1392 +    }
  1.1393 +
  1.1394 +    private class TestCombinedView extends TestCase {
  1.1395 +        @Override
  1.1396 +        public void test() throws Exception {
  1.1397 +            final String TITLE_1 = "Test Page 1";
  1.1398 +            final String TITLE_2 = "Test Page 2";
  1.1399 +            final String TITLE_3_HISTORY = "Test Page 3 (History Entry)";
  1.1400 +            final String TITLE_3_BOOKMARK = "Test Page 3 (Bookmark Entry)";
  1.1401 +            final String TITLE_3_BOOKMARK2 = "Test Page 3 (Bookmark Entry 2)";
  1.1402 +
  1.1403 +            final String URL_1 = "http://example1.com";
  1.1404 +            final String URL_2 = "http://example2.com";
  1.1405 +            final String URL_3 = "http://example3.com";
  1.1406 +
  1.1407 +            final int VISITS = 10;
  1.1408 +            final long LAST_VISITED = System.currentTimeMillis();
  1.1409 +
  1.1410 +            // Create a basic history entry
  1.1411 +            ContentValues basicHistory = createHistoryEntry(TITLE_1, URL_1, VISITS, LAST_VISITED);
  1.1412 +            long basicHistoryId = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, basicHistory));
  1.1413 +
  1.1414 +            // Create a basic bookmark entry
  1.1415 +            ContentValues basicBookmark = createBookmark(TITLE_2, URL_2, mMobileFolderId,
  1.1416 +                BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
  1.1417 +            long basicBookmarkId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, basicBookmark));
  1.1418 +
  1.1419 +            // Create a history entry and bookmark entry with the same URL to
  1.1420 +            // represent a visited bookmark
  1.1421 +            ContentValues combinedHistory = createHistoryEntry(TITLE_3_HISTORY, URL_3, VISITS, LAST_VISITED);
  1.1422 +            long combinedHistoryId = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, combinedHistory));
  1.1423 +
  1.1424 +
  1.1425 +            ContentValues combinedBookmark = createBookmark(TITLE_3_BOOKMARK, URL_3, mMobileFolderId,
  1.1426 +                BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
  1.1427 +            long combinedBookmarkId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, combinedBookmark));
  1.1428 +
  1.1429 +            ContentValues combinedBookmark2 = createBookmark(TITLE_3_BOOKMARK2, URL_3, mMobileFolderId,
  1.1430 +                BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
  1.1431 +            long combinedBookmarkId2 = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, combinedBookmark2));
  1.1432 +
  1.1433 +            // Create a bookmark folder to make sure it _doesn't_ show up in the results
  1.1434 +            ContentValues folderBookmark = createBookmark("", "", mMobileFolderId,
  1.1435 +                BrowserContract.Bookmarks.TYPE_FOLDER, 0, "tags", "description", "keyword");
  1.1436 +            mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, folderBookmark);
  1.1437 +
  1.1438 +            // Sort entries by url so we can check them individually
  1.1439 +            Cursor c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, BrowserContract.Combined.URL);
  1.1440 +
  1.1441 +            mAsserter.is(c.getCount(), 3, "3 combined entries found");
  1.1442 +
  1.1443 +            // First combined entry is basic history entry
  1.1444 +            mAsserter.is(c.moveToFirst(), true, "Found basic history entry");
  1.1445 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined._ID))), new Long(0),
  1.1446 +                         "Combined _id column should always be 0");
  1.1447 +            // TODO: Should we change BrowserProvider to make this return -1, not 0?
  1.1448 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(0),
  1.1449 +                         "Bookmark id should be 0 for basic history entry");
  1.1450 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.HISTORY_ID))), new Long(basicHistoryId),
  1.1451 +                         "Basic history entry has correct history id");
  1.1452 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)), TITLE_1,
  1.1453 +                         "Basic history entry has correct title");
  1.1454 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.URL)), URL_1,
  1.1455 +                         "Basic history entry has correct url");
  1.1456 +            mAsserter.is(c.getInt(c.getColumnIndex(BrowserContract.Combined.VISITS)), VISITS,
  1.1457 +                         "Basic history entry has correct number of visits");
  1.1458 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DATE_LAST_VISITED))), new Long(LAST_VISITED),
  1.1459 +                         "Basic history entry has correct last visit time");
  1.1460 +
  1.1461 +            // Second combined entry is basic bookmark entry
  1.1462 +            mAsserter.is(c.moveToNext(), true, "Found basic bookmark entry");
  1.1463 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined._ID))), new Long(0),
  1.1464 +                         "Combined _id column should always be 0");
  1.1465 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(basicBookmarkId),
  1.1466 +                         "Basic bookmark entry has correct bookmark id");
  1.1467 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.HISTORY_ID))), new Long(-1),
  1.1468 +                         "History id should be -1 for basic bookmark entry");
  1.1469 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)), TITLE_2,
  1.1470 +                         "Basic bookmark entry has correct title");
  1.1471 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.URL)), URL_2,
  1.1472 +                         "Basic bookmark entry has correct url");
  1.1473 +            mAsserter.is(c.getInt(c.getColumnIndex(BrowserContract.Combined.VISITS)), -1,
  1.1474 +                         "Visits should be -1 for basic bookmark entry");
  1.1475 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DATE_LAST_VISITED))), new Long(-1),
  1.1476 +                         "Basic entry has correct last visit time");
  1.1477 +
  1.1478 +            // Third combined entry is a combined history/bookmark entry
  1.1479 +            mAsserter.is(c.moveToNext(), true, "Found third combined entry");
  1.1480 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined._ID))), new Long(0),
  1.1481 +                         "Combined _id column should always be 0");
  1.1482 +            // The bookmark data (bookmark_id and title) associated with the combined entry is non-deterministic,
  1.1483 +            // it might end up with data coming from any of the matching bookmark entries.
  1.1484 +            mAsserter.is(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)) == combinedBookmarkId ||
  1.1485 +                         c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID)) == combinedBookmarkId2, true,
  1.1486 +                         "Combined entry has correct bookmark id");
  1.1487 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)).equals(TITLE_3_BOOKMARK) ||
  1.1488 +                         c.getString(c.getColumnIndex(BrowserContract.Combined.TITLE)).equals(TITLE_3_BOOKMARK2), true,
  1.1489 +                         "Combined entry has title corresponding to bookmark entry");
  1.1490 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.HISTORY_ID))), new Long(combinedHistoryId),
  1.1491 +                         "Combined entry has correct history id");
  1.1492 +            mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.URL)), URL_3,
  1.1493 +                         "Combined entry has correct url");
  1.1494 +            mAsserter.is(c.getInt(c.getColumnIndex(BrowserContract.Combined.VISITS)), VISITS,
  1.1495 +                         "Combined entry has correct number of visits");
  1.1496 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DATE_LAST_VISITED))), new Long(LAST_VISITED),
  1.1497 +                         "Combined entry has correct last visit time");
  1.1498 +            c.close();
  1.1499 +        }
  1.1500 +    }
  1.1501 +
  1.1502 +    private class TestCombinedViewDisplay extends TestCase {
  1.1503 +        @Override
  1.1504 +        public void test() throws Exception {
  1.1505 +            final String TITLE_1 = "Test Page 1";
  1.1506 +            final String TITLE_2 = "Test Page 2";
  1.1507 +            final String TITLE_3_HISTORY = "Test Page 3 (History Entry)";
  1.1508 +            final String TITLE_3_BOOKMARK = "Test Page 3 (Bookmark Entry)";
  1.1509 +            final String TITLE_4 = "Test Page 4";
  1.1510 +
  1.1511 +            final String URL_1 = "http://example.com";
  1.1512 +            final String URL_2 = "http://example.org";
  1.1513 +            final String URL_3 = "http://examples2.com";
  1.1514 +            final String URL_4 = "http://readinglist.com";
  1.1515 +
  1.1516 +            final int VISITS = 10;
  1.1517 +            final long LAST_VISITED = System.currentTimeMillis();
  1.1518 +
  1.1519 +            // Create a basic history entry
  1.1520 +            ContentValues basicHistory = createHistoryEntry(TITLE_1, URL_1, VISITS, LAST_VISITED);
  1.1521 +            ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, basicHistory));
  1.1522 +
  1.1523 +            // Create a basic bookmark entry
  1.1524 +            ContentValues basicBookmark = createBookmark(TITLE_2, URL_2, mMobileFolderId,
  1.1525 +                BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
  1.1526 +            mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, basicBookmark);
  1.1527 +
  1.1528 +            // Create a history entry and bookmark entry with the same URL to
  1.1529 +            // represent a visited bookmark
  1.1530 +            ContentValues combinedHistory = createHistoryEntry(TITLE_3_HISTORY, URL_3, VISITS, LAST_VISITED);
  1.1531 +            mProvider.insert(BrowserContract.History.CONTENT_URI, combinedHistory);
  1.1532 +
  1.1533 +            ContentValues combinedBookmark = createBookmark(TITLE_3_BOOKMARK, URL_3, mMobileFolderId,
  1.1534 +                BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
  1.1535 +            mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, combinedBookmark);
  1.1536 +
  1.1537 +            // Create a reading list entries
  1.1538 +            int readingListId = BrowserContract.Bookmarks.FIXED_READING_LIST_ID;
  1.1539 +            ContentValues readingListItem = createBookmark(TITLE_3_BOOKMARK, URL_3, readingListId,
  1.1540 +                BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
  1.1541 +            long readingListItemId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, readingListItem));
  1.1542 +
  1.1543 +            ContentValues readingListItem2 = createBookmark(TITLE_4, URL_4, readingListId,
  1.1544 +                BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
  1.1545 +            long readingListItemId2 = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, readingListItem2));
  1.1546 +
  1.1547 +            Cursor c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);
  1.1548 +            mAsserter.is(c.getCount(), 4, "4 combined entries found");
  1.1549 +
  1.1550 +            while (c.moveToNext()) {
  1.1551 +                long id = c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID));
  1.1552 +
  1.1553 +                int display = c.getInt(c.getColumnIndex(BrowserContract.Combined.DISPLAY));
  1.1554 +                int expectedDisplay = (id == readingListItemId || id == readingListItemId2 ? BrowserContract.Combined.DISPLAY_READER : BrowserContract.Combined.DISPLAY_NORMAL);
  1.1555 +
  1.1556 +                mAsserter.is(new Integer(display), new Integer(expectedDisplay),
  1.1557 +                                 "Combined display column should always be DISPLAY_READER for the reading list item");
  1.1558 +            }
  1.1559 +            c.close();
  1.1560 +        }
  1.1561 +    }
  1.1562 +
  1.1563 +    private class TestCombinedViewWithDeletedBookmark extends TestCase {
  1.1564 +        @Override
  1.1565 +        public void test() throws Exception {
  1.1566 +            final String TITLE = "Test Page 1";
  1.1567 +            final String URL = "http://example.com";
  1.1568 +            final int VISITS = 10;
  1.1569 +            final long LAST_VISITED = System.currentTimeMillis();
  1.1570 +
  1.1571 +            // Create a combined history entry
  1.1572 +            ContentValues combinedHistory = createHistoryEntry(TITLE, URL, VISITS, LAST_VISITED);
  1.1573 +            mProvider.insert(BrowserContract.History.CONTENT_URI, combinedHistory);
  1.1574 +
  1.1575 +            // Create a combined bookmark entry
  1.1576 +            ContentValues combinedBookmark = createBookmark(TITLE, URL, mMobileFolderId,
  1.1577 +                BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
  1.1578 +            long combinedBookmarkId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, combinedBookmark));
  1.1579 +
  1.1580 +            Cursor c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);
  1.1581 +            mAsserter.is(c.getCount(), 1, "1 combined entry found");
  1.1582 +
  1.1583 +            mAsserter.is(c.moveToFirst(), true, "Found combined entry with bookmark id");
  1.1584 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(combinedBookmarkId),
  1.1585 +                         "Bookmark id should be set correctly on combined entry");
  1.1586 +
  1.1587 +            int deleted = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI,
  1.1588 +                                           BrowserContract.Bookmarks._ID + " = ?",
  1.1589 +                                           new String[] { String.valueOf(combinedBookmarkId) });
  1.1590 +
  1.1591 +            mAsserter.is((deleted == 1), true, "Inserted combined bookmark was deleted");
  1.1592 +            c.close();
  1.1593 +
  1.1594 +            c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);
  1.1595 +            mAsserter.is(c.getCount(), 1, "1 combined entry found");
  1.1596 +
  1.1597 +            mAsserter.is(c.moveToFirst(), true, "Found combined entry without bookmark id");
  1.1598 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(0),
  1.1599 +                         "Bookmark id should not be set to removed bookmark id");
  1.1600 +            c.close();
  1.1601 +        }
  1.1602 +    }
  1.1603 +
  1.1604 +    private class TestCombinedViewWithDeletedReadingListItem extends TestCase {
  1.1605 +        @Override
  1.1606 +        public void test() throws Exception {
  1.1607 +            final String TITLE = "Test Page 1";
  1.1608 +            final String URL = "http://example.com";
  1.1609 +            final int VISITS = 10;
  1.1610 +            final long LAST_VISITED = System.currentTimeMillis();
  1.1611 +
  1.1612 +            // Create a combined history entry
  1.1613 +            ContentValues combinedHistory = createHistoryEntry(TITLE, URL, VISITS, LAST_VISITED);
  1.1614 +            mProvider.insert(BrowserContract.History.CONTENT_URI, combinedHistory);
  1.1615 +
  1.1616 +            // Create a combined bookmark entry
  1.1617 +            int readingListId = BrowserContract.Bookmarks.FIXED_READING_LIST_ID;
  1.1618 +            ContentValues combinedReadingListItem = createBookmark(TITLE, URL, readingListId,
  1.1619 +                BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
  1.1620 +            long combinedReadingListItemId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, combinedReadingListItem));
  1.1621 +
  1.1622 +            Cursor c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);
  1.1623 +            mAsserter.is(c.getCount(), 1, "1 combined entry found");
  1.1624 +
  1.1625 +            mAsserter.is(c.moveToFirst(), true, "Found combined entry with bookmark id");
  1.1626 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(combinedReadingListItemId),
  1.1627 +                         "Bookmark id should be set correctly on combined entry");
  1.1628 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DISPLAY))), new Long(BrowserContract.Combined.DISPLAY_READER),
  1.1629 +                         "Combined entry should have reader display type");
  1.1630 +
  1.1631 +            int deleted = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI,
  1.1632 +                                           BrowserContract.Bookmarks._ID + " = ?",
  1.1633 +                                           new String[] { String.valueOf(combinedReadingListItemId) });
  1.1634 +
  1.1635 +            mAsserter.is((deleted == 1), true, "Inserted combined reading list item was deleted");
  1.1636 +            c.close();
  1.1637 +
  1.1638 +            c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);
  1.1639 +            mAsserter.is(c.getCount(), 1, "1 combined entry found");
  1.1640 +
  1.1641 +            mAsserter.is(c.moveToFirst(), true, "Found combined entry without bookmark id");
  1.1642 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(0),
  1.1643 +                         "Bookmark id should not be set to removed bookmark id");
  1.1644 +            mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DISPLAY))), new Long(BrowserContract.Combined.DISPLAY_NORMAL),
  1.1645 +                         "Combined entry should have reader display type");
  1.1646 +            c.close();
  1.1647 +        }
  1.1648 +    }
  1.1649 +
  1.1650 +    private class TestExpireHistory extends TestCase {
  1.1651 +        private void createFakeHistory(long timeShift, int count) {
  1.1652 +            // Insert a bunch of very new entries
  1.1653 +            ContentValues[] allVals = new ContentValues[count];
  1.1654 +            long time = System.currentTimeMillis() - timeShift;
  1.1655 +            for (int i = 0; i < count; i++) {
  1.1656 +                allVals[i] = new ContentValues();
  1.1657 +                allVals[i].put(BrowserContract.History.TITLE, "Test " + i);
  1.1658 +                allVals[i].put(BrowserContract.History.URL, "http://www.test.org/" + i);
  1.1659 +                allVals[i].put(BrowserContract.History.VISITS, i);
  1.1660 +                allVals[i].put(BrowserContract.History.DATE_LAST_VISITED, time);
  1.1661 +            }
  1.1662 +
  1.1663 +            int inserts = mProvider.bulkInsert(BrowserContract.History.CONTENT_URI, allVals);
  1.1664 +            mAsserter.is(inserts, count, "Expected number of inserts matches");
  1.1665 +
  1.1666 +            // inserting a new entry sets the date created and modified automatically
  1.1667 +            // reset all of them
  1.1668 +            for (int i = 0; i < count; i++) {
  1.1669 +                ContentValues cv = new ContentValues();
  1.1670 +                cv.put(BrowserContract.History.DATE_CREATED, time);
  1.1671 +                cv.put(BrowserContract.History.DATE_MODIFIED, time);
  1.1672 +                mProvider.update(BrowserContract.History.CONTENT_URI, cv, BrowserContract.History.URL + " = ?",
  1.1673 +                                 new String[] { "http://www.test.org/" + i });
  1.1674 +            }
  1.1675 +
  1.1676 +            Cursor c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
  1.1677 +
  1.1678 +            assertCountIsAndClose(c, count, count + " history entries found");
  1.1679 +
  1.1680 +            // add thumbnails for each entry
  1.1681 +            allVals = new ContentValues[count];
  1.1682 +            for (int i = 0; i < count; i++) {
  1.1683 +                allVals[i] = new ContentValues();
  1.1684 +                allVals[i].put(BrowserContract.Thumbnails.DATA, i);
  1.1685 +                allVals[i].put(BrowserContract.Thumbnails.URL, "http://www.test.org/" + i);
  1.1686 +            }
  1.1687 +
  1.1688 +            inserts = mProvider.bulkInsert(BrowserContract.Thumbnails.CONTENT_URI, allVals);
  1.1689 +            mAsserter.is(inserts, count, "Expected number of inserts matches");
  1.1690 +
  1.1691 +            c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
  1.1692 +            assertCountIsAndClose(c, count, count + " thumbnails entries found");
  1.1693 +        }
  1.1694 +
  1.1695 +        @Override
  1.1696 +        public void test() throws Exception {
  1.1697 +            final int count = 3000;
  1.1698 +            final int thumbCount = 15;
  1.1699 +
  1.1700 +            // insert a bunch of new entries
  1.1701 +            createFakeHistory(0, count);
  1.1702 +
  1.1703 +            // expiring with a normal priority should not delete new entries
  1.1704 +            Uri url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "NORMAL");
  1.1705 +            mProvider.delete(url, null, null);
  1.1706 +            Cursor c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
  1.1707 +            assertCountIsAndClose(c, count, count + " history entries found");
  1.1708 +
  1.1709 +            // expiring with a normal priority should delete all but 10 thumbnails
  1.1710 +            c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
  1.1711 +            assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
  1.1712 +
  1.1713 +            ensureEmptyDatabase();
  1.1714 +
  1.1715 +            // Insert a bunch of new entries.
  1.1716 +            createFakeHistory(0, count);
  1.1717 +
  1.1718 +            // Expiring with a aggressive priority should leave 500 entries.
  1.1719 +            url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "AGGRESSIVE");
  1.1720 +            mProvider.delete(url, null, null);
  1.1721 +
  1.1722 +            c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
  1.1723 +            assertCountIsAndClose(c, 500, "500 history entries found");
  1.1724 +
  1.1725 +            // Expiring with a aggressive priority should delete all but 10 thumbnails.
  1.1726 +            c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
  1.1727 +            assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
  1.1728 +
  1.1729 +            ensureEmptyDatabase();
  1.1730 +
  1.1731 +            // Insert a bunch of entries with an old time created/modified.
  1.1732 +            long time = 1000L * 60L * 60L * 24L * 30L * 3L;
  1.1733 +            createFakeHistory(time, count);
  1.1734 +
  1.1735 +            // Expiring with an normal priority should remove at most 1000 entries,
  1.1736 +            // entries leaving at least 2000.
  1.1737 +            url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "NORMAL");
  1.1738 +            mProvider.delete(url, null, null);
  1.1739 +
  1.1740 +            c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
  1.1741 +            assertCountIsAndClose(c, 2000, "2000 history entries found");
  1.1742 +
  1.1743 +            // Expiring with a normal priority should delete all but 10 thumbnails.
  1.1744 +            c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
  1.1745 +            assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
  1.1746 +
  1.1747 +            ensureEmptyDatabase();
  1.1748 +            // insert a bunch of entries with an old time created/modified
  1.1749 +            time = 1000L * 60L * 60L * 24L * 30L * 3L;
  1.1750 +            createFakeHistory(time, count);
  1.1751 +
  1.1752 +            // Expiring with an aggressive priority should remove old
  1.1753 +            // entries, leaving at least 500.
  1.1754 +            url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "AGGRESSIVE");
  1.1755 +            mProvider.delete(url, null, null);
  1.1756 +            c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
  1.1757 +            assertCountIsAndClose(c, 500, "500 history entries found");
  1.1758 +
  1.1759 +            // expiring with an aggressive priority should delete all but 10 thumbnails
  1.1760 +            c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
  1.1761 +            assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
  1.1762 +        }
  1.1763 +    }
  1.1764 +
  1.1765 +    /*
  1.1766 +     * Verify that insert, update, delete, and bulkInsert operations
  1.1767 +     * notify the ambient content resolver.  Each operation calls the
  1.1768 +     * content resolver notifyChange method synchronously, so it is
  1.1769 +     * okay to test sequentially.
  1.1770 +     */
  1.1771 +    private class TestBrowserProviderNotifications extends TestCase {
  1.1772 +        public static final String LOGTAG = "TestBPNotifications";
  1.1773 +
  1.1774 +        protected void ensureOnlyChangeNotifiedStartsWith(Uri expectedUri, String operation) {
  1.1775 +            if (expectedUri == null) {
  1.1776 +                throw new IllegalArgumentException("expectedUri must not be null");
  1.1777 +            }
  1.1778 +
  1.1779 +            if (mResolver.notifyChangeList.size() != 1) {
  1.1780 +                // Log to help post-mortem debugging
  1.1781 +                Log.w(LOGTAG, "after operation, notifyChangeList = " + mResolver.notifyChangeList);
  1.1782 +            }
  1.1783 +
  1.1784 +            mAsserter.is(Long.valueOf(mResolver.notifyChangeList.size()),
  1.1785 +                         Long.valueOf(1),
  1.1786 +                         "Content observer was notified exactly once by " + operation);
  1.1787 +
  1.1788 +            Uri uri = mResolver.notifyChangeList.poll();
  1.1789 +
  1.1790 +            mAsserter.isnot(uri,
  1.1791 +                            null,
  1.1792 +                            "Notification from " + operation + " was valid");
  1.1793 +
  1.1794 +            mAsserter.ok(uri.toString().startsWith(expectedUri.toString()),
  1.1795 +                         "Content observer was notified exactly once by " + operation,
  1.1796 +                         uri.toString() + " starts with expected prefix " + expectedUri);
  1.1797 +        }
  1.1798 +
  1.1799 +        @Override
  1.1800 +        public void test() throws Exception {
  1.1801 +            // Insert
  1.1802 +            final ContentValues h = createOneHistoryEntry();
  1.1803 +
  1.1804 +            mResolver.notifyChangeList.clear();
  1.1805 +            long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h));
  1.1806 +
  1.1807 +            mAsserter.isnot(Long.valueOf(id),
  1.1808 +                            Long.valueOf(-1),
  1.1809 +                            "Inserted item has valid id");
  1.1810 +
  1.1811 +            ensureOnlyChangeNotifiedStartsWith(BrowserContract.History.CONTENT_URI, "insert");
  1.1812 +
  1.1813 +            // Update
  1.1814 +            mResolver.notifyChangeList.clear();
  1.1815 +            h.put(BrowserContract.History.TITLE, "http://newexample.com");
  1.1816 +
  1.1817 +            long numUpdated = mProvider.update(BrowserContract.History.CONTENT_URI, h,
  1.1818 +                                               BrowserContract.History._ID + " = ?",
  1.1819 +                                               new String[] { String.valueOf(id) });
  1.1820 +
  1.1821 +            mAsserter.is(Long.valueOf(numUpdated),
  1.1822 +                         Long.valueOf(1),
  1.1823 +                         "Correct number of items are updated");
  1.1824 +
  1.1825 +            ensureOnlyChangeNotifiedStartsWith(BrowserContract.History.CONTENT_URI, "update");
  1.1826 +
  1.1827 +            // Delete
  1.1828 +            mResolver.notifyChangeList.clear();
  1.1829 +            long numDeleted = mProvider.delete(BrowserContract.History.CONTENT_URI, null, null);
  1.1830 +
  1.1831 +            mAsserter.is(Long.valueOf(numDeleted),
  1.1832 +                         Long.valueOf(1),
  1.1833 +                         "Correct number of items are deleted");
  1.1834 +
  1.1835 +            ensureOnlyChangeNotifiedStartsWith(BrowserContract.History.CONTENT_URI, "delete");
  1.1836 +
  1.1837 +            // Bulk insert
  1.1838 +            final ContentValues[] hs = new ContentValues[] { createOneHistoryEntry() };
  1.1839 +
  1.1840 +            mResolver.notifyChangeList.clear();
  1.1841 +            long numBulkInserted = mProvider.bulkInsert(BrowserContract.History.CONTENT_URI, hs);
  1.1842 +
  1.1843 +            mAsserter.is(Long.valueOf(numBulkInserted),
  1.1844 +                         Long.valueOf(1),
  1.1845 +                         "Correct number of items are bulkInserted");
  1.1846 +
  1.1847 +            ensureOnlyChangeNotifiedStartsWith(BrowserContract.History.CONTENT_URI, "bulkInsert");
  1.1848 +        }
  1.1849 +    }
  1.1850 +
  1.1851 +    /**
  1.1852 +     * Assert that the provided cursor has the expected number of rows,
  1.1853 +     * closing the cursor afterwards.
  1.1854 +     */
  1.1855 +    private void assertCountIsAndClose(Cursor c, int expectedCount, String message) {
  1.1856 +        try {
  1.1857 +            mAsserter.is(c.getCount(), expectedCount, message);
  1.1858 +        } finally {
  1.1859 +            c.close();
  1.1860 +        }
  1.1861 +    }
  1.1862 +}

mercurial