Bug 975792 - ContentProvider tests don't clean up their cursors. r=nalexander
☠☠ backed out by 1422dfcd7fd8 ☠ ☠
authorRichard Newman <rnewman@mozilla.com>
Sun, 23 Feb 2014 10:53:10 -0800
changeset 170494 49a1923db9be8fd1b60f798ddbabf24d3e7d24e3
parent 170493 4f7128acbae06c52609ffa93e0609f41a0227a21
child 170495 1f6450d61d3fdf18f8a9e4fc1dd048a44692f72e
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersnalexander
bugs975792
milestone30.0a1
Bug 975792 - ContentProvider tests don't clean up their cursors. r=nalexander
mobile/android/base/tests/testBrowserProvider.java
--- a/mobile/android/base/tests/testBrowserProvider.java
+++ b/mobile/android/base/tests/testBrowserProvider.java
@@ -60,27 +60,33 @@ public class testBrowserProvider extends
                                         BrowserContract.Bookmarks.MOBILE_FOLDER_GUID,
                                         BrowserContract.Bookmarks.MENU_FOLDER_GUID,
                                         BrowserContract.Bookmarks.TAGS_FOLDER_GUID,
                                         BrowserContract.Bookmarks.TOOLBAR_FOLDER_GUID,
                                         BrowserContract.Bookmarks.UNFILED_FOLDER_GUID,
                                         BrowserContract.Bookmarks.READING_LIST_FOLDER_GUID });
 
         c = mProvider.query(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), null, null, null, null);
-        mAsserter.is(c.getCount(), 7, "All non-special bookmarks and folders were deleted");
+        assertCountIsAndClose(c, 7, "All non-special bookmarks and folders were deleted");
 
         mProvider.delete(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), null, null);
         c = mProvider.query(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), null, null, null, null);
-        mAsserter.is(c.getCount(), 0, "All history entries were deleted");
+        assertCountIsAndClose(c, 0, "All history entries were deleted");
 
         mProvider.delete(BrowserContract.Favicons.CONTENT_URI, null, null);
-        mAsserter.is(c.getCount(), 0, "All favicons were deleted");
+        c = mProvider.query(appendUriParam(BrowserContract.Favicons.CONTENT_URI,
+                                           BrowserContract.PARAM_SHOW_DELETED, "1"),
+                                           null, null, null, null);
+        assertCountIsAndClose(c, 0, "All favicons were deleted");
 
         mProvider.delete(BrowserContract.Thumbnails.CONTENT_URI, null, null);
-        mAsserter.is(c.getCount(), 0, "All thumbnails were deleted");
+        c = mProvider.query(appendUriParam(BrowserContract.Thumbnails.CONTENT_URI,
+                                           BrowserContract.PARAM_SHOW_DELETED, "1"),
+                                           null, null, null, null);
+        assertCountIsAndClose(c, 0, "All thumbnails were deleted");
     }
 
     private ContentValues createBookmark(String title, String url, long parentId,
             int type, int position, String tags, String description, String keyword) throws Exception {
         ContentValues bookmark = new ContentValues();
 
         bookmark.put(BrowserContract.Bookmarks.TITLE, title);
         bookmark.put(BrowserContract.Bookmarks.URL, url);
@@ -321,31 +327,31 @@ public class testBrowserProvider extends
             try {
                 applyResult = mProvider.applyBatch(mOperations);
             } catch (OperationApplicationException ex) {
                 seenException = true;
             }
             mAsserter.is(seenException, false, "Batch updating succeded");
             mOperations.clear();
 
-            // Delte all visits
+            // Delete all visits
             for (int i = 0; i < TESTCOUNT; i++) {
                 builder = ContentProviderOperation.newDelete(BrowserContract.History.CONTENT_URI);
                 builder.withSelection(BrowserContract.History.URL  + " = ?",
                                       new String[] {"http://www.test.org/" + i});
                 builder.withExpectedCount(1);
                 // Queue the operation
                 mOperations.add(builder.build());
             }
             try {
                 applyResult = mProvider.applyBatch(mOperations);
             } catch (OperationApplicationException ex) {
                 seenException = true;
             }
-            mAsserter.is(seenException, false, "Batch deletion succeded");
+            mAsserter.is(seenException, false, "Batch deletion succeeded");
         }
 
         // Force a Constraint error, see if later operations still apply correctly
         public void testApplyBatchErrors() throws Exception {
             ArrayList<ContentProviderOperation> mOperations
                 = new ArrayList<ContentProviderOperation>();
 
             // Test a bunch of inserts with applyBatch
@@ -470,16 +476,18 @@ public class testBrowserProvider extends
                     mAsserter.is(new Integer(id), new Integer(rootId), "The id of places folder is correct");
                 } else if (guid.equals(BrowserContract.Bookmarks.READING_LIST_FOLDER_GUID)) {
                     mAsserter.is(new Integer(id), new Integer(readingListId), "The id of reading list folder is correct");
                 }
 
                 mAsserter.is(new Integer(parentId), new Integer(rootId),
                              "The PARENT of the " + guid + " special folder is correct");
             }
+
+            c.close();
         }
     }
 
     class TestInsertBookmarks extends Test {
         private long insertWithNullCol(String colName) throws Exception {
             ContentValues b = createOneBookmark();
             b.putNull(colName);
             long id = -1;
@@ -544,16 +552,17 @@ public class testBrowserProvider extends
             b.remove(BrowserContract.Bookmarks.TYPE);
             id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
             c = getBookmarkById(id);
 
             mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
 
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TYPE)), String.valueOf(BrowserContract.Bookmarks.TYPE_BOOKMARK),
                          "Inserted bookmark has correct default type");
+            c.close();
         }
     }
 
     class TestInsertBookmarksFavicons extends Test {
         @Override
         public void test() throws Exception {
             ContentValues b = createOneBookmark();
 
@@ -566,83 +575,91 @@ public class testBrowserProvider extends
             mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, favicon));
 
             Cursor c = getBookmarkById(id, new String[] { BrowserContract.Bookmarks.FAVICON });
 
             mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Bookmarks.FAVICON)), "UTF8"),
                          favicon, "Inserted bookmark has corresponding favicon image");
+            c.close();
 
             c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
                          favicon, "Inserted favicon has corresponding favicon image");
+            c.close();
         }
     }
 
     class TestDeleteBookmarks extends Test {
         private long insertOneBookmark() throws Exception {
             ContentValues b = createOneBookmark();
             long id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
 
             Cursor c = getBookmarkById(id);
             mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
+            c.close();
 
             return id;
         }
 
         @Override
         public void test() throws Exception {
             long id = insertOneBookmark();
 
             int deleted = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI,
                                            BrowserContract.Bookmarks._ID + " = ?",
                                            new String[] { String.valueOf(id) });
 
             mAsserter.is((deleted == 1), true, "Inserted bookmark was deleted");
 
             Cursor c = getBookmarkById(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
             mAsserter.is(c.moveToFirst(), true, "Deleted bookmark was only marked as deleted");
+            c.close();
 
             deleted = mProvider.delete(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"),
                                        BrowserContract.Bookmarks._ID + " = ?",
                                        new String[] { String.valueOf(id) });
 
             mAsserter.is((deleted == 1), true, "Inserted bookmark was deleted");
 
             c = getBookmarkById(appendUriParam(BrowserContract.Bookmarks.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
             mAsserter.is(c.moveToFirst(), false, "Inserted bookmark is now actually deleted");
+            c.close();
 
             id = insertOneBookmark();
 
             deleted = mProvider.delete(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), null, null);
             mAsserter.is((deleted == 1), true,
                          "Inserted bookmark was deleted using URI with id");
 
             c = getBookmarkById(id);
             mAsserter.is(c.moveToFirst(), false,
                          "Inserted bookmark can't be found after deletion using URI with ID");
+            c.close();
 
             if (Build.VERSION.SDK_INT >= 8 &&
                 Build.VERSION.SDK_INT < 16) {
                 ContentValues b = createBookmark("Folder", null, mMobileFolderId,
                         BrowserContract.Bookmarks.TYPE_FOLDER, 0, "folderTags", "folderDescription", "folderKeyword");
 
                 long parentId = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
                 c = getBookmarkById(parentId);
                 mAsserter.is(c.moveToFirst(), true, "Inserted bookmarks folder found");
+                c.close();
 
                 b = createBookmark("Example", "http://example.com", parentId,
                         BrowserContract.Bookmarks.TYPE_BOOKMARK, 0, "tags", "description", "keyword");
 
                 id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
                 c = getBookmarkById(id);
                 mAsserter.is(c.moveToFirst(), true, "Inserted bookmark found");
+                c.close();
 
                 deleted = 0;
                 try {
                     Uri uri = ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, parentId);
                     deleted = mProvider.delete(appendUriParam(uri, BrowserContract.PARAM_IS_SYNC, "1"), null, null);
                 } catch(Exception e) {}
 
                 mAsserter.is((deleted == 0), true,
@@ -659,21 +676,23 @@ public class testBrowserProvider extends
             final String pageUrl = b.getAsString(BrowserContract.Bookmarks.URL);
             long id = ContentUris.parseId(mProvider.insert(BrowserContract.Bookmarks.CONTENT_URI, b));
 
             // Insert the favicon into the favicons table
             mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, "FAVICON"));
 
             Cursor c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
+            c.close();
 
             mProvider.delete(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), null, null);
 
             c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), false, "Favicon is deleted with last reference to it");
+            c.close();
         }
     }
 
     class TestUpdateBookmarks extends Test {
         private int updateWithNullCol(long id, String colName) throws Exception {
             ContentValues u = new ContentValues();
             u.putNull(colName);
 
@@ -708,16 +727,17 @@ public class testBrowserProvider extends
             u.put(BrowserContract.Bookmarks.TYPE, BrowserContract.Bookmarks.TYPE_FOLDER);
             u.put(BrowserContract.Bookmarks.POSITION, 10);
 
             int updated = mProvider.update(BrowserContract.Bookmarks.CONTENT_URI, u,
                                            BrowserContract.Bookmarks._ID + " = ?",
                                            new String[] { String.valueOf(id) });
 
             mAsserter.is((updated == 1), true, "Inserted bookmark was updated");
+            c.close();
 
             c = getBookmarkById(id);
             mAsserter.is(c.moveToFirst(), true, "Updated bookmark found");
 
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.TITLE)), u.getAsString(BrowserContract.Bookmarks.TITLE),
                          "Inserted bookmark has correct title");
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.URL)), u.getAsString(BrowserContract.Bookmarks.URL),
                          "Inserted bookmark has correct URL");
@@ -747,22 +767,24 @@ public class testBrowserProvider extends
             updated = updateWithNullCol(id, BrowserContract.Bookmarks.TYPE);
             mAsserter.is((updated > 0), false,
                          "Should not be able to update bookmark with null type");
 
             u = new ContentValues();
             u.put(BrowserContract.Bookmarks.URL, "http://examples2.com");
 
             updated = mProvider.update(ContentUris.withAppendedId(BrowserContract.Bookmarks.CONTENT_URI, id), u, null, null);
+            c.close();
 
             c = getBookmarkById(id);
             mAsserter.is(c.moveToFirst(), true, "Updated bookmark found");
 
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Bookmarks.URL)), u.getAsString(BrowserContract.Bookmarks.URL),
                          "Updated bookmark has correct URL using URI with id");
+            c.close();
         }
     }
 
     class TestUpdateBookmarksFavicons extends Test {
         @Override
         public void test() throws Exception {
             ContentValues b = createOneBookmark();
 
@@ -779,22 +801,24 @@ public class testBrowserProvider extends
             Cursor c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
                          favicon, "Inserted favicon has corresponding favicon image");
 
             ContentValues u = createFaviconEntry(pageUrl, newFavicon);
             mProvider.update(BrowserContract.Favicons.CONTENT_URI, u, null, null);
+            c.close();
 
             c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Updated favicon found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
                          newFavicon, "Updated favicon has corresponding favicon image");
+            c.close();
         }
     }
 
     /**
      * Create a folder of one thousand and one bookmarks, then impose an order
      * on them.
      *
      * Verify that the reordering worked by querying.
@@ -820,16 +844,17 @@ public class testBrowserProvider extends
                     mAsserter.is(pos, (long) i, "Position matches sequence.");
                     mAsserter.is(guid, items[i], "GUID matches sequence.");
                 }
                 ++i;
                 c.moveToNext();
             }
 
             mAsserter.is(i, count, "Folder has the right number of children.");
+            c.close();
         }
 
         public static final int NUMBER_OF_CHILDREN = 1001;
         @Override
         public void test() throws Exception {
             // Create the containing folder.
             ContentValues folder = createBookmark("FolderFolder", "", mMobileFolderId,
                                                   BrowserContract.Bookmarks.TYPE_FOLDER, 0, "",
@@ -916,16 +941,17 @@ public class testBrowserProvider extends
 
             id = insertWithNullCol(BrowserContract.History.URL);
             mAsserter.is(new Long(id), new Long(-1),
                          "Should not be able to insert history with null URL");
 
             id = insertWithNullCol(BrowserContract.History.VISITS);
             mAsserter.is(new Long(id), new Long(-1),
                          "Should not be able to insert history with null number of visits");
+            c.close();
         }
     }
 
     class TestInsertHistoryFavicons extends Test {
         @Override
         public void test() throws Exception {
             ContentValues h = createOneHistoryEntry();
 
@@ -938,32 +964,35 @@ public class testBrowserProvider extends
             mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, favicon));
 
             Cursor c = getHistoryEntryById(id, new String[] { BrowserContract.History.FAVICON });
 
             mAsserter.is(c.moveToFirst(), true, "Inserted history entry found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.History.FAVICON)), "UTF8"),
                          favicon, "Inserted history entry has corresponding favicon image");
+            c.close();
 
             c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
                          favicon, "Inserted favicon has corresponding favicon image");
+            c.close();
         }
     }
 
     class TestDeleteHistory extends Test {
         private long insertOneHistoryEntry() throws Exception {
             ContentValues h = createOneHistoryEntry();
             long id = ContentUris.parseId(mProvider.insert(BrowserContract.History.CONTENT_URI, h));
 
             Cursor c = getHistoryEntryById(id);
             mAsserter.is(c.moveToFirst(), true, "Inserted history entry found");
+            c.close();
 
             return id;
         }
 
         @Override
         public void test() throws Exception {
             long id = insertOneHistoryEntry();
 
@@ -976,29 +1005,32 @@ public class testBrowserProvider extends
             Cursor c = getHistoryEntryById(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
             mAsserter.is(c.moveToFirst(), true, "Deleted history entry was only marked as deleted");
 
             deleted = mProvider.delete(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"),
                                        BrowserContract.History._ID + " = ?",
                                        new String[] { String.valueOf(id) });
 
             mAsserter.is((deleted == 1), true, "Inserted history entry was deleted");
+            c.close();
 
             c = getHistoryEntryById(appendUriParam(BrowserContract.History.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"), id);
             mAsserter.is(c.moveToFirst(), false, "Inserted history is now actually deleted");
 
             id = insertOneHistoryEntry();
 
             deleted = mProvider.delete(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), null, null);
             mAsserter.is((deleted == 1), true,
                          "Inserted history entry was deleted using URI with id");
+            c.close();
 
             c = getHistoryEntryById(id);
             mAsserter.is(c.moveToFirst(), false,
                          "Inserted history entry can't be found after deletion using URI with ID");
+            c.close();
         }
     }
 
     class TestDeleteHistoryFavicons extends Test {
         @Override
         public void test() throws Exception {
             ContentValues h = createOneHistoryEntry();
 
@@ -1007,19 +1039,21 @@ public class testBrowserProvider extends
 
             // Insert the favicon into the favicons table
             mProvider.insert(BrowserContract.Favicons.CONTENT_URI, createFaviconEntry(pageUrl, "FAVICON"));
 
             Cursor c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
 
             mProvider.delete(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), null, null);
+            c.close();
 
             c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), false, "Favicon is deleted with last reference to it");
+            c.close();
         }
     }
 
     class TestUpdateHistory extends Test {
         private int updateWithNullCol(long id, String colName) throws Exception {
             ContentValues u = new ContentValues();
             u.putNull(colName);
 
@@ -1051,16 +1085,17 @@ public class testBrowserProvider extends
             u.put(BrowserContract.History.TITLE, h.getAsString(BrowserContract.History.TITLE) + "CHANGED");
             u.put(BrowserContract.History.URL, h.getAsString(BrowserContract.History.URL) + "/more/stuff");
 
             int updated = mProvider.update(BrowserContract.History.CONTENT_URI, u,
                                            BrowserContract.History._ID + " = ?",
                                            new String[] { String.valueOf(id) });
 
             mAsserter.is((updated == 1), true, "Inserted history entry was updated");
+            c.close();
 
             c = getHistoryEntryById(id);
             mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
 
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), u.getAsString(BrowserContract.History.TITLE),
                          "Updated history entry has correct title");
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), u.getAsString(BrowserContract.History.URL),
                          "Updated history entry has correct URL");
@@ -1084,22 +1119,24 @@ public class testBrowserProvider extends
             updated = updateWithNullCol(id, BrowserContract.History.VISITS);
             mAsserter.is((updated > 0), false,
                          "Should not be able to update history with null number of visits");
 
             u = new ContentValues();
             u.put(BrowserContract.History.URL, "http://examples2.com");
 
             updated = mProvider.update(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), u, null, null);
+            c.close();
 
             c = getHistoryEntryById(id);
             mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
 
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), u.getAsString(BrowserContract.History.URL),
                          "Updated history entry has correct URL using URI with id");
+            c.close();
         }
     }
 
     class TestUpdateHistoryFavicons extends Test {
         @Override
         public void test() throws Exception {
             ContentValues h = createOneHistoryEntry();
 
@@ -1116,22 +1153,24 @@ public class testBrowserProvider extends
             mAsserter.is(c.moveToFirst(), true, "Inserted favicon found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
                          favicon, "Inserted favicon has corresponding favicon image");
 
             ContentValues u = createFaviconEntry(pageUrl, newFavicon);
 
             mProvider.update(BrowserContract.Favicons.CONTENT_URI, u, null, null);
+            c.close();
 
             c = getFaviconsByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Updated favicon found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Combined.FAVICON)), "UTF8"),
                          newFavicon, "Updated favicon has corresponding favicon image");
+            c.close();
         }
     }
 
     class TestUpdateOrInsertHistory extends Test {
         private final String TEST_URL_1 = "http://example.com";
         private final String TEST_URL_2 = "http://example.org";
         private final String TEST_TITLE = "Example";
 
@@ -1190,16 +1229,17 @@ public class testBrowserProvider extends
             values = new ContentValues();
             values.put(BrowserContract.History.DATE_LAST_VISITED, System.currentTimeMillis());
             values.put(BrowserContract.History.TITLE, TEST_TITLE);
 
             updated = mProvider.update(updateOrInsertHistoryUri, values,
                                        BrowserContract.History._ID + " = ?",
                                        new String[] { String.valueOf(id) });
             mAsserter.is((updated == 1), true, "Inserted history entry was updated");
+            c.close();
 
             c = getHistoryEntryById(id);
             mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
 
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), TEST_TITLE,
                          "Updated history entry has correct title");
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS))), new Long(2),
                          "Updated history entry has correct number of visits");
@@ -1215,16 +1255,18 @@ public class testBrowserProvider extends
             values.put(BrowserContract.History.VISITS, 10);
 
             updated = mProvider.update(updateOrInsertHistoryUri, values,
                                            BrowserContract.History.URL + " = ?",
                                            new String[] { values.getAsString(BrowserContract.History.URL) });
             mAsserter.is((updated == 1), true, "History entry was inserted");
 
             id = getHistoryEntryIdByUrl(TEST_URL_2);
+            c.close();
+
             c = getHistoryEntryById(id);
             mAsserter.is(c.moveToFirst(), true, "History entry was inserted");
 
             dateCreated = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED));
             dateModified = c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED));
 
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS))), new Long(10),
                          "Inserted history entry has correct specified number of visits");
@@ -1234,30 +1276,32 @@ public class testBrowserProvider extends
             // Update the history entry, specifying additional visit count
             values = new ContentValues();
             values.put(BrowserContract.History.VISITS, 10);
 
             updated = mProvider.update(updateOrInsertHistoryUri, values,
                                        BrowserContract.History._ID + " = ?",
                                        new String[] { String.valueOf(id) });
             mAsserter.is((updated == 1), true, "Inserted history entry was updated");
+            c.close();
 
             c = getHistoryEntryById(id);
             mAsserter.is(c.moveToFirst(), true, "Updated history entry found");
 
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.TITLE)), TEST_TITLE,
                          "Updated history entry has correct unchanged title");
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.History.URL)), TEST_URL_2,
                          "Updated history entry has correct unchanged URL");
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.VISITS))), new Long(20),
                          "Updated history entry has correct number of visits");
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_CREATED))), new Long(dateCreated),
                          "Updated history entry has same creation date");
             mAsserter.isnot(new Long(c.getLong(c.getColumnIndex(BrowserContract.History.DATE_MODIFIED))), new Long(dateModified),
                             "Updated history entry has new modification date");
+            c.close();
 
         }
     }
 
     class TestInsertHistoryThumbnails extends Test {
         @Override
         public void test() throws Exception {
             ContentValues h = createOneHistoryEntry();
@@ -1270,16 +1314,17 @@ public class testBrowserProvider extends
             // Insert the thumbnail into the thumbnails table
             mProvider.insert(BrowserContract.Thumbnails.CONTENT_URI, createThumbnailEntry(pageUrl, thumbnail));
 
             Cursor c = getThumbnailByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Inserted thumbnail found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Thumbnails.DATA)), "UTF8"),
                          thumbnail, "Inserted thumbnail has corresponding thumbnail image");
+            c.close();
         }
     }
 
     class TestUpdateHistoryThumbnails extends Test {
         @Override
         public void test() throws Exception {
             ContentValues h = createOneHistoryEntry();
 
@@ -1296,22 +1341,24 @@ public class testBrowserProvider extends
             mAsserter.is(c.moveToFirst(), true, "Inserted thumbnail found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Thumbnails.DATA)), "UTF8"),
                          thumbnail, "Inserted thumbnail has corresponding thumbnail image");
 
             ContentValues u = createThumbnailEntry(pageUrl, newThumbnail);
 
             mProvider.update(BrowserContract.Thumbnails.CONTENT_URI, u, null, null);
+            c.close();
 
             c = getThumbnailByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Updated thumbnail found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(BrowserContract.Thumbnails.DATA)), "UTF8"),
                          newThumbnail, "Updated thumbnail has corresponding thumbnail image");
+            c.close();
         }
     }
 
     class TestDeleteHistoryThumbnails extends Test {
         @Override
         public void test() throws Exception {
             ContentValues h = createOneHistoryEntry();
 
@@ -1320,19 +1367,21 @@ public class testBrowserProvider extends
 
             // Insert the thumbnail into the thumbnails table
             mProvider.insert(BrowserContract.Thumbnails.CONTENT_URI, createThumbnailEntry(pageUrl, "THUMBNAIL"));
 
             Cursor c = getThumbnailByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), true, "Inserted thumbnail found");
 
             mProvider.delete(ContentUris.withAppendedId(BrowserContract.History.CONTENT_URI, id), null, null);
+            c.close();
 
             c = getThumbnailByUrl(pageUrl);
             mAsserter.is(c.moveToFirst(), false, "Thumbnail is deleted with last reference to it");
+            c.close();
         }
     }
 
     class TestCombinedView extends Test {
         @Override
         public void test() throws Exception {
             final String TITLE_1 = "Test Page 1";
             final String TITLE_2 = "Test Page 2";
@@ -1430,16 +1479,17 @@ public class testBrowserProvider extends
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.HISTORY_ID))), new Long(combinedHistoryId),
                          "Combined entry has correct history id");
             mAsserter.is(c.getString(c.getColumnIndex(BrowserContract.Combined.URL)), URL_3,
                          "Combined entry has correct url");
             mAsserter.is(c.getInt(c.getColumnIndex(BrowserContract.Combined.VISITS)), VISITS,
                          "Combined entry has correct number of visits");
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DATE_LAST_VISITED))), new Long(LAST_VISITED),
                          "Combined entry has correct last visit time");
+            c.close();
         }
     }
 
     class TestCombinedViewDisplay extends Test {
         @Override
         public void test() throws Exception {
             final String TITLE_1 = "Test Page 1";
             final String TITLE_2 = "Test Page 2";
@@ -1490,16 +1540,17 @@ public class testBrowserProvider extends
                 long id = c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID));
 
                 int display = c.getInt(c.getColumnIndex(BrowserContract.Combined.DISPLAY));
                 int expectedDisplay = (id == readingListItemId || id == readingListItemId2 ? BrowserContract.Combined.DISPLAY_READER : BrowserContract.Combined.DISPLAY_NORMAL);
 
                 mAsserter.is(new Integer(display), new Integer(expectedDisplay),
                                  "Combined display column should always be DISPLAY_READER for the reading list item");
             }
+            c.close();
         }
     }
 
     class TestCombinedViewWithDeletedBookmark extends Test {
         @Override
         public void test() throws Exception {
             final String TITLE = "Test Page 1";
             final String URL = "http://example.com";
@@ -1522,23 +1573,25 @@ public class testBrowserProvider extends
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(combinedBookmarkId),
                          "Bookmark id should be set correctly on combined entry");
 
             int deleted = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI,
                                            BrowserContract.Bookmarks._ID + " = ?",
                                            new String[] { String.valueOf(combinedBookmarkId) });
 
             mAsserter.is((deleted == 1), true, "Inserted combined bookmark was deleted");
+            c.close();
 
             c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);
             mAsserter.is(c.getCount(), 1, "1 combined entry found");
 
             mAsserter.is(c.moveToFirst(), true, "Found combined entry without bookmark id");
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(0),
                          "Bookmark id should not be set to removed bookmark id");
+            c.close();
         }
     }
 
     class TestCombinedViewWithDeletedReadingListItem extends Test {
         @Override
         public void test() throws Exception {
             final String TITLE = "Test Page 1";
             final String URL = "http://example.com";
@@ -1564,25 +1617,27 @@ public class testBrowserProvider extends
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DISPLAY))), new Long(BrowserContract.Combined.DISPLAY_READER),
                          "Combined entry should have reader display type");
 
             int deleted = mProvider.delete(BrowserContract.Bookmarks.CONTENT_URI,
                                            BrowserContract.Bookmarks._ID + " = ?",
                                            new String[] { String.valueOf(combinedReadingListItemId) });
 
             mAsserter.is((deleted == 1), true, "Inserted combined reading list item was deleted");
+            c.close();
 
             c = mProvider.query(BrowserContract.Combined.CONTENT_URI, null, "", null, null);
             mAsserter.is(c.getCount(), 1, "1 combined entry found");
 
             mAsserter.is(c.moveToFirst(), true, "Found combined entry without bookmark id");
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.BOOKMARK_ID))), new Long(0),
                          "Bookmark id should not be set to removed bookmark id");
             mAsserter.is(new Long(c.getLong(c.getColumnIndex(BrowserContract.Combined.DISPLAY))), new Long(BrowserContract.Combined.DISPLAY_NORMAL),
                          "Combined entry should have reader display type");
+            c.close();
         }
     }
 
     class TestExpireHistory extends Test {
         private void createFakeHistory(long timeShift, int count) {
             // Insert a bunch of very new entries
             ContentValues[] allVals = new ContentValues[count];
             long time = System.currentTimeMillis() - timeShift;
@@ -1603,96 +1658,101 @@ public class testBrowserProvider extends
                 ContentValues cv = new ContentValues();
                 cv.put(BrowserContract.History.DATE_CREATED, time);
                 cv.put(BrowserContract.History.DATE_MODIFIED, time);
                 mProvider.update(BrowserContract.History.CONTENT_URI, cv, BrowserContract.History.URL + " = ?",
                                  new String[] { "http://www.test.org/" + i });
             }
 
             Cursor c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
-            mAsserter.is(c.getCount(), count, count + " history entries found");
+
+            assertCountIsAndClose(c, count, count + " history entries found");
 
             // add thumbnails for each entry
             allVals = new ContentValues[count];
             for (int i = 0; i < count; i++) {
                 allVals[i] = new ContentValues();
                 allVals[i].put(BrowserContract.Thumbnails.DATA, i);
                 allVals[i].put(BrowserContract.Thumbnails.URL, "http://www.test.org/" + i);
             }
 
             inserts = mProvider.bulkInsert(BrowserContract.Thumbnails.CONTENT_URI, allVals);
             mAsserter.is(inserts, count, "Expected number of inserts matches");
 
             c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
-            mAsserter.is(c.getCount(), count, count + " thumbnails entries found");
+            assertCountIsAndClose(c, count, count + " thumbnails entries found");
         }
 
         @Override
         public void test() throws Exception {
             final int count = 3000;
             final int thumbCount = 15;
 
             // insert a bunch of new entries
             createFakeHistory(0, count);
 
             // expiring with a normal priority should not delete new entries
             Uri url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "NORMAL");
             mProvider.delete(url, null, null);
             Cursor c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
-            mAsserter.is(c.getCount(), count, count + " history entries found");
+            assertCountIsAndClose(c, count, count + " history entries found");
 
             // expiring with a normal priority should delete all but 10 thumbnails
             c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
-            mAsserter.is(c.getCount(), thumbCount, thumbCount + " thumbnails found");
+            assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
 
             ensureEmptyDatabase();
-            // insert a bunch of new entries
+
+            // Insert a bunch of new entries.
             createFakeHistory(0, count);
 
-            // expiring with a aggressive priority should leave 500 entries
+            // Expiring with a aggressive priority should leave 500 entries.
             url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "AGGRESSIVE");
             mProvider.delete(url, null, null);
+
             c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
-            mAsserter.is(c.getCount(), 500, "500 history entries found");
+            assertCountIsAndClose(c, 500, "500 history entries found");
 
-            // expiring with a aggressive priority should delete all but 10 thumbnails
+            // Expiring with a aggressive priority should delete all but 10 thumbnails.
             c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
-            mAsserter.is(c.getCount(), thumbCount, thumbCount + " thumbnails found");
+            assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
 
             ensureEmptyDatabase();
-            // insert a bunch of entries with an old time created/modified
+
+            // Insert a bunch of entries with an old time created/modified.
             long time = 1000L * 60L * 60L * 24L * 30L * 3L;
             createFakeHistory(time, count);
 
-            // expiring with an normal priority should remove at most 1000 entries
-            // entries leaving at least 2000
+            // Expiring with an normal priority should remove at most 1000 entries,
+            // entries leaving at least 2000.
             url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "NORMAL");
             mProvider.delete(url, null, null);
+
             c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
-            mAsserter.is(c.getCount(), 2000, "2000 history entries found");
+            assertCountIsAndClose(c, 2000, "2000 history entries found");
 
-            // expiring with a normal priority should delete all but 10 thumbnails
+            // Expiring with a normal priority should delete all but 10 thumbnails.
             c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
-            mAsserter.is(c.getCount(), thumbCount, thumbCount + " thumbnails found");
+            assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
 
             ensureEmptyDatabase();
             // insert a bunch of entries with an old time created/modified
             time = 1000L * 60L * 60L * 24L * 30L * 3L;
             createFakeHistory(time, count);
 
-            // expiring with an agressive priority should remove old
-            // entries leaving at least 500
+            // Expiring with an aggressive priority should remove old
+            // entries, leaving at least 500.
             url = appendUriParam(BrowserContract.History.CONTENT_OLD_URI, BrowserContract.PARAM_EXPIRE_PRIORITY, "AGGRESSIVE");
             mProvider.delete(url, null, null);
             c = mProvider.query(BrowserContract.History.CONTENT_URI, null, "", null, null);
-            mAsserter.is(c.getCount(), 500, "500 history entries found");
+            assertCountIsAndClose(c, 500, "500 history entries found");
 
             // expiring with an aggressive priority should delete all but 10 thumbnails
             c = mProvider.query(BrowserContract.Thumbnails.CONTENT_URI, null, null, null, null);
-            mAsserter.is(c.getCount(), thumbCount, thumbCount + " thumbnails found");
+            assertCountIsAndClose(c, thumbCount, thumbCount + " thumbnails found");
         }
     }
 
     /*
      * Verify that insert, update, delete, and bulkInsert operations
      * notify the ambient content resolver.  Each operation calls the
      * content resolver notifyChange method synchronously, so it is
      * okay to test sequentially.
@@ -1771,9 +1831,21 @@ public class testBrowserProvider extends
 
             mAsserter.is(Long.valueOf(numBulkInserted),
                          Long.valueOf(1),
                          "Correct number of items are bulkInserted");
 
             ensureOnlyChangeNotifiedStartsWith(BrowserContract.History.CONTENT_URI, "bulkInsert");
         }
     }
+
+    /**
+     * Assert that the provided cursor has the expected number of rows,
+     * closing the cursor afterwards.
+     */
+    private void assertCountIsAndClose(Cursor c, int expectedCount, String message) {
+        try {
+            mAsserter.is(c.getCount(), expectedCount, message);
+        } finally {
+            c.close();
+        }
+    }
 }