1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/tests/testReadingListProvider.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,558 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +package org.mozilla.gecko.tests; 1.9 + 1.10 +import java.util.HashSet; 1.11 +import java.util.Random; 1.12 +import java.util.concurrent.Callable; 1.13 + 1.14 +import org.mozilla.gecko.db.BrowserContract; 1.15 +import org.mozilla.gecko.db.BrowserContract.ReadingListItems; 1.16 +import org.mozilla.gecko.db.ReadingListProvider; 1.17 + 1.18 +import android.content.ContentProvider; 1.19 +import android.content.ContentUris; 1.20 +import android.content.ContentValues; 1.21 +import android.database.Cursor; 1.22 +import android.database.sqlite.SQLiteDatabase; 1.23 +import android.net.Uri; 1.24 + 1.25 +public class testReadingListProvider extends ContentProviderTest { 1.26 + 1.27 + private static final String DB_NAME = "browser.db"; 1.28 + 1.29 + // List of tests to be run sorted by dependency. 1.30 + private final TestCase[] TESTS_TO_RUN = { new TestInsertItems(), 1.31 + new TestDeleteItems(), 1.32 + new TestUpdateItems(), 1.33 + new TestBatchOperations(), 1.34 + new TestBrowserProviderNotifications() }; 1.35 + 1.36 + // Columns used to test for item equivalence. 1.37 + final String[] TEST_COLUMNS = { ReadingListItems.TITLE, 1.38 + ReadingListItems.URL, 1.39 + ReadingListItems.EXCERPT, 1.40 + ReadingListItems.LENGTH, 1.41 + ReadingListItems.DATE_CREATED }; 1.42 + 1.43 + // Indicates that insertions have been tested. ContentProvider.insert 1.44 + // has been proven to work. 1.45 + private boolean mContentProviderInsertTested = false; 1.46 + 1.47 + // Indicates that updates have been tested. ContentProvider.update 1.48 + // has been proven to work. 1.49 + private boolean mContentProviderUpdateTested = false; 1.50 + 1.51 + /** 1.52 + * Factory function that makes new ReadingListProvider instances. 1.53 + * <p> 1.54 + * We want a fresh provider each test, so this should be invoked in 1.55 + * <code>setUp</code> before each individual test. 1.56 + */ 1.57 + private static Callable<ContentProvider> sProviderFactory = new Callable<ContentProvider>() { 1.58 + @Override 1.59 + public ContentProvider call() { 1.60 + return new ReadingListProvider(); 1.61 + } 1.62 + }; 1.63 + 1.64 + @Override 1.65 + public void setUp() throws Exception { 1.66 + super.setUp(sProviderFactory, BrowserContract.READING_LIST_AUTHORITY, DB_NAME); 1.67 + for (TestCase test: TESTS_TO_RUN) { 1.68 + mTests.add(test); 1.69 + } 1.70 + } 1.71 + 1.72 + public void testReadingListProviderTests() throws Exception { 1.73 + for (Runnable test : mTests) { 1.74 + setTestName(test.getClass().getSimpleName()); 1.75 + ensureEmptyDatabase(); 1.76 + test.run(); 1.77 + } 1.78 + 1.79 + // Ensure browser initialization is complete before completing test, 1.80 + // so that the minidumps directory is consistently created. 1.81 + blockForGeckoReady(); 1.82 + } 1.83 + 1.84 + /** 1.85 + * Verify that we can insert a reading list item into the DB. 1.86 + */ 1.87 + private class TestInsertItems extends TestCase { 1.88 + @Override 1.89 + public void test() throws Exception { 1.90 + ContentValues b = createFillerReadingListItem(); 1.91 + long id = ContentUris.parseId(mProvider.insert(ReadingListItems.CONTENT_URI, b)); 1.92 + Cursor c = getItemById(id); 1.93 + 1.94 + try { 1.95 + mAsserter.ok(c.moveToFirst(), "Inserted item found", ""); 1.96 + assertRowEqualsContentValues(c, b); 1.97 + } finally { 1.98 + c.close(); 1.99 + } 1.100 + 1.101 + testInsertWithNullCol(ReadingListItems.GUID); 1.102 + mContentProviderInsertTested = true; 1.103 + } 1.104 + 1.105 + /** 1.106 + * Test that insertion fails when a required column 1.107 + * is null. 1.108 + */ 1.109 + private void testInsertWithNullCol(String colName) { 1.110 + ContentValues b = createFillerReadingListItem(); 1.111 + b.putNull(colName); 1.112 + 1.113 + try { 1.114 + ContentUris.parseId(mProvider.insert(ReadingListItems.CONTENT_URI, b)); 1.115 + // If we get to here, the flawed insertion succeeded. Fail the test. 1.116 + mAsserter.ok(false, "Insertion did not succeed with " + colName + " == null", ""); 1.117 + } catch (NullPointerException e) { 1.118 + // Indicates test was successful. 1.119 + } 1.120 + } 1.121 + } 1.122 + 1.123 + /** 1.124 + * Verify that we can remove a reading list item from the DB. 1.125 + */ 1.126 + private class TestDeleteItems extends TestCase { 1.127 + 1.128 + @Override 1.129 + public void test() throws Exception { 1.130 + long id = insertAnItemWithAssertion(); 1.131 + // Test that the item is only marked as deleted and 1.132 + // not removed from the database. 1.133 + testNonFirefoxSyncDelete(id); 1.134 + 1.135 + // Test that the item is removed from the database. 1.136 + testFirefoxSyncDelete(id); 1.137 + 1.138 + id = insertAnItemWithAssertion(); 1.139 + // Test that deleting works with only a URI. 1.140 + testDeleteWithItemURI(id); 1.141 + } 1.142 + 1.143 + /** 1.144 + * Delete an item with PARAM_IS_SYNC unset and verify that item was only marked 1.145 + * as deleted and not actually removed from the database. Also verify that the item 1.146 + * marked as deleted doesn't show up in a query. 1.147 + * 1.148 + * @param id of the item to be deleted 1.149 + */ 1.150 + private void testNonFirefoxSyncDelete(long id) { 1.151 + final int deleted = mProvider.delete(ReadingListItems.CONTENT_URI, 1.152 + ReadingListItems._ID + " = ?", 1.153 + new String[] { String.valueOf(id) }); 1.154 + 1.155 + mAsserter.is(deleted, 1, "Inserted item was deleted"); 1.156 + 1.157 + // PARAM_SHOW_DELETED in the URI allows items marked as deleted to be 1.158 + // included in the query. 1.159 + Uri uri = appendUriParam(ReadingListItems.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"); 1.160 + assertItemExistsByID(uri, id, "Deleted item was only marked as deleted"); 1.161 + 1.162 + // Test that the 'deleted' item does not show up in a query when PARAM_SHOW_DELETED 1.163 + // is not specified in the URI. 1.164 + assertItemDoesNotExistByID(id, "Inserted item can't be found after deletion"); 1.165 + } 1.166 + 1.167 + /** 1.168 + * Delete an item with PARAM_IS_SYNC=1 and verify that item 1.169 + * was actually removed from the database. 1.170 + * 1.171 + * @param id of the item to be deleted 1.172 + */ 1.173 + private void testFirefoxSyncDelete(long id) { 1.174 + final int deleted = mProvider.delete(appendUriParam(ReadingListItems.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), 1.175 + ReadingListItems._ID + " = ?", 1.176 + new String[] { String.valueOf(id) }); 1.177 + 1.178 + mAsserter.is(deleted, 1, "Inserted item was deleted"); 1.179 + 1.180 + Uri uri = appendUriParam(ReadingListItems.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"); 1.181 + assertItemDoesNotExistByID(uri, id, "Inserted item is now actually deleted"); 1.182 + } 1.183 + 1.184 + /** 1.185 + * Delete an item with its URI and verify that the item 1.186 + * was actually removed from the database. 1.187 + * 1.188 + * @param id of the item to be deleted 1.189 + */ 1.190 + private void testDeleteWithItemURI(long id) { 1.191 + final int deleted = mProvider.delete(ContentUris.withAppendedId(ReadingListItems.CONTENT_URI, id), null, null); 1.192 + mAsserter.is(deleted, 1, "Inserted item was deleted using URI with id"); 1.193 + } 1.194 + } 1.195 + 1.196 + /** 1.197 + * Verify that we can update reading list items. 1.198 + */ 1.199 + private class TestUpdateItems extends TestCase { 1.200 + 1.201 + @Override 1.202 + public void test() throws Exception { 1.203 + // We should be able to insert into the DB. 1.204 + ensureCanInsert(); 1.205 + 1.206 + ContentValues original = createFillerReadingListItem(); 1.207 + long id = ContentUris.parseId(mProvider.insert(ReadingListItems.CONTENT_URI, original)); 1.208 + int updated = 0; 1.209 + Long originalDateCreated = null; 1.210 + Long originalDateModified = null; 1.211 + ContentValues updates = new ContentValues(); 1.212 + Cursor c = getItemById(id); 1.213 + try { 1.214 + mAsserter.ok(c.moveToFirst(), "Inserted item found", ""); 1.215 + 1.216 + originalDateCreated = c.getLong(c.getColumnIndex(ReadingListItems.DATE_CREATED)); 1.217 + originalDateModified = c.getLong(c.getColumnIndex(ReadingListItems.DATE_MODIFIED)); 1.218 + 1.219 + updates.put(ReadingListItems.TITLE, original.getAsString(ReadingListItems.TITLE) + "CHANGED"); 1.220 + updates.put(ReadingListItems.URL, original.getAsString(ReadingListItems.URL) + "/more/stuff"); 1.221 + updates.put(ReadingListItems.EXCERPT, original.getAsString(ReadingListItems.EXCERPT) + "CHANGED"); 1.222 + 1.223 + updated = mProvider.update(ReadingListItems.CONTENT_URI, updates, 1.224 + ReadingListItems._ID + " = ?", 1.225 + new String[] { String.valueOf(id) }); 1.226 + 1.227 + mAsserter.is(updated, 1, "Inserted item was updated"); 1.228 + } finally { 1.229 + c.close(); 1.230 + } 1.231 + 1.232 + // Name change for clarity. These values will be compared with the 1.233 + // current cursor row. 1.234 + ContentValues expectedValues = updates; 1.235 + c = getItemById(id); 1.236 + try { 1.237 + mAsserter.ok(c.moveToFirst(), "Updated item found", ""); 1.238 + mAsserter.isnot(c.getLong(c.getColumnIndex(ReadingListItems.DATE_MODIFIED)), 1.239 + originalDateModified, 1.240 + "Date modified should have changed"); 1.241 + 1.242 + // DATE_CREATED and LENGTH should equal old values since they weren't updated. 1.243 + expectedValues.put(ReadingListItems.DATE_CREATED, originalDateCreated); 1.244 + expectedValues.put(ReadingListItems.LENGTH, original.getAsString(ReadingListItems.LENGTH)); 1.245 + assertRowEqualsContentValues(c, expectedValues, /* compareDateModified */ false); 1.246 + } finally { 1.247 + c.close(); 1.248 + } 1.249 + 1.250 + // Test that updates on an item that doesn't exist does not modify any rows. 1.251 + testUpdateWithInvalidID(); 1.252 + 1.253 + // Test that update fails when a GUID is null. 1.254 + testUpdateWithNullCol(id, ReadingListItems.GUID); 1.255 + 1.256 + mContentProviderUpdateTested = true; 1.257 + } 1.258 + 1.259 + /** 1.260 + * Test that updates on an item that doesn't exist does 1.261 + * not modify any rows. 1.262 + * 1.263 + * @param id of the item to be deleted 1.264 + */ 1.265 + private void testUpdateWithInvalidID() { 1.266 + ensureEmptyDatabase(); 1.267 + final ContentValues b = createFillerReadingListItem(); 1.268 + final long id = ContentUris.parseId(mProvider.insert(ReadingListItems.CONTENT_URI, b)); 1.269 + final long INVALID_ID = id + 1; 1.270 + final ContentValues updates = new ContentValues(); 1.271 + updates.put(ReadingListItems.TITLE, b.getAsString(ReadingListItems.TITLE) + "CHANGED"); 1.272 + final int updated = mProvider.update(ReadingListItems.CONTENT_URI, updates, 1.273 + ReadingListItems._ID + " = ?", 1.274 + new String[] { String.valueOf(INVALID_ID) }); 1.275 + mAsserter.is(updated, 0, "Should not be able to update item with an invalid GUID"); 1.276 + } 1.277 + 1.278 + /** 1.279 + * Test that update fails when a required column is null. 1.280 + */ 1.281 + private int testUpdateWithNullCol(long id, String colName) { 1.282 + ContentValues updates = new ContentValues(); 1.283 + updates.putNull(colName); 1.284 + 1.285 + int updated = mProvider.update(ReadingListItems.CONTENT_URI, updates, 1.286 + ReadingListItems._ID + " = ?", 1.287 + new String[] { String.valueOf(id) }); 1.288 + 1.289 + mAsserter.is(updated, 0, "Should not be able to update item with " + colName + " == null "); 1.290 + return updated; 1.291 + } 1.292 + } 1.293 + 1.294 + private class TestBatchOperations extends TestCase { 1.295 + private static final int ITEM_COUNT = 10; 1.296 + 1.297 + /** 1.298 + * Insert a bunch of items into the DB with the bulkInsert 1.299 + * method and verify that they are there. 1.300 + */ 1.301 + private void testBulkInsert() { 1.302 + ensureEmptyDatabase(); 1.303 + final ContentValues allVals[] = new ContentValues[ITEM_COUNT]; 1.304 + final HashSet<String> urls = new HashSet<String>(); 1.305 + for (int i = 0; i < ITEM_COUNT; i++) { 1.306 + final String url = "http://www.test.org/" + i; 1.307 + allVals[i] = new ContentValues(); 1.308 + allVals[i].put(ReadingListItems.TITLE, "Test" + i); 1.309 + allVals[i].put(ReadingListItems.URL, url); 1.310 + allVals[i].put(ReadingListItems.EXCERPT, "EXCERPT" + i); 1.311 + allVals[i].put(ReadingListItems.LENGTH, i); 1.312 + urls.add(url); 1.313 + } 1.314 + 1.315 + int inserts = mProvider.bulkInsert(ReadingListItems.CONTENT_URI, allVals); 1.316 + mAsserter.is(inserts, ITEM_COUNT, "Excepted number of inserts matches"); 1.317 + 1.318 + Cursor c = mProvider.query(ReadingListItems.CONTENT_URI, null, 1.319 + null, 1.320 + null, 1.321 + null); 1.322 + try { 1.323 + while (c.moveToNext()) { 1.324 + final String url = c.getString(c.getColumnIndex(ReadingListItems.URL)); 1.325 + mAsserter.ok(urls.contains(url), "Bulk inserted item with url == " + url + " was found in the DB", ""); 1.326 + // We should only be seeing each item once. Remove from set to prevent dups. 1.327 + urls.remove(url); 1.328 + } 1.329 + } finally { 1.330 + c.close(); 1.331 + } 1.332 + } 1.333 + 1.334 + @Override 1.335 + public void test() { 1.336 + testBulkInsert(); 1.337 + } 1.338 + } 1.339 + 1.340 + /* 1.341 + * Verify that insert, update, delete, and bulkInsert operations 1.342 + * notify the ambient content resolver. Each operation calls the 1.343 + * content resolver notifyChange method synchronously, so it is 1.344 + * okay to test sequentially. 1.345 + */ 1.346 + private class TestBrowserProviderNotifications extends TestCase { 1.347 + 1.348 + @Override 1.349 + public void test() { 1.350 + // We should be able to insert into the DB. 1.351 + ensureCanInsert(); 1.352 + // We should be able to update the DB. 1.353 + ensureCanUpdate(); 1.354 + 1.355 + final String CONTENT_URI = ReadingListItems.CONTENT_URI.toString(); 1.356 + 1.357 + mResolver.notifyChangeList.clear(); 1.358 + 1.359 + // Insert 1.360 + final ContentValues h = createFillerReadingListItem(); 1.361 + long id = ContentUris.parseId(mProvider.insert(ReadingListItems.CONTENT_URI, h)); 1.362 + 1.363 + mAsserter.isnot(id, 1.364 + -1L, 1.365 + "Inserted item has valid id"); 1.366 + 1.367 + ensureOnlyChangeNotifiedStartsWith(CONTENT_URI, "insert"); 1.368 + 1.369 + // Update 1.370 + mResolver.notifyChangeList.clear(); 1.371 + h.put(ReadingListItems.TITLE, "http://newexample.com"); 1.372 + 1.373 + long numUpdated = mProvider.update(ReadingListItems.CONTENT_URI, h, 1.374 + ReadingListItems._ID + " = ?", 1.375 + new String[] { String.valueOf(id) }); 1.376 + 1.377 + mAsserter.is(numUpdated, 1.378 + 1L, 1.379 + "Correct number of items are updated"); 1.380 + 1.381 + ensureOnlyChangeNotifiedStartsWith(CONTENT_URI, "update"); 1.382 + 1.383 + // Delete 1.384 + mResolver.notifyChangeList.clear(); 1.385 + long numDeleted = mProvider.delete(ReadingListItems.CONTENT_URI, null, null); 1.386 + 1.387 + mAsserter.is(numDeleted, 1.388 + 1L, 1.389 + "Correct number of items are deleted"); 1.390 + 1.391 + ensureOnlyChangeNotifiedStartsWith(CONTENT_URI, "delete"); 1.392 + 1.393 + // Bulk insert 1.394 + mResolver.notifyChangeList.clear(); 1.395 + final ContentValues[] hs = { createFillerReadingListItem(), 1.396 + createFillerReadingListItem(), 1.397 + createFillerReadingListItem() }; 1.398 + 1.399 + long numBulkInserted = mProvider.bulkInsert(ReadingListItems.CONTENT_URI, hs); 1.400 + 1.401 + mAsserter.is(numBulkInserted, 1.402 + 3L, 1.403 + "Correct number of items are bulkInserted"); 1.404 + 1.405 + ensureOnlyChangeNotifiedStartsWith(CONTENT_URI, "bulkInsert"); 1.406 + } 1.407 + 1.408 + protected void ensureOnlyChangeNotifiedStartsWith(String expectedUri, String operation) { 1.409 + mAsserter.is(Long.valueOf(mResolver.notifyChangeList.size()), 1.410 + 1L, 1.411 + "Content observer was notified exactly once by " + operation); 1.412 + 1.413 + final Uri uri = mResolver.notifyChangeList.poll(); 1.414 + 1.415 + mAsserter.isnot(uri, 1.416 + null, 1.417 + "Notification from " + operation + " was valid"); 1.418 + 1.419 + mAsserter.ok(uri.toString().startsWith(expectedUri), 1.420 + "Content observer was notified exactly once by " + operation, 1.421 + ""); 1.422 + } 1.423 + } 1.424 + 1.425 + /** 1.426 + * Removes all items from the DB. 1.427 + */ 1.428 + private void ensureEmptyDatabase() { 1.429 + Uri uri = appendUriParam(ReadingListItems.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"); 1.430 + getWritableDatabase(uri).delete(ReadingListItems.TABLE_NAME, null, null); 1.431 + } 1.432 + 1.433 + 1.434 + private SQLiteDatabase getWritableDatabase(Uri uri) { 1.435 + Uri testUri = appendUriParam(uri, BrowserContract.PARAM_IS_TEST, "1"); 1.436 + DelegatingTestContentProvider delegateProvider = (DelegatingTestContentProvider) mProvider; 1.437 + ReadingListProvider readingListProvider = (ReadingListProvider) delegateProvider.getTargetProvider(); 1.438 + return readingListProvider.getWritableDatabaseForTesting(testUri); 1.439 + } 1.440 + 1.441 + /** 1.442 + * Checks that the values in the cursor's current row match those 1.443 + * in the ContentValues object. 1.444 + * 1.445 + * @param cursor over the row to be checked 1.446 + * @param values to be checked 1.447 + */ 1.448 + private void assertRowEqualsContentValues(Cursor cursorWithActual, ContentValues expectedValues, boolean compareDateModified) { 1.449 + for (String column: TEST_COLUMNS) { 1.450 + String expected = expectedValues.getAsString(column); 1.451 + String actual = cursorWithActual.getString(cursorWithActual.getColumnIndex(column)); 1.452 + mAsserter.is(actual, expected, "Item has correct " + column); 1.453 + } 1.454 + 1.455 + if (compareDateModified) { 1.456 + String expected = expectedValues.getAsString(ReadingListItems.DATE_MODIFIED); 1.457 + String actual = cursorWithActual.getString(cursorWithActual.getColumnIndex(ReadingListItems.DATE_MODIFIED)); 1.458 + mAsserter.is(actual, expected, "Item has correct " + ReadingListItems.DATE_MODIFIED); 1.459 + } 1.460 + } 1.461 + 1.462 + private void assertRowEqualsContentValues(Cursor cursorWithActual, ContentValues expectedValues) { 1.463 + assertRowEqualsContentValues(cursorWithActual, expectedValues, true); 1.464 + } 1.465 + 1.466 + private ContentValues fillContentValues(String title, String url, String excerpt) { 1.467 + ContentValues values = new ContentValues(); 1.468 + 1.469 + values.put(ReadingListItems.TITLE, title); 1.470 + values.put(ReadingListItems.URL, url); 1.471 + values.put(ReadingListItems.EXCERPT, excerpt); 1.472 + values.put(ReadingListItems.LENGTH, excerpt.length()); 1.473 + 1.474 + return values; 1.475 + } 1.476 + 1.477 + private ContentValues createFillerReadingListItem() { 1.478 + Random rand = new Random(); 1.479 + return fillContentValues("Example", "http://example.com/?num=" + rand.nextInt(), "foo bar"); 1.480 + } 1.481 + 1.482 + private Cursor getItemById(Uri uri, long id, String[] projection) { 1.483 + return mProvider.query(uri, projection, 1.484 + ReadingListItems._ID + " = ?", 1.485 + new String[] { String.valueOf(id) }, 1.486 + null); 1.487 + } 1.488 + 1.489 + private Cursor getItemById(long id) { 1.490 + return getItemById(ReadingListItems.CONTENT_URI, id, null); 1.491 + } 1.492 + 1.493 + private Cursor getItemById(Uri uri, long id) { 1.494 + return getItemById(uri, id, null); 1.495 + } 1.496 + 1.497 + /** 1.498 + * Verifies that ContentProvider insertions have been tested. 1.499 + */ 1.500 + private void ensureCanInsert() { 1.501 + if (!mContentProviderInsertTested) { 1.502 + mAsserter.ok(false, "ContentProvider insertions have not been tested yet.", ""); 1.503 + } 1.504 + } 1.505 + 1.506 + /** 1.507 + * Verifies that ContentProvider updates have been tested. 1.508 + */ 1.509 + private void ensureCanUpdate() { 1.510 + if (!mContentProviderUpdateTested) { 1.511 + mAsserter.ok(false, "ContentProvider updates have not been tested yet.", ""); 1.512 + } 1.513 + } 1.514 + 1.515 + private long insertAnItemWithAssertion() { 1.516 + // We should be able to insert into the DB. 1.517 + ensureCanInsert(); 1.518 + 1.519 + ContentValues v = createFillerReadingListItem(); 1.520 + long id = ContentUris.parseId(mProvider.insert(ReadingListItems.CONTENT_URI, v)); 1.521 + 1.522 + assertItemExistsByID(id, "Inserted item found"); 1.523 + return id; 1.524 + } 1.525 + 1.526 + private void assertItemExistsByID(Uri uri, long id, String msg) { 1.527 + Cursor c = getItemById(uri, id); 1.528 + try { 1.529 + mAsserter.ok(c.moveToFirst(), msg, ""); 1.530 + } finally { 1.531 + c.close(); 1.532 + } 1.533 + } 1.534 + 1.535 + private void assertItemExistsByID(long id, String msg) { 1.536 + Cursor c = getItemById(id); 1.537 + try { 1.538 + mAsserter.ok(c.moveToFirst(), msg, ""); 1.539 + } finally { 1.540 + c.close(); 1.541 + } 1.542 + } 1.543 + 1.544 + private void assertItemDoesNotExistByID(long id, String msg) { 1.545 + Cursor c = getItemById(id); 1.546 + try { 1.547 + mAsserter.ok(!c.moveToFirst(), msg, ""); 1.548 + } finally { 1.549 + c.close(); 1.550 + } 1.551 + } 1.552 + 1.553 + private void assertItemDoesNotExistByID(Uri uri, long id, String msg) { 1.554 + Cursor c = getItemById(uri, id); 1.555 + try { 1.556 + mAsserter.ok(!c.moveToFirst(), msg, ""); 1.557 + } finally { 1.558 + c.close(); 1.559 + } 1.560 + } 1.561 +}