|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 package org.mozilla.gecko.tests; |
|
6 |
|
7 import java.util.HashSet; |
|
8 import java.util.Random; |
|
9 import java.util.concurrent.Callable; |
|
10 |
|
11 import org.mozilla.gecko.db.BrowserContract; |
|
12 import org.mozilla.gecko.db.BrowserContract.ReadingListItems; |
|
13 import org.mozilla.gecko.db.ReadingListProvider; |
|
14 |
|
15 import android.content.ContentProvider; |
|
16 import android.content.ContentUris; |
|
17 import android.content.ContentValues; |
|
18 import android.database.Cursor; |
|
19 import android.database.sqlite.SQLiteDatabase; |
|
20 import android.net.Uri; |
|
21 |
|
22 public class testReadingListProvider extends ContentProviderTest { |
|
23 |
|
24 private static final String DB_NAME = "browser.db"; |
|
25 |
|
26 // List of tests to be run sorted by dependency. |
|
27 private final TestCase[] TESTS_TO_RUN = { new TestInsertItems(), |
|
28 new TestDeleteItems(), |
|
29 new TestUpdateItems(), |
|
30 new TestBatchOperations(), |
|
31 new TestBrowserProviderNotifications() }; |
|
32 |
|
33 // Columns used to test for item equivalence. |
|
34 final String[] TEST_COLUMNS = { ReadingListItems.TITLE, |
|
35 ReadingListItems.URL, |
|
36 ReadingListItems.EXCERPT, |
|
37 ReadingListItems.LENGTH, |
|
38 ReadingListItems.DATE_CREATED }; |
|
39 |
|
40 // Indicates that insertions have been tested. ContentProvider.insert |
|
41 // has been proven to work. |
|
42 private boolean mContentProviderInsertTested = false; |
|
43 |
|
44 // Indicates that updates have been tested. ContentProvider.update |
|
45 // has been proven to work. |
|
46 private boolean mContentProviderUpdateTested = false; |
|
47 |
|
48 /** |
|
49 * Factory function that makes new ReadingListProvider instances. |
|
50 * <p> |
|
51 * We want a fresh provider each test, so this should be invoked in |
|
52 * <code>setUp</code> before each individual test. |
|
53 */ |
|
54 private static Callable<ContentProvider> sProviderFactory = new Callable<ContentProvider>() { |
|
55 @Override |
|
56 public ContentProvider call() { |
|
57 return new ReadingListProvider(); |
|
58 } |
|
59 }; |
|
60 |
|
61 @Override |
|
62 public void setUp() throws Exception { |
|
63 super.setUp(sProviderFactory, BrowserContract.READING_LIST_AUTHORITY, DB_NAME); |
|
64 for (TestCase test: TESTS_TO_RUN) { |
|
65 mTests.add(test); |
|
66 } |
|
67 } |
|
68 |
|
69 public void testReadingListProviderTests() throws Exception { |
|
70 for (Runnable test : mTests) { |
|
71 setTestName(test.getClass().getSimpleName()); |
|
72 ensureEmptyDatabase(); |
|
73 test.run(); |
|
74 } |
|
75 |
|
76 // Ensure browser initialization is complete before completing test, |
|
77 // so that the minidumps directory is consistently created. |
|
78 blockForGeckoReady(); |
|
79 } |
|
80 |
|
81 /** |
|
82 * Verify that we can insert a reading list item into the DB. |
|
83 */ |
|
84 private class TestInsertItems extends TestCase { |
|
85 @Override |
|
86 public void test() throws Exception { |
|
87 ContentValues b = createFillerReadingListItem(); |
|
88 long id = ContentUris.parseId(mProvider.insert(ReadingListItems.CONTENT_URI, b)); |
|
89 Cursor c = getItemById(id); |
|
90 |
|
91 try { |
|
92 mAsserter.ok(c.moveToFirst(), "Inserted item found", ""); |
|
93 assertRowEqualsContentValues(c, b); |
|
94 } finally { |
|
95 c.close(); |
|
96 } |
|
97 |
|
98 testInsertWithNullCol(ReadingListItems.GUID); |
|
99 mContentProviderInsertTested = true; |
|
100 } |
|
101 |
|
102 /** |
|
103 * Test that insertion fails when a required column |
|
104 * is null. |
|
105 */ |
|
106 private void testInsertWithNullCol(String colName) { |
|
107 ContentValues b = createFillerReadingListItem(); |
|
108 b.putNull(colName); |
|
109 |
|
110 try { |
|
111 ContentUris.parseId(mProvider.insert(ReadingListItems.CONTENT_URI, b)); |
|
112 // If we get to here, the flawed insertion succeeded. Fail the test. |
|
113 mAsserter.ok(false, "Insertion did not succeed with " + colName + " == null", ""); |
|
114 } catch (NullPointerException e) { |
|
115 // Indicates test was successful. |
|
116 } |
|
117 } |
|
118 } |
|
119 |
|
120 /** |
|
121 * Verify that we can remove a reading list item from the DB. |
|
122 */ |
|
123 private class TestDeleteItems extends TestCase { |
|
124 |
|
125 @Override |
|
126 public void test() throws Exception { |
|
127 long id = insertAnItemWithAssertion(); |
|
128 // Test that the item is only marked as deleted and |
|
129 // not removed from the database. |
|
130 testNonFirefoxSyncDelete(id); |
|
131 |
|
132 // Test that the item is removed from the database. |
|
133 testFirefoxSyncDelete(id); |
|
134 |
|
135 id = insertAnItemWithAssertion(); |
|
136 // Test that deleting works with only a URI. |
|
137 testDeleteWithItemURI(id); |
|
138 } |
|
139 |
|
140 /** |
|
141 * Delete an item with PARAM_IS_SYNC unset and verify that item was only marked |
|
142 * as deleted and not actually removed from the database. Also verify that the item |
|
143 * marked as deleted doesn't show up in a query. |
|
144 * |
|
145 * @param id of the item to be deleted |
|
146 */ |
|
147 private void testNonFirefoxSyncDelete(long id) { |
|
148 final int deleted = mProvider.delete(ReadingListItems.CONTENT_URI, |
|
149 ReadingListItems._ID + " = ?", |
|
150 new String[] { String.valueOf(id) }); |
|
151 |
|
152 mAsserter.is(deleted, 1, "Inserted item was deleted"); |
|
153 |
|
154 // PARAM_SHOW_DELETED in the URI allows items marked as deleted to be |
|
155 // included in the query. |
|
156 Uri uri = appendUriParam(ReadingListItems.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"); |
|
157 assertItemExistsByID(uri, id, "Deleted item was only marked as deleted"); |
|
158 |
|
159 // Test that the 'deleted' item does not show up in a query when PARAM_SHOW_DELETED |
|
160 // is not specified in the URI. |
|
161 assertItemDoesNotExistByID(id, "Inserted item can't be found after deletion"); |
|
162 } |
|
163 |
|
164 /** |
|
165 * Delete an item with PARAM_IS_SYNC=1 and verify that item |
|
166 * was actually removed from the database. |
|
167 * |
|
168 * @param id of the item to be deleted |
|
169 */ |
|
170 private void testFirefoxSyncDelete(long id) { |
|
171 final int deleted = mProvider.delete(appendUriParam(ReadingListItems.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"), |
|
172 ReadingListItems._ID + " = ?", |
|
173 new String[] { String.valueOf(id) }); |
|
174 |
|
175 mAsserter.is(deleted, 1, "Inserted item was deleted"); |
|
176 |
|
177 Uri uri = appendUriParam(ReadingListItems.CONTENT_URI, BrowserContract.PARAM_SHOW_DELETED, "1"); |
|
178 assertItemDoesNotExistByID(uri, id, "Inserted item is now actually deleted"); |
|
179 } |
|
180 |
|
181 /** |
|
182 * Delete an item with its URI and verify that the item |
|
183 * was actually removed from the database. |
|
184 * |
|
185 * @param id of the item to be deleted |
|
186 */ |
|
187 private void testDeleteWithItemURI(long id) { |
|
188 final int deleted = mProvider.delete(ContentUris.withAppendedId(ReadingListItems.CONTENT_URI, id), null, null); |
|
189 mAsserter.is(deleted, 1, "Inserted item was deleted using URI with id"); |
|
190 } |
|
191 } |
|
192 |
|
193 /** |
|
194 * Verify that we can update reading list items. |
|
195 */ |
|
196 private class TestUpdateItems extends TestCase { |
|
197 |
|
198 @Override |
|
199 public void test() throws Exception { |
|
200 // We should be able to insert into the DB. |
|
201 ensureCanInsert(); |
|
202 |
|
203 ContentValues original = createFillerReadingListItem(); |
|
204 long id = ContentUris.parseId(mProvider.insert(ReadingListItems.CONTENT_URI, original)); |
|
205 int updated = 0; |
|
206 Long originalDateCreated = null; |
|
207 Long originalDateModified = null; |
|
208 ContentValues updates = new ContentValues(); |
|
209 Cursor c = getItemById(id); |
|
210 try { |
|
211 mAsserter.ok(c.moveToFirst(), "Inserted item found", ""); |
|
212 |
|
213 originalDateCreated = c.getLong(c.getColumnIndex(ReadingListItems.DATE_CREATED)); |
|
214 originalDateModified = c.getLong(c.getColumnIndex(ReadingListItems.DATE_MODIFIED)); |
|
215 |
|
216 updates.put(ReadingListItems.TITLE, original.getAsString(ReadingListItems.TITLE) + "CHANGED"); |
|
217 updates.put(ReadingListItems.URL, original.getAsString(ReadingListItems.URL) + "/more/stuff"); |
|
218 updates.put(ReadingListItems.EXCERPT, original.getAsString(ReadingListItems.EXCERPT) + "CHANGED"); |
|
219 |
|
220 updated = mProvider.update(ReadingListItems.CONTENT_URI, updates, |
|
221 ReadingListItems._ID + " = ?", |
|
222 new String[] { String.valueOf(id) }); |
|
223 |
|
224 mAsserter.is(updated, 1, "Inserted item was updated"); |
|
225 } finally { |
|
226 c.close(); |
|
227 } |
|
228 |
|
229 // Name change for clarity. These values will be compared with the |
|
230 // current cursor row. |
|
231 ContentValues expectedValues = updates; |
|
232 c = getItemById(id); |
|
233 try { |
|
234 mAsserter.ok(c.moveToFirst(), "Updated item found", ""); |
|
235 mAsserter.isnot(c.getLong(c.getColumnIndex(ReadingListItems.DATE_MODIFIED)), |
|
236 originalDateModified, |
|
237 "Date modified should have changed"); |
|
238 |
|
239 // DATE_CREATED and LENGTH should equal old values since they weren't updated. |
|
240 expectedValues.put(ReadingListItems.DATE_CREATED, originalDateCreated); |
|
241 expectedValues.put(ReadingListItems.LENGTH, original.getAsString(ReadingListItems.LENGTH)); |
|
242 assertRowEqualsContentValues(c, expectedValues, /* compareDateModified */ false); |
|
243 } finally { |
|
244 c.close(); |
|
245 } |
|
246 |
|
247 // Test that updates on an item that doesn't exist does not modify any rows. |
|
248 testUpdateWithInvalidID(); |
|
249 |
|
250 // Test that update fails when a GUID is null. |
|
251 testUpdateWithNullCol(id, ReadingListItems.GUID); |
|
252 |
|
253 mContentProviderUpdateTested = true; |
|
254 } |
|
255 |
|
256 /** |
|
257 * Test that updates on an item that doesn't exist does |
|
258 * not modify any rows. |
|
259 * |
|
260 * @param id of the item to be deleted |
|
261 */ |
|
262 private void testUpdateWithInvalidID() { |
|
263 ensureEmptyDatabase(); |
|
264 final ContentValues b = createFillerReadingListItem(); |
|
265 final long id = ContentUris.parseId(mProvider.insert(ReadingListItems.CONTENT_URI, b)); |
|
266 final long INVALID_ID = id + 1; |
|
267 final ContentValues updates = new ContentValues(); |
|
268 updates.put(ReadingListItems.TITLE, b.getAsString(ReadingListItems.TITLE) + "CHANGED"); |
|
269 final int updated = mProvider.update(ReadingListItems.CONTENT_URI, updates, |
|
270 ReadingListItems._ID + " = ?", |
|
271 new String[] { String.valueOf(INVALID_ID) }); |
|
272 mAsserter.is(updated, 0, "Should not be able to update item with an invalid GUID"); |
|
273 } |
|
274 |
|
275 /** |
|
276 * Test that update fails when a required column is null. |
|
277 */ |
|
278 private int testUpdateWithNullCol(long id, String colName) { |
|
279 ContentValues updates = new ContentValues(); |
|
280 updates.putNull(colName); |
|
281 |
|
282 int updated = mProvider.update(ReadingListItems.CONTENT_URI, updates, |
|
283 ReadingListItems._ID + " = ?", |
|
284 new String[] { String.valueOf(id) }); |
|
285 |
|
286 mAsserter.is(updated, 0, "Should not be able to update item with " + colName + " == null "); |
|
287 return updated; |
|
288 } |
|
289 } |
|
290 |
|
291 private class TestBatchOperations extends TestCase { |
|
292 private static final int ITEM_COUNT = 10; |
|
293 |
|
294 /** |
|
295 * Insert a bunch of items into the DB with the bulkInsert |
|
296 * method and verify that they are there. |
|
297 */ |
|
298 private void testBulkInsert() { |
|
299 ensureEmptyDatabase(); |
|
300 final ContentValues allVals[] = new ContentValues[ITEM_COUNT]; |
|
301 final HashSet<String> urls = new HashSet<String>(); |
|
302 for (int i = 0; i < ITEM_COUNT; i++) { |
|
303 final String url = "http://www.test.org/" + i; |
|
304 allVals[i] = new ContentValues(); |
|
305 allVals[i].put(ReadingListItems.TITLE, "Test" + i); |
|
306 allVals[i].put(ReadingListItems.URL, url); |
|
307 allVals[i].put(ReadingListItems.EXCERPT, "EXCERPT" + i); |
|
308 allVals[i].put(ReadingListItems.LENGTH, i); |
|
309 urls.add(url); |
|
310 } |
|
311 |
|
312 int inserts = mProvider.bulkInsert(ReadingListItems.CONTENT_URI, allVals); |
|
313 mAsserter.is(inserts, ITEM_COUNT, "Excepted number of inserts matches"); |
|
314 |
|
315 Cursor c = mProvider.query(ReadingListItems.CONTENT_URI, null, |
|
316 null, |
|
317 null, |
|
318 null); |
|
319 try { |
|
320 while (c.moveToNext()) { |
|
321 final String url = c.getString(c.getColumnIndex(ReadingListItems.URL)); |
|
322 mAsserter.ok(urls.contains(url), "Bulk inserted item with url == " + url + " was found in the DB", ""); |
|
323 // We should only be seeing each item once. Remove from set to prevent dups. |
|
324 urls.remove(url); |
|
325 } |
|
326 } finally { |
|
327 c.close(); |
|
328 } |
|
329 } |
|
330 |
|
331 @Override |
|
332 public void test() { |
|
333 testBulkInsert(); |
|
334 } |
|
335 } |
|
336 |
|
337 /* |
|
338 * Verify that insert, update, delete, and bulkInsert operations |
|
339 * notify the ambient content resolver. Each operation calls the |
|
340 * content resolver notifyChange method synchronously, so it is |
|
341 * okay to test sequentially. |
|
342 */ |
|
343 private class TestBrowserProviderNotifications extends TestCase { |
|
344 |
|
345 @Override |
|
346 public void test() { |
|
347 // We should be able to insert into the DB. |
|
348 ensureCanInsert(); |
|
349 // We should be able to update the DB. |
|
350 ensureCanUpdate(); |
|
351 |
|
352 final String CONTENT_URI = ReadingListItems.CONTENT_URI.toString(); |
|
353 |
|
354 mResolver.notifyChangeList.clear(); |
|
355 |
|
356 // Insert |
|
357 final ContentValues h = createFillerReadingListItem(); |
|
358 long id = ContentUris.parseId(mProvider.insert(ReadingListItems.CONTENT_URI, h)); |
|
359 |
|
360 mAsserter.isnot(id, |
|
361 -1L, |
|
362 "Inserted item has valid id"); |
|
363 |
|
364 ensureOnlyChangeNotifiedStartsWith(CONTENT_URI, "insert"); |
|
365 |
|
366 // Update |
|
367 mResolver.notifyChangeList.clear(); |
|
368 h.put(ReadingListItems.TITLE, "http://newexample.com"); |
|
369 |
|
370 long numUpdated = mProvider.update(ReadingListItems.CONTENT_URI, h, |
|
371 ReadingListItems._ID + " = ?", |
|
372 new String[] { String.valueOf(id) }); |
|
373 |
|
374 mAsserter.is(numUpdated, |
|
375 1L, |
|
376 "Correct number of items are updated"); |
|
377 |
|
378 ensureOnlyChangeNotifiedStartsWith(CONTENT_URI, "update"); |
|
379 |
|
380 // Delete |
|
381 mResolver.notifyChangeList.clear(); |
|
382 long numDeleted = mProvider.delete(ReadingListItems.CONTENT_URI, null, null); |
|
383 |
|
384 mAsserter.is(numDeleted, |
|
385 1L, |
|
386 "Correct number of items are deleted"); |
|
387 |
|
388 ensureOnlyChangeNotifiedStartsWith(CONTENT_URI, "delete"); |
|
389 |
|
390 // Bulk insert |
|
391 mResolver.notifyChangeList.clear(); |
|
392 final ContentValues[] hs = { createFillerReadingListItem(), |
|
393 createFillerReadingListItem(), |
|
394 createFillerReadingListItem() }; |
|
395 |
|
396 long numBulkInserted = mProvider.bulkInsert(ReadingListItems.CONTENT_URI, hs); |
|
397 |
|
398 mAsserter.is(numBulkInserted, |
|
399 3L, |
|
400 "Correct number of items are bulkInserted"); |
|
401 |
|
402 ensureOnlyChangeNotifiedStartsWith(CONTENT_URI, "bulkInsert"); |
|
403 } |
|
404 |
|
405 protected void ensureOnlyChangeNotifiedStartsWith(String expectedUri, String operation) { |
|
406 mAsserter.is(Long.valueOf(mResolver.notifyChangeList.size()), |
|
407 1L, |
|
408 "Content observer was notified exactly once by " + operation); |
|
409 |
|
410 final Uri uri = mResolver.notifyChangeList.poll(); |
|
411 |
|
412 mAsserter.isnot(uri, |
|
413 null, |
|
414 "Notification from " + operation + " was valid"); |
|
415 |
|
416 mAsserter.ok(uri.toString().startsWith(expectedUri), |
|
417 "Content observer was notified exactly once by " + operation, |
|
418 ""); |
|
419 } |
|
420 } |
|
421 |
|
422 /** |
|
423 * Removes all items from the DB. |
|
424 */ |
|
425 private void ensureEmptyDatabase() { |
|
426 Uri uri = appendUriParam(ReadingListItems.CONTENT_URI, BrowserContract.PARAM_IS_SYNC, "1"); |
|
427 getWritableDatabase(uri).delete(ReadingListItems.TABLE_NAME, null, null); |
|
428 } |
|
429 |
|
430 |
|
431 private SQLiteDatabase getWritableDatabase(Uri uri) { |
|
432 Uri testUri = appendUriParam(uri, BrowserContract.PARAM_IS_TEST, "1"); |
|
433 DelegatingTestContentProvider delegateProvider = (DelegatingTestContentProvider) mProvider; |
|
434 ReadingListProvider readingListProvider = (ReadingListProvider) delegateProvider.getTargetProvider(); |
|
435 return readingListProvider.getWritableDatabaseForTesting(testUri); |
|
436 } |
|
437 |
|
438 /** |
|
439 * Checks that the values in the cursor's current row match those |
|
440 * in the ContentValues object. |
|
441 * |
|
442 * @param cursor over the row to be checked |
|
443 * @param values to be checked |
|
444 */ |
|
445 private void assertRowEqualsContentValues(Cursor cursorWithActual, ContentValues expectedValues, boolean compareDateModified) { |
|
446 for (String column: TEST_COLUMNS) { |
|
447 String expected = expectedValues.getAsString(column); |
|
448 String actual = cursorWithActual.getString(cursorWithActual.getColumnIndex(column)); |
|
449 mAsserter.is(actual, expected, "Item has correct " + column); |
|
450 } |
|
451 |
|
452 if (compareDateModified) { |
|
453 String expected = expectedValues.getAsString(ReadingListItems.DATE_MODIFIED); |
|
454 String actual = cursorWithActual.getString(cursorWithActual.getColumnIndex(ReadingListItems.DATE_MODIFIED)); |
|
455 mAsserter.is(actual, expected, "Item has correct " + ReadingListItems.DATE_MODIFIED); |
|
456 } |
|
457 } |
|
458 |
|
459 private void assertRowEqualsContentValues(Cursor cursorWithActual, ContentValues expectedValues) { |
|
460 assertRowEqualsContentValues(cursorWithActual, expectedValues, true); |
|
461 } |
|
462 |
|
463 private ContentValues fillContentValues(String title, String url, String excerpt) { |
|
464 ContentValues values = new ContentValues(); |
|
465 |
|
466 values.put(ReadingListItems.TITLE, title); |
|
467 values.put(ReadingListItems.URL, url); |
|
468 values.put(ReadingListItems.EXCERPT, excerpt); |
|
469 values.put(ReadingListItems.LENGTH, excerpt.length()); |
|
470 |
|
471 return values; |
|
472 } |
|
473 |
|
474 private ContentValues createFillerReadingListItem() { |
|
475 Random rand = new Random(); |
|
476 return fillContentValues("Example", "http://example.com/?num=" + rand.nextInt(), "foo bar"); |
|
477 } |
|
478 |
|
479 private Cursor getItemById(Uri uri, long id, String[] projection) { |
|
480 return mProvider.query(uri, projection, |
|
481 ReadingListItems._ID + " = ?", |
|
482 new String[] { String.valueOf(id) }, |
|
483 null); |
|
484 } |
|
485 |
|
486 private Cursor getItemById(long id) { |
|
487 return getItemById(ReadingListItems.CONTENT_URI, id, null); |
|
488 } |
|
489 |
|
490 private Cursor getItemById(Uri uri, long id) { |
|
491 return getItemById(uri, id, null); |
|
492 } |
|
493 |
|
494 /** |
|
495 * Verifies that ContentProvider insertions have been tested. |
|
496 */ |
|
497 private void ensureCanInsert() { |
|
498 if (!mContentProviderInsertTested) { |
|
499 mAsserter.ok(false, "ContentProvider insertions have not been tested yet.", ""); |
|
500 } |
|
501 } |
|
502 |
|
503 /** |
|
504 * Verifies that ContentProvider updates have been tested. |
|
505 */ |
|
506 private void ensureCanUpdate() { |
|
507 if (!mContentProviderUpdateTested) { |
|
508 mAsserter.ok(false, "ContentProvider updates have not been tested yet.", ""); |
|
509 } |
|
510 } |
|
511 |
|
512 private long insertAnItemWithAssertion() { |
|
513 // We should be able to insert into the DB. |
|
514 ensureCanInsert(); |
|
515 |
|
516 ContentValues v = createFillerReadingListItem(); |
|
517 long id = ContentUris.parseId(mProvider.insert(ReadingListItems.CONTENT_URI, v)); |
|
518 |
|
519 assertItemExistsByID(id, "Inserted item found"); |
|
520 return id; |
|
521 } |
|
522 |
|
523 private void assertItemExistsByID(Uri uri, long id, String msg) { |
|
524 Cursor c = getItemById(uri, id); |
|
525 try { |
|
526 mAsserter.ok(c.moveToFirst(), msg, ""); |
|
527 } finally { |
|
528 c.close(); |
|
529 } |
|
530 } |
|
531 |
|
532 private void assertItemExistsByID(long id, String msg) { |
|
533 Cursor c = getItemById(id); |
|
534 try { |
|
535 mAsserter.ok(c.moveToFirst(), msg, ""); |
|
536 } finally { |
|
537 c.close(); |
|
538 } |
|
539 } |
|
540 |
|
541 private void assertItemDoesNotExistByID(long id, String msg) { |
|
542 Cursor c = getItemById(id); |
|
543 try { |
|
544 mAsserter.ok(!c.moveToFirst(), msg, ""); |
|
545 } finally { |
|
546 c.close(); |
|
547 } |
|
548 } |
|
549 |
|
550 private void assertItemDoesNotExistByID(Uri uri, long id, String msg) { |
|
551 Cursor c = getItemById(uri, id); |
|
552 try { |
|
553 mAsserter.ok(!c.moveToFirst(), msg, ""); |
|
554 } finally { |
|
555 c.close(); |
|
556 } |
|
557 } |
|
558 } |