mobile/android/tests/background/junit3/src/db/TestBookmarks.java

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /* Any copyright is dedicated to the Public Domain.
michael@0 2 http://creativecommons.org/publicdomain/zero/1.0/ */
michael@0 3
michael@0 4 package org.mozilla.gecko.background.db;
michael@0 5
michael@0 6 import java.util.ArrayList;
michael@0 7 import java.util.Collection;
michael@0 8 import java.util.Collections;
michael@0 9 import java.util.HashSet;
michael@0 10 import java.util.Iterator;
michael@0 11
michael@0 12 import org.json.simple.JSONArray;
michael@0 13 import org.mozilla.gecko.R;
michael@0 14 import org.mozilla.gecko.background.common.log.Logger;
michael@0 15 import org.mozilla.gecko.background.helpers.AndroidSyncTestCase;
michael@0 16 import org.mozilla.gecko.background.sync.helpers.BookmarkHelpers;
michael@0 17 import org.mozilla.gecko.background.sync.helpers.SimpleSuccessBeginDelegate;
michael@0 18 import org.mozilla.gecko.background.sync.helpers.SimpleSuccessCreationDelegate;
michael@0 19 import org.mozilla.gecko.background.sync.helpers.SimpleSuccessFetchDelegate;
michael@0 20 import org.mozilla.gecko.background.sync.helpers.SimpleSuccessFinishDelegate;
michael@0 21 import org.mozilla.gecko.background.sync.helpers.SimpleSuccessStoreDelegate;
michael@0 22 import org.mozilla.gecko.db.BrowserContract;
michael@0 23 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
michael@0 24 import org.mozilla.gecko.sync.Utils;
michael@0 25 import org.mozilla.gecko.sync.repositories.InactiveSessionException;
michael@0 26 import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
michael@0 27 import org.mozilla.gecko.sync.repositories.NoStoreDelegateException;
michael@0 28 import org.mozilla.gecko.sync.repositories.NullCursorException;
michael@0 29 import org.mozilla.gecko.sync.repositories.RepositorySession;
michael@0 30 import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
michael@0 31 import org.mozilla.gecko.sync.repositories.android.AndroidBrowserBookmarksDataAccessor;
michael@0 32 import org.mozilla.gecko.sync.repositories.android.AndroidBrowserBookmarksRepository;
michael@0 33 import org.mozilla.gecko.sync.repositories.android.AndroidBrowserBookmarksRepositorySession;
michael@0 34 import org.mozilla.gecko.sync.repositories.android.BrowserContractHelpers;
michael@0 35 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionBeginDelegate;
michael@0 36 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionCreationDelegate;
michael@0 37 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFetchRecordsDelegate;
michael@0 38 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionStoreDelegate;
michael@0 39 import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
michael@0 40 import org.mozilla.gecko.sync.repositories.domain.Record;
michael@0 41
michael@0 42 import android.content.ContentResolver;
michael@0 43 import android.content.ContentUris;
michael@0 44 import android.content.ContentValues;
michael@0 45 import android.database.Cursor;
michael@0 46 import android.net.Uri;
michael@0 47
michael@0 48 public class TestBookmarks extends AndroidSyncTestCase {
michael@0 49
michael@0 50 protected static final String LOG_TAG = "BookmarksTest";
michael@0 51
michael@0 52 /**
michael@0 53 * Trivial test that forbidden records (reading list prior to Bug 762109, pinned items…)
michael@0 54 * will be ignored if processed.
michael@0 55 */
michael@0 56 public void testForbiddenItemsAreIgnored() {
michael@0 57 final AndroidBrowserBookmarksRepository repo = new AndroidBrowserBookmarksRepository();
michael@0 58 final long now = System.currentTimeMillis();
michael@0 59 final String bookmarksCollection = "bookmarks";
michael@0 60
michael@0 61 final BookmarkRecord toRead = new BookmarkRecord("daaaaaaaaaaa", "bookmarks", now - 1, false);
michael@0 62 final BookmarkRecord pinned = new BookmarkRecord("pinpinpinpin", "bookmarks", now - 1, false);
michael@0 63 final BookmarkRecord normal = new BookmarkRecord("baaaaaaaaaaa", "bookmarks", now - 2, false);
michael@0 64
michael@0 65 final BookmarkRecord readingList = new BookmarkRecord(Bookmarks.READING_LIST_FOLDER_GUID,
michael@0 66 bookmarksCollection, now - 3, false);
michael@0 67 final BookmarkRecord pinnedItems = new BookmarkRecord(Bookmarks.PINNED_FOLDER_GUID,
michael@0 68 bookmarksCollection, now - 4, false);
michael@0 69
michael@0 70 toRead.type = normal.type = pinned.type = "bookmark";
michael@0 71 readingList.type = "folder";
michael@0 72 pinnedItems.type = "folder";
michael@0 73
michael@0 74 toRead.parentID = Bookmarks.READING_LIST_FOLDER_GUID;
michael@0 75 pinned.parentID = Bookmarks.PINNED_FOLDER_GUID;
michael@0 76 normal.parentID = Bookmarks.TOOLBAR_FOLDER_GUID;
michael@0 77
michael@0 78 readingList.parentID = Bookmarks.PLACES_FOLDER_GUID;
michael@0 79 pinnedItems.parentID = Bookmarks.PLACES_FOLDER_GUID;
michael@0 80
michael@0 81 inBegunSession(repo, new SimpleSuccessBeginDelegate() {
michael@0 82 @Override
michael@0 83 public void onBeginSucceeded(RepositorySession session) {
michael@0 84 assertTrue(((AndroidBrowserBookmarksRepositorySession) session).shouldIgnore(toRead));
michael@0 85 assertTrue(((AndroidBrowserBookmarksRepositorySession) session).shouldIgnore(pinned));
michael@0 86 assertTrue(((AndroidBrowserBookmarksRepositorySession) session).shouldIgnore(readingList));
michael@0 87 assertTrue(((AndroidBrowserBookmarksRepositorySession) session).shouldIgnore(pinnedItems));
michael@0 88 assertFalse(((AndroidBrowserBookmarksRepositorySession) session).shouldIgnore(normal));
michael@0 89 finishAndNotify(session);
michael@0 90 }
michael@0 91 });
michael@0 92 }
michael@0 93
michael@0 94 /**
michael@0 95 * Trivial test that pinned items will be skipped if present in the DB.
michael@0 96 */
michael@0 97 public void testPinnedItemsAreNotRetrieved() {
michael@0 98 final AndroidBrowserBookmarksRepository repo = new AndroidBrowserBookmarksRepository();
michael@0 99
michael@0 100 // Ensure that they exist.
michael@0 101 setUpFennecPinnedItemsRecord();
michael@0 102
michael@0 103 // They're there in the DB…
michael@0 104 final ArrayList<String> roots = fetchChildrenDirect(Bookmarks.FIXED_ROOT_ID);
michael@0 105 Logger.info(LOG_TAG, "Roots: " + roots);
michael@0 106 assertTrue(roots.contains(Bookmarks.PINNED_FOLDER_GUID));
michael@0 107
michael@0 108 final ArrayList<String> pinned = fetchChildrenDirect(Bookmarks.FIXED_PINNED_LIST_ID);
michael@0 109 Logger.info(LOG_TAG, "Pinned: " + pinned);
michael@0 110 assertTrue(pinned.contains("dapinneditem"));
michael@0 111
michael@0 112 // … but not when we fetch.
michael@0 113 final ArrayList<String> guids = fetchGUIDs(repo);
michael@0 114 assertFalse(guids.contains(Bookmarks.PINNED_FOLDER_GUID));
michael@0 115 assertFalse(guids.contains("dapinneditem"));
michael@0 116 }
michael@0 117
michael@0 118 /**
michael@0 119 * Trivial test that reading list records will be skipped if present in the DB.
michael@0 120 */
michael@0 121 public void testReadingListIsNotRetrieved() {
michael@0 122 final AndroidBrowserBookmarksRepository repo = new AndroidBrowserBookmarksRepository();
michael@0 123
michael@0 124 // Ensure that it exists.
michael@0 125 setUpFennecReadingListRecord();
michael@0 126
michael@0 127 // It's there in the DB…
michael@0 128 final ArrayList<String> roots = fetchChildrenDirect(BrowserContract.Bookmarks.FIXED_ROOT_ID);
michael@0 129 Logger.info(LOG_TAG, "Roots: " + roots);
michael@0 130 assertTrue(roots.contains(Bookmarks.READING_LIST_FOLDER_GUID));
michael@0 131
michael@0 132 // … but not when we fetch.
michael@0 133 assertFalse(fetchGUIDs(repo).contains(Bookmarks.READING_LIST_FOLDER_GUID));
michael@0 134 }
michael@0 135
michael@0 136 public void testRetrieveFolderHasAccurateChildren() {
michael@0 137 AndroidBrowserBookmarksRepository repo = new AndroidBrowserBookmarksRepository();
michael@0 138
michael@0 139 final long now = System.currentTimeMillis();
michael@0 140
michael@0 141 final String folderGUID = "eaaaaaaaafff";
michael@0 142 BookmarkRecord folder = new BookmarkRecord(folderGUID, "bookmarks", now - 5, false);
michael@0 143 BookmarkRecord bookmarkA = new BookmarkRecord("daaaaaaaaaaa", "bookmarks", now - 1, false);
michael@0 144 BookmarkRecord bookmarkB = new BookmarkRecord("baaaaaaaabbb", "bookmarks", now - 3, false);
michael@0 145 BookmarkRecord bookmarkC = new BookmarkRecord("aaaaaaaaaccc", "bookmarks", now - 2, false);
michael@0 146
michael@0 147 folder.children = childrenFromRecords(bookmarkA, bookmarkB, bookmarkC);
michael@0 148 folder.sortIndex = 150;
michael@0 149 folder.title = "Test items";
michael@0 150 folder.parentID = "toolbar";
michael@0 151 folder.parentName = "Bookmarks Toolbar";
michael@0 152 folder.type = "folder";
michael@0 153
michael@0 154 bookmarkA.parentID = folderGUID;
michael@0 155 bookmarkA.bookmarkURI = "http://example.com/A";
michael@0 156 bookmarkA.title = "Title A";
michael@0 157 bookmarkA.type = "bookmark";
michael@0 158
michael@0 159 bookmarkB.parentID = folderGUID;
michael@0 160 bookmarkB.bookmarkURI = "http://example.com/B";
michael@0 161 bookmarkB.title = "Title B";
michael@0 162 bookmarkB.type = "bookmark";
michael@0 163
michael@0 164 bookmarkC.parentID = folderGUID;
michael@0 165 bookmarkC.bookmarkURI = "http://example.com/C";
michael@0 166 bookmarkC.title = "Title C";
michael@0 167 bookmarkC.type = "bookmark";
michael@0 168
michael@0 169 BookmarkRecord[] folderOnly = new BookmarkRecord[1];
michael@0 170 BookmarkRecord[] children = new BookmarkRecord[3];
michael@0 171
michael@0 172 folderOnly[0] = folder;
michael@0 173
michael@0 174 children[0] = bookmarkA;
michael@0 175 children[1] = bookmarkB;
michael@0 176 children[2] = bookmarkC;
michael@0 177
michael@0 178 wipe();
michael@0 179 Logger.debug(getName(), "Storing just folder...");
michael@0 180 storeRecordsInSession(repo, folderOnly, null);
michael@0 181
michael@0 182 // We don't have any children, despite our insistence upon storing.
michael@0 183 assertChildrenAreOrdered(repo, folderGUID, new Record[] {});
michael@0 184
michael@0 185 // Now store the children.
michael@0 186 Logger.debug(getName(), "Storing children...");
michael@0 187 storeRecordsInSession(repo, children, null);
michael@0 188
michael@0 189 // Now we have children, but their order is not determined, because
michael@0 190 // they were stored out-of-session with the original folder.
michael@0 191 assertChildrenAreUnordered(repo, folderGUID, children);
michael@0 192
michael@0 193 // Now if we store the folder record again, they'll be put in the
michael@0 194 // right place.
michael@0 195 folder.lastModified++;
michael@0 196 Logger.debug(getName(), "Storing just folder again...");
michael@0 197 storeRecordsInSession(repo, folderOnly, null);
michael@0 198 Logger.debug(getName(), "Fetching children yet again...");
michael@0 199 assertChildrenAreOrdered(repo, folderGUID, children);
michael@0 200
michael@0 201 // Now let's see what happens when we see records in the same session.
michael@0 202 BookmarkRecord[] parentMixed = new BookmarkRecord[4];
michael@0 203 BookmarkRecord[] parentFirst = new BookmarkRecord[4];
michael@0 204 BookmarkRecord[] parentLast = new BookmarkRecord[4];
michael@0 205
michael@0 206 // None of our records have a position set.
michael@0 207 assertTrue(bookmarkA.androidPosition <= 0);
michael@0 208 assertTrue(bookmarkB.androidPosition <= 0);
michael@0 209 assertTrue(bookmarkC.androidPosition <= 0);
michael@0 210
michael@0 211 parentMixed[1] = folder;
michael@0 212 parentMixed[0] = bookmarkA;
michael@0 213 parentMixed[2] = bookmarkC;
michael@0 214 parentMixed[3] = bookmarkB;
michael@0 215
michael@0 216 parentFirst[0] = folder;
michael@0 217 parentFirst[1] = bookmarkC;
michael@0 218 parentFirst[2] = bookmarkA;
michael@0 219 parentFirst[3] = bookmarkB;
michael@0 220
michael@0 221 parentLast[3] = folder;
michael@0 222 parentLast[0] = bookmarkB;
michael@0 223 parentLast[1] = bookmarkA;
michael@0 224 parentLast[2] = bookmarkC;
michael@0 225
michael@0 226 wipe();
michael@0 227 storeRecordsInSession(repo, parentMixed, null);
michael@0 228 assertChildrenAreOrdered(repo, folderGUID, children);
michael@0 229
michael@0 230 wipe();
michael@0 231 storeRecordsInSession(repo, parentFirst, null);
michael@0 232 assertChildrenAreOrdered(repo, folderGUID, children);
michael@0 233
michael@0 234 wipe();
michael@0 235 storeRecordsInSession(repo, parentLast, null);
michael@0 236 assertChildrenAreOrdered(repo, folderGUID, children);
michael@0 237
michael@0 238 // Ensure that records are ordered even if we re-process the folder.
michael@0 239 wipe();
michael@0 240 storeRecordsInSession(repo, parentLast, null);
michael@0 241 folder.lastModified++;
michael@0 242 storeRecordsInSession(repo, folderOnly, null);
michael@0 243 assertChildrenAreOrdered(repo, folderGUID, children);
michael@0 244 }
michael@0 245
michael@0 246 public void testMergeFoldersPreservesSaneOrder() {
michael@0 247 AndroidBrowserBookmarksRepository repo = new AndroidBrowserBookmarksRepository();
michael@0 248
michael@0 249 final long now = System.currentTimeMillis();
michael@0 250 final String folderGUID = "mobile";
michael@0 251
michael@0 252 wipe();
michael@0 253 final long mobile = setUpFennecMobileRecord();
michael@0 254
michael@0 255 // No children.
michael@0 256 assertChildrenAreUnordered(repo, folderGUID, new Record[] {});
michael@0 257
michael@0 258 // Add some, as Fennec would.
michael@0 259 fennecAddBookmark("Bookmark One", "http://example.com/fennec/One");
michael@0 260 fennecAddBookmark("Bookmark Two", "http://example.com/fennec/Two");
michael@0 261
michael@0 262 Logger.debug(getName(), "Fetching children...");
michael@0 263 JSONArray folderChildren = fetchChildrenForGUID(repo, folderGUID);
michael@0 264
michael@0 265 assertTrue(folderChildren != null);
michael@0 266 Logger.debug(getName(), "Children are " + folderChildren.toJSONString());
michael@0 267 assertEquals(2, folderChildren.size());
michael@0 268 String guidOne = (String) folderChildren.get(0);
michael@0 269 String guidTwo = (String) folderChildren.get(1);
michael@0 270
michael@0 271 // Make sure positions were saved.
michael@0 272 assertChildrenAreDirect(mobile, new String[] {
michael@0 273 guidOne,
michael@0 274 guidTwo
michael@0 275 });
michael@0 276
michael@0 277 // Add some through Sync.
michael@0 278 BookmarkRecord folder = new BookmarkRecord(folderGUID, "bookmarks", now, false);
michael@0 279 BookmarkRecord bookmarkA = new BookmarkRecord("daaaaaaaaaaa", "bookmarks", now, false);
michael@0 280 BookmarkRecord bookmarkB = new BookmarkRecord("baaaaaaaabbb", "bookmarks", now, false);
michael@0 281
michael@0 282 folder.children = childrenFromRecords(bookmarkA, bookmarkB);
michael@0 283 folder.sortIndex = 150;
michael@0 284 folder.title = "Mobile Bookmarks";
michael@0 285 folder.parentID = "places";
michael@0 286 folder.parentName = "";
michael@0 287 folder.type = "folder";
michael@0 288
michael@0 289 bookmarkA.parentID = folderGUID;
michael@0 290 bookmarkA.parentName = "Mobile Bookmarks"; // Using this title exercises Bug 748898.
michael@0 291 bookmarkA.bookmarkURI = "http://example.com/A";
michael@0 292 bookmarkA.title = "Title A";
michael@0 293 bookmarkA.type = "bookmark";
michael@0 294
michael@0 295 bookmarkB.parentID = folderGUID;
michael@0 296 bookmarkB.parentName = "mobile";
michael@0 297 bookmarkB.bookmarkURI = "http://example.com/B";
michael@0 298 bookmarkB.title = "Title B";
michael@0 299 bookmarkB.type = "bookmark";
michael@0 300
michael@0 301 BookmarkRecord[] parentMixed = new BookmarkRecord[3];
michael@0 302 parentMixed[0] = bookmarkA;
michael@0 303 parentMixed[1] = folder;
michael@0 304 parentMixed[2] = bookmarkB;
michael@0 305
michael@0 306 storeRecordsInSession(repo, parentMixed, null);
michael@0 307
michael@0 308 BookmarkRecord expectedOne = new BookmarkRecord(guidOne, "bookmarks", now - 10, false);
michael@0 309 BookmarkRecord expectedTwo = new BookmarkRecord(guidTwo, "bookmarks", now - 10, false);
michael@0 310
michael@0 311 // We want the server to win in this case, and otherwise to preserve order.
michael@0 312 // TODO
michael@0 313 assertChildrenAreOrdered(repo, folderGUID, new Record[] {
michael@0 314 bookmarkA,
michael@0 315 bookmarkB,
michael@0 316 expectedOne,
michael@0 317 expectedTwo
michael@0 318 });
michael@0 319
michael@0 320 // Furthermore, the children of that folder should be correct in the DB.
michael@0 321 ContentResolver cr = getApplicationContext().getContentResolver();
michael@0 322 final long folderId = fennecGetFolderId(cr, folderGUID);
michael@0 323 Logger.debug(getName(), "Folder " + folderGUID + " => " + folderId);
michael@0 324
michael@0 325 assertChildrenAreDirect(folderId, new String[] {
michael@0 326 bookmarkA.guid,
michael@0 327 bookmarkB.guid,
michael@0 328 expectedOne.guid,
michael@0 329 expectedTwo.guid
michael@0 330 });
michael@0 331 }
michael@0 332
michael@0 333 /**
michael@0 334 * Apply a folder record whose children array is already accurately
michael@0 335 * stored in the database. Verify that the parent folder is not flagged
michael@0 336 * for reupload (i.e., that its modified time is *ahem* unmodified).
michael@0 337 */
michael@0 338 public void testNoReorderingMeansNoReupload() {
michael@0 339 AndroidBrowserBookmarksRepository repo = new AndroidBrowserBookmarksRepository();
michael@0 340
michael@0 341 final long now = System.currentTimeMillis();
michael@0 342
michael@0 343 final String folderGUID = "eaaaaaaaafff";
michael@0 344 BookmarkRecord folder = new BookmarkRecord(folderGUID, "bookmarks", now -5, false);
michael@0 345 BookmarkRecord bookmarkA = new BookmarkRecord("daaaaaaaaaaa", "bookmarks", now -1, false);
michael@0 346 BookmarkRecord bookmarkB = new BookmarkRecord("baaaaaaaabbb", "bookmarks", now -3, false);
michael@0 347
michael@0 348 folder.children = childrenFromRecords(bookmarkA, bookmarkB);
michael@0 349 folder.sortIndex = 150;
michael@0 350 folder.title = "Test items";
michael@0 351 folder.parentID = "toolbar";
michael@0 352 folder.parentName = "Bookmarks Toolbar";
michael@0 353 folder.type = "folder";
michael@0 354
michael@0 355 bookmarkA.parentID = folderGUID;
michael@0 356 bookmarkA.bookmarkURI = "http://example.com/A";
michael@0 357 bookmarkA.title = "Title A";
michael@0 358 bookmarkA.type = "bookmark";
michael@0 359
michael@0 360 bookmarkB.parentID = folderGUID;
michael@0 361 bookmarkB.bookmarkURI = "http://example.com/B";
michael@0 362 bookmarkB.title = "Title B";
michael@0 363 bookmarkB.type = "bookmark";
michael@0 364
michael@0 365 BookmarkRecord[] abf = new BookmarkRecord[3];
michael@0 366 BookmarkRecord[] justFolder = new BookmarkRecord[1];
michael@0 367
michael@0 368 abf[0] = bookmarkA;
michael@0 369 abf[1] = bookmarkB;
michael@0 370 abf[2] = folder;
michael@0 371
michael@0 372 justFolder[0] = folder;
michael@0 373
michael@0 374 final String[] abGUIDs = new String[] { bookmarkA.guid, bookmarkB.guid };
michael@0 375 final Record[] abRecords = new Record[] { bookmarkA, bookmarkB };
michael@0 376 final String[] baGUIDs = new String[] { bookmarkB.guid, bookmarkA.guid };
michael@0 377 final Record[] baRecords = new Record[] { bookmarkB, bookmarkA };
michael@0 378
michael@0 379 wipe();
michael@0 380 Logger.debug(getName(), "Storing A, B, folder...");
michael@0 381 storeRecordsInSession(repo, abf, null);
michael@0 382
michael@0 383 ContentResolver cr = getApplicationContext().getContentResolver();
michael@0 384 final long folderID = fennecGetFolderId(cr, folderGUID);
michael@0 385 assertChildrenAreOrdered(repo, folderGUID, abRecords);
michael@0 386 assertChildrenAreDirect(folderID, abGUIDs);
michael@0 387
michael@0 388 // To ensure an interval.
michael@0 389 try {
michael@0 390 Thread.sleep(100);
michael@0 391 } catch (InterruptedException e) {
michael@0 392 }
michael@0 393
michael@0 394 // Store the same folder record again, and check the tracking.
michael@0 395 // Because the folder array didn't change,
michael@0 396 // the item is still tracked to not be uploaded.
michael@0 397 folder.lastModified = System.currentTimeMillis() + 1;
michael@0 398 HashSet<String> tracked = new HashSet<String>();
michael@0 399 storeRecordsInSession(repo, justFolder, tracked);
michael@0 400 assertChildrenAreOrdered(repo, folderGUID, abRecords);
michael@0 401 assertChildrenAreDirect(folderID, abGUIDs);
michael@0 402
michael@0 403 assertTrue(tracked.contains(folderGUID));
michael@0 404
michael@0 405 // Store again, but with a different order.
michael@0 406 tracked = new HashSet<String>();
michael@0 407 folder.children = childrenFromRecords(bookmarkB, bookmarkA);
michael@0 408 folder.lastModified = System.currentTimeMillis() + 1;
michael@0 409 storeRecordsInSession(repo, justFolder, tracked);
michael@0 410 assertChildrenAreOrdered(repo, folderGUID, baRecords);
michael@0 411 assertChildrenAreDirect(folderID, baGUIDs);
michael@0 412
michael@0 413 // Now it's going to be reuploaded.
michael@0 414 assertFalse(tracked.contains(folderGUID));
michael@0 415 }
michael@0 416
michael@0 417 /**
michael@0 418 * Exercise the deletion of folders when their children have not been
michael@0 419 * marked as deleted. In a database with constraints, this would fail
michael@0 420 * if we simply deleted the records, so we move them first.
michael@0 421 */
michael@0 422 public void testFolderDeletionOrphansChildren() {
michael@0 423 AndroidBrowserBookmarksRepository repo = new AndroidBrowserBookmarksRepository();
michael@0 424
michael@0 425 long now = System.currentTimeMillis();
michael@0 426
michael@0 427 // Add a folder and four children.
michael@0 428 final String folderGUID = "eaaaaaaaafff";
michael@0 429 BookmarkRecord folder = new BookmarkRecord(folderGUID, "bookmarks", now -5, false);
michael@0 430 BookmarkRecord bookmarkA = new BookmarkRecord("daaaaaaaaaaa", "bookmarks", now -1, false);
michael@0 431 BookmarkRecord bookmarkB = new BookmarkRecord("baaaaaaaabbb", "bookmarks", now -3, false);
michael@0 432 BookmarkRecord bookmarkC = new BookmarkRecord("daaaaaaaaccc", "bookmarks", now -7, false);
michael@0 433 BookmarkRecord bookmarkD = new BookmarkRecord("baaaaaaaaddd", "bookmarks", now -4, false);
michael@0 434
michael@0 435 folder.children = childrenFromRecords(bookmarkA, bookmarkB, bookmarkC, bookmarkD);
michael@0 436 folder.sortIndex = 150;
michael@0 437 folder.title = "Test items";
michael@0 438 folder.parentID = "toolbar";
michael@0 439 folder.parentName = "Bookmarks Toolbar";
michael@0 440 folder.type = "folder";
michael@0 441
michael@0 442 bookmarkA.parentID = folderGUID;
michael@0 443 bookmarkA.bookmarkURI = "http://example.com/A";
michael@0 444 bookmarkA.title = "Title A";
michael@0 445 bookmarkA.type = "bookmark";
michael@0 446
michael@0 447 bookmarkB.parentID = folderGUID;
michael@0 448 bookmarkB.bookmarkURI = "http://example.com/B";
michael@0 449 bookmarkB.title = "Title B";
michael@0 450 bookmarkB.type = "bookmark";
michael@0 451
michael@0 452 bookmarkC.parentID = folderGUID;
michael@0 453 bookmarkC.bookmarkURI = "http://example.com/C";
michael@0 454 bookmarkC.title = "Title C";
michael@0 455 bookmarkC.type = "bookmark";
michael@0 456
michael@0 457 bookmarkD.parentID = folderGUID;
michael@0 458 bookmarkD.bookmarkURI = "http://example.com/D";
michael@0 459 bookmarkD.title = "Title D";
michael@0 460 bookmarkD.type = "bookmark";
michael@0 461
michael@0 462 BookmarkRecord[] abfcd = new BookmarkRecord[5];
michael@0 463 BookmarkRecord[] justFolder = new BookmarkRecord[1];
michael@0 464 abfcd[0] = bookmarkA;
michael@0 465 abfcd[1] = bookmarkB;
michael@0 466 abfcd[2] = folder;
michael@0 467 abfcd[3] = bookmarkC;
michael@0 468 abfcd[4] = bookmarkD;
michael@0 469
michael@0 470 justFolder[0] = folder;
michael@0 471
michael@0 472 final String[] abcdGUIDs = new String[] { bookmarkA.guid, bookmarkB.guid, bookmarkC.guid, bookmarkD.guid };
michael@0 473 final Record[] abcdRecords = new Record[] { bookmarkA, bookmarkB, bookmarkC, bookmarkD };
michael@0 474
michael@0 475 wipe();
michael@0 476 Logger.debug(getName(), "Storing A, B, folder, C, D...");
michael@0 477 storeRecordsInSession(repo, abfcd, null);
michael@0 478
michael@0 479 // Verify that it worked.
michael@0 480 ContentResolver cr = getApplicationContext().getContentResolver();
michael@0 481 final long folderID = fennecGetFolderId(cr, folderGUID);
michael@0 482 assertChildrenAreOrdered(repo, folderGUID, abcdRecords);
michael@0 483 assertChildrenAreDirect(folderID, abcdGUIDs);
michael@0 484
michael@0 485 now = System.currentTimeMillis();
michael@0 486
michael@0 487 // Add one child to unsorted bookmarks.
michael@0 488 BookmarkRecord unsortedA = new BookmarkRecord("yiamunsorted", "bookmarks", now, false);
michael@0 489 unsortedA.parentID = "unfiled";
michael@0 490 unsortedA.title = "Unsorted A";
michael@0 491 unsortedA.type = "bookmark";
michael@0 492 unsortedA.androidPosition = 0;
michael@0 493
michael@0 494 BookmarkRecord[] ua = new BookmarkRecord[1];
michael@0 495 ua[0] = unsortedA;
michael@0 496
michael@0 497 storeRecordsInSession(repo, ua, null);
michael@0 498
michael@0 499 // Ensure that the database is in this state.
michael@0 500 assertChildrenAreOrdered(repo, "unfiled", ua);
michael@0 501
michael@0 502 // Delete the second child, the folder, and then the third child.
michael@0 503 bookmarkB.bookmarkURI = bookmarkC.bookmarkURI = folder.bookmarkURI = null;
michael@0 504 bookmarkB.deleted = bookmarkC.deleted = folder.deleted = true;
michael@0 505 bookmarkB.title = bookmarkC.title = folder.title = null;
michael@0 506
michael@0 507 // Nulling the type of folder is very important: it verifies
michael@0 508 // that the session can behave correctly according to local type.
michael@0 509 bookmarkB.type = bookmarkC.type = folder.type = null;
michael@0 510
michael@0 511 bookmarkB.lastModified = bookmarkC.lastModified = folder.lastModified = now = System.currentTimeMillis();
michael@0 512
michael@0 513 BookmarkRecord[] deletions = new BookmarkRecord[] { bookmarkB, folder, bookmarkC };
michael@0 514 storeRecordsInSession(repo, deletions, null);
michael@0 515
michael@0 516 // Verify that the unsorted bookmarks folder contains its child and the
michael@0 517 // first and fourth children of the now-deleted folder.
michael@0 518 // Also verify that the folder is gone.
michael@0 519 long unsortedID = fennecGetFolderId(cr, "unfiled");
michael@0 520 long toolbarID = fennecGetFolderId(cr, "toolbar");
michael@0 521 String[] expected = new String[] { unsortedA.guid, bookmarkA.guid, bookmarkD.guid };
michael@0 522
michael@0 523 // This will trigger positioning.
michael@0 524 assertChildrenAreUnordered(repo, "unfiled", new Record[] { unsortedA, bookmarkA, bookmarkD });
michael@0 525 assertChildrenAreDirect(unsortedID, expected);
michael@0 526 assertChildrenAreDirect(toolbarID, new String[] {});
michael@0 527 }
michael@0 528
michael@0 529 /**
michael@0 530 * A test where we expect to replace a local folder with a new folder (with a
michael@0 531 * new GUID), whilst adding children to it. Verifies that replace and insert
michael@0 532 * co-operate.
michael@0 533 */
michael@0 534 public void testInsertAndReplaceGuid() {
michael@0 535 AndroidBrowserBookmarksRepository repo = new AndroidBrowserBookmarksRepository();
michael@0 536 wipe();
michael@0 537
michael@0 538 BookmarkRecord folder1 = BookmarkHelpers.createFolder1();
michael@0 539 BookmarkRecord folder2 = BookmarkHelpers.createFolder2(); // child of folder1
michael@0 540 BookmarkRecord folder3 = BookmarkHelpers.createFolder3(); // child of folder2
michael@0 541 BookmarkRecord bmk1 = BookmarkHelpers.createBookmark1(); // child of folder1
michael@0 542 BookmarkRecord bmk2 = BookmarkHelpers.createBookmark2(); // child of folder1
michael@0 543 BookmarkRecord bmk3 = BookmarkHelpers.createBookmark3(); // child of folder2
michael@0 544 BookmarkRecord bmk4 = BookmarkHelpers.createBookmark4(); // child of folder3
michael@0 545
michael@0 546 BookmarkRecord[] records = new BookmarkRecord[] {
michael@0 547 folder1, folder2, folder3,
michael@0 548 bmk1, bmk4
michael@0 549 };
michael@0 550 storeRecordsInSession(repo, records, null);
michael@0 551
michael@0 552 assertChildrenAreUnordered(repo, folder1.guid, new Record[] { bmk1, folder2 });
michael@0 553 assertChildrenAreUnordered(repo, folder2.guid, new Record[] { folder3 });
michael@0 554 assertChildrenAreUnordered(repo, folder3.guid, new Record[] { bmk4 });
michael@0 555
michael@0 556 // Replace folder3 with a record with a new GUID, and add bmk4 as folder3's child.
michael@0 557 final long now = System.currentTimeMillis();
michael@0 558 folder3.guid = Utils.generateGuid();
michael@0 559 folder3.lastModified = now;
michael@0 560 bmk4.title = bmk4.title + "/NEW";
michael@0 561 bmk4.parentID = folder3.guid; // Incoming child knows its parent.
michael@0 562 bmk4.parentName = folder3.title;
michael@0 563 bmk4.lastModified = now;
michael@0 564
michael@0 565 // Order of store should not matter.
michael@0 566 ArrayList<BookmarkRecord> changedRecords = new ArrayList<BookmarkRecord>();
michael@0 567 changedRecords.add(bmk2); changedRecords.add(bmk3); changedRecords.add(bmk4); changedRecords.add(folder3);
michael@0 568 Collections.shuffle(changedRecords);
michael@0 569 storeRecordsInSession(repo, changedRecords.toArray(new BookmarkRecord[changedRecords.size()]), null);
michael@0 570
michael@0 571 assertChildrenAreUnordered(repo, folder1.guid, new Record[] { bmk1, bmk2, folder2 });
michael@0 572 assertChildrenAreUnordered(repo, folder2.guid, new Record[] { bmk3, folder3 });
michael@0 573 assertChildrenAreUnordered(repo, folder3.guid, new Record[] { bmk4 });
michael@0 574
michael@0 575 assertNotNull(fetchGUID(repo, folder3.guid));
michael@0 576 assertEquals(bmk4.title, fetchGUID(repo, bmk4.guid).title);
michael@0 577 }
michael@0 578
michael@0 579 /**
michael@0 580 * A test where we expect to replace a local folder with a new folder (with a
michael@0 581 * new title but the same GUID), whilst adding children to it. Verifies that
michael@0 582 * replace and insert co-operate.
michael@0 583 */
michael@0 584 public void testInsertAndReplaceTitle() {
michael@0 585 AndroidBrowserBookmarksRepository repo = new AndroidBrowserBookmarksRepository();
michael@0 586 wipe();
michael@0 587
michael@0 588 BookmarkRecord folder1 = BookmarkHelpers.createFolder1();
michael@0 589 BookmarkRecord folder2 = BookmarkHelpers.createFolder2(); // child of folder1
michael@0 590 BookmarkRecord folder3 = BookmarkHelpers.createFolder3(); // child of folder2
michael@0 591 BookmarkRecord bmk1 = BookmarkHelpers.createBookmark1(); // child of folder1
michael@0 592 BookmarkRecord bmk2 = BookmarkHelpers.createBookmark2(); // child of folder1
michael@0 593 BookmarkRecord bmk3 = BookmarkHelpers.createBookmark3(); // child of folder2
michael@0 594 BookmarkRecord bmk4 = BookmarkHelpers.createBookmark4(); // child of folder3
michael@0 595
michael@0 596 BookmarkRecord[] records = new BookmarkRecord[] {
michael@0 597 folder1, folder2, folder3,
michael@0 598 bmk1, bmk4
michael@0 599 };
michael@0 600 storeRecordsInSession(repo, records, null);
michael@0 601
michael@0 602 assertChildrenAreUnordered(repo, folder1.guid, new Record[] { bmk1, folder2 });
michael@0 603 assertChildrenAreUnordered(repo, folder2.guid, new Record[] { folder3 });
michael@0 604 assertChildrenAreUnordered(repo, folder3.guid, new Record[] { bmk4 });
michael@0 605
michael@0 606 // Rename folder1, and add bmk2 as folder1's child.
michael@0 607 final long now = System.currentTimeMillis();
michael@0 608 folder1.title = folder1.title + "/NEW";
michael@0 609 folder1.lastModified = now;
michael@0 610 bmk2.title = bmk2.title + "/NEW";
michael@0 611 bmk2.parentID = folder1.guid; // Incoming child knows its parent.
michael@0 612 bmk2.parentName = folder1.title;
michael@0 613 bmk2.lastModified = now;
michael@0 614
michael@0 615 // Order of store should not matter.
michael@0 616 ArrayList<BookmarkRecord> changedRecords = new ArrayList<BookmarkRecord>();
michael@0 617 changedRecords.add(bmk2); changedRecords.add(bmk3); changedRecords.add(folder1);
michael@0 618 Collections.shuffle(changedRecords);
michael@0 619 storeRecordsInSession(repo, changedRecords.toArray(new BookmarkRecord[changedRecords.size()]), null);
michael@0 620
michael@0 621 assertChildrenAreUnordered(repo, folder1.guid, new Record[] { bmk1, bmk2, folder2 });
michael@0 622 assertChildrenAreUnordered(repo, folder2.guid, new Record[] { bmk3, folder3 });
michael@0 623 assertChildrenAreUnordered(repo, folder3.guid, new Record[] { bmk4 });
michael@0 624
michael@0 625 assertEquals(folder1.title, fetchGUID(repo, folder1.guid).title);
michael@0 626 assertEquals(bmk2.title, fetchGUID(repo, bmk2.guid).title);
michael@0 627 }
michael@0 628
michael@0 629 /**
michael@0 630 * Create and begin a new session, handing control to the delegate when started.
michael@0 631 * Returns when the delegate has notified.
michael@0 632 */
michael@0 633 public void inBegunSession(final AndroidBrowserBookmarksRepository repo,
michael@0 634 final RepositorySessionBeginDelegate beginDelegate) {
michael@0 635 Runnable go = new Runnable() {
michael@0 636 @Override
michael@0 637 public void run() {
michael@0 638 RepositorySessionCreationDelegate delegate = new SimpleSuccessCreationDelegate() {
michael@0 639 @Override
michael@0 640 public void onSessionCreated(final RepositorySession session) {
michael@0 641 try {
michael@0 642 session.begin(beginDelegate);
michael@0 643 } catch (InvalidSessionTransitionException e) {
michael@0 644 performNotify(e);
michael@0 645 }
michael@0 646 }
michael@0 647 };
michael@0 648 repo.createSession(delegate, getApplicationContext());
michael@0 649 }
michael@0 650 };
michael@0 651 performWait(go);
michael@0 652 }
michael@0 653
michael@0 654 /**
michael@0 655 * Finish the provided session, notifying on success.
michael@0 656 *
michael@0 657 * @param session
michael@0 658 */
michael@0 659 public void finishAndNotify(final RepositorySession session) {
michael@0 660 try {
michael@0 661 session.finish(new SimpleSuccessFinishDelegate() {
michael@0 662 @Override
michael@0 663 public void onFinishSucceeded(RepositorySession session,
michael@0 664 RepositorySessionBundle bundle) {
michael@0 665 performNotify();
michael@0 666 }
michael@0 667 });
michael@0 668 } catch (InactiveSessionException e) {
michael@0 669 performNotify(e);
michael@0 670 }
michael@0 671 }
michael@0 672
michael@0 673 /**
michael@0 674 * Simple helper class for fetching all records.
michael@0 675 * The fetched records' GUIDs are stored in `fetchedGUIDs`.
michael@0 676 */
michael@0 677 public class SimpleFetchAllBeginDelegate extends SimpleSuccessBeginDelegate {
michael@0 678 public final ArrayList<String> fetchedGUIDs = new ArrayList<String>();
michael@0 679
michael@0 680 @Override
michael@0 681 public void onBeginSucceeded(final RepositorySession session) {
michael@0 682 RepositorySessionFetchRecordsDelegate fetchDelegate = new SimpleSuccessFetchDelegate() {
michael@0 683
michael@0 684 @Override
michael@0 685 public void onFetchedRecord(Record record) {
michael@0 686 fetchedGUIDs.add(record.guid);
michael@0 687 }
michael@0 688
michael@0 689 @Override
michael@0 690 public void onFetchCompleted(long end) {
michael@0 691 finishAndNotify(session);
michael@0 692 }
michael@0 693 };
michael@0 694 session.fetchSince(0, fetchDelegate);
michael@0 695 }
michael@0 696 }
michael@0 697
michael@0 698 /**
michael@0 699 * Simple helper class for fetching a single record by GUID.
michael@0 700 * The fetched record is stored in `fetchedRecord`.
michael@0 701 */
michael@0 702 public class SimpleFetchOneBeginDelegate extends SimpleSuccessBeginDelegate {
michael@0 703 public final String guid;
michael@0 704 public Record fetchedRecord = null;
michael@0 705
michael@0 706 public SimpleFetchOneBeginDelegate(String guid) {
michael@0 707 this.guid = guid;
michael@0 708 }
michael@0 709
michael@0 710 @Override
michael@0 711 public void onBeginSucceeded(final RepositorySession session) {
michael@0 712 RepositorySessionFetchRecordsDelegate fetchDelegate = new SimpleSuccessFetchDelegate() {
michael@0 713
michael@0 714 @Override
michael@0 715 public void onFetchedRecord(Record record) {
michael@0 716 fetchedRecord = record;
michael@0 717 }
michael@0 718
michael@0 719 @Override
michael@0 720 public void onFetchCompleted(long end) {
michael@0 721 finishAndNotify(session);
michael@0 722 }
michael@0 723 };
michael@0 724 try {
michael@0 725 session.fetch(new String[] { guid }, fetchDelegate);
michael@0 726 } catch (InactiveSessionException e) {
michael@0 727 performNotify("Session is inactive.", e);
michael@0 728 }
michael@0 729 }
michael@0 730 }
michael@0 731
michael@0 732 /**
michael@0 733 * Create a new session for the given repository, storing each record
michael@0 734 * from the provided array. Notifies on failure or success.
michael@0 735 *
michael@0 736 * Optionally populates a provided Collection with tracked items.
michael@0 737 * @param repo
michael@0 738 * @param records
michael@0 739 * @param tracked
michael@0 740 */
michael@0 741 public void storeRecordsInSession(AndroidBrowserBookmarksRepository repo,
michael@0 742 final BookmarkRecord[] records,
michael@0 743 final Collection<String> tracked) {
michael@0 744 SimpleSuccessBeginDelegate beginDelegate = new SimpleSuccessBeginDelegate() {
michael@0 745 @Override
michael@0 746 public void onBeginSucceeded(final RepositorySession session) {
michael@0 747 RepositorySessionStoreDelegate storeDelegate = new SimpleSuccessStoreDelegate() {
michael@0 748
michael@0 749 @Override
michael@0 750 public void onStoreCompleted(final long storeEnd) {
michael@0 751 // Pass back whatever we tracked.
michael@0 752 if (tracked != null) {
michael@0 753 Iterator<String> iter = session.getTrackedRecordIDs();
michael@0 754 while (iter.hasNext()) {
michael@0 755 tracked.add(iter.next());
michael@0 756 }
michael@0 757 }
michael@0 758 finishAndNotify(session);
michael@0 759 }
michael@0 760
michael@0 761 @Override
michael@0 762 public void onRecordStoreSucceeded(String guid) {
michael@0 763 }
michael@0 764 };
michael@0 765 session.setStoreDelegate(storeDelegate);
michael@0 766 for (BookmarkRecord record : records) {
michael@0 767 try {
michael@0 768 session.store(record);
michael@0 769 } catch (NoStoreDelegateException e) {
michael@0 770 // Never happens.
michael@0 771 }
michael@0 772 }
michael@0 773 session.storeDone();
michael@0 774 }
michael@0 775 };
michael@0 776 inBegunSession(repo, beginDelegate);
michael@0 777 }
michael@0 778
michael@0 779 public ArrayList<String> fetchGUIDs(AndroidBrowserBookmarksRepository repo) {
michael@0 780 SimpleFetchAllBeginDelegate beginDelegate = new SimpleFetchAllBeginDelegate();
michael@0 781 inBegunSession(repo, beginDelegate);
michael@0 782 return beginDelegate.fetchedGUIDs;
michael@0 783 }
michael@0 784
michael@0 785 public BookmarkRecord fetchGUID(AndroidBrowserBookmarksRepository repo,
michael@0 786 final String guid) {
michael@0 787 Logger.info(LOG_TAG, "Fetching for " + guid);
michael@0 788 SimpleFetchOneBeginDelegate beginDelegate = new SimpleFetchOneBeginDelegate(guid);
michael@0 789 inBegunSession(repo, beginDelegate);
michael@0 790 Logger.info(LOG_TAG, "Fetched " + beginDelegate.fetchedRecord);
michael@0 791 assertTrue(beginDelegate.fetchedRecord != null);
michael@0 792 return (BookmarkRecord) beginDelegate.fetchedRecord;
michael@0 793 }
michael@0 794
michael@0 795 public JSONArray fetchChildrenForGUID(AndroidBrowserBookmarksRepository repo,
michael@0 796 final String guid) {
michael@0 797 return fetchGUID(repo, guid).children;
michael@0 798 }
michael@0 799
michael@0 800 @SuppressWarnings("unchecked")
michael@0 801 protected static JSONArray childrenFromRecords(BookmarkRecord... records) {
michael@0 802 JSONArray children = new JSONArray();
michael@0 803 for (BookmarkRecord record : records) {
michael@0 804 children.add(record.guid);
michael@0 805 }
michael@0 806 return children;
michael@0 807 }
michael@0 808
michael@0 809
michael@0 810 protected void updateRow(ContentValues values) {
michael@0 811 Uri uri = BrowserContractHelpers.BOOKMARKS_CONTENT_URI;
michael@0 812 final String where = BrowserContract.Bookmarks.GUID + " = ?";
michael@0 813 final String[] args = new String[] { values.getAsString(BrowserContract.Bookmarks.GUID) };
michael@0 814 getApplicationContext().getContentResolver().update(uri, values, where, args);
michael@0 815 }
michael@0 816
michael@0 817 protected Uri insertRow(ContentValues values) {
michael@0 818 Uri uri = BrowserContractHelpers.BOOKMARKS_CONTENT_URI;
michael@0 819 return getApplicationContext().getContentResolver().insert(uri, values);
michael@0 820 }
michael@0 821
michael@0 822 protected static ContentValues specialFolder() {
michael@0 823 ContentValues values = new ContentValues();
michael@0 824
michael@0 825 final long now = System.currentTimeMillis();
michael@0 826 values.put(Bookmarks.DATE_CREATED, now);
michael@0 827 values.put(Bookmarks.DATE_MODIFIED, now);
michael@0 828 values.put(Bookmarks.TYPE, BrowserContract.Bookmarks.TYPE_FOLDER);
michael@0 829
michael@0 830 return values;
michael@0 831 }
michael@0 832
michael@0 833 protected static ContentValues fennecMobileRecordWithoutTitle() {
michael@0 834 ContentValues values = specialFolder();
michael@0 835 values.put(BrowserContract.SyncColumns.GUID, "mobile");
michael@0 836 values.putNull(BrowserContract.Bookmarks.TITLE);
michael@0 837
michael@0 838 return values;
michael@0 839 }
michael@0 840
michael@0 841 protected ContentValues fennecPinnedItemsRecord() {
michael@0 842 final ContentValues values = specialFolder();
michael@0 843 final String title = getApplicationContext().getResources().getString(R.string.bookmarks_folder_pinned);
michael@0 844
michael@0 845 values.put(BrowserContract.SyncColumns.GUID, Bookmarks.PINNED_FOLDER_GUID);
michael@0 846 values.put(Bookmarks._ID, Bookmarks.FIXED_PINNED_LIST_ID);
michael@0 847 values.put(Bookmarks.PARENT, Bookmarks.FIXED_ROOT_ID);
michael@0 848 values.put(Bookmarks.TITLE, title);
michael@0 849 return values;
michael@0 850 }
michael@0 851
michael@0 852 protected static ContentValues fennecPinnedChildItemRecord() {
michael@0 853 ContentValues values = new ContentValues();
michael@0 854
michael@0 855 final long now = System.currentTimeMillis();
michael@0 856
michael@0 857 values.put(BrowserContract.SyncColumns.GUID, "dapinneditem");
michael@0 858 values.put(Bookmarks.DATE_CREATED, now);
michael@0 859 values.put(Bookmarks.DATE_MODIFIED, now);
michael@0 860 values.put(Bookmarks.TYPE, BrowserContract.Bookmarks.TYPE_BOOKMARK);
michael@0 861 values.put(Bookmarks.URL, "user-entered:foobar");
michael@0 862 values.put(Bookmarks.PARENT, Bookmarks.FIXED_PINNED_LIST_ID);
michael@0 863 values.put(Bookmarks.TITLE, "Foobar");
michael@0 864 return values;
michael@0 865 }
michael@0 866
michael@0 867 protected ContentValues fennecReadingListRecord() {
michael@0 868 final ContentValues values = specialFolder();
michael@0 869 final String title = getApplicationContext().getResources().getString(R.string.bookmarks_folder_reading_list);
michael@0 870
michael@0 871 values.put(BrowserContract.SyncColumns.GUID, Bookmarks.READING_LIST_FOLDER_GUID);
michael@0 872 values.put(Bookmarks._ID, Bookmarks.FIXED_READING_LIST_ID);
michael@0 873 values.put(Bookmarks.PARENT, Bookmarks.FIXED_ROOT_ID);
michael@0 874 values.put(Bookmarks.TITLE, title);
michael@0 875 return values;
michael@0 876 }
michael@0 877
michael@0 878 protected long setUpFennecMobileRecordWithoutTitle() {
michael@0 879 ContentResolver cr = getApplicationContext().getContentResolver();
michael@0 880 ContentValues values = fennecMobileRecordWithoutTitle();
michael@0 881 updateRow(values);
michael@0 882 return fennecGetMobileBookmarksFolderId(cr);
michael@0 883 }
michael@0 884
michael@0 885 protected long setUpFennecMobileRecord() {
michael@0 886 ContentResolver cr = getApplicationContext().getContentResolver();
michael@0 887 ContentValues values = fennecMobileRecordWithoutTitle();
michael@0 888 values.put(BrowserContract.Bookmarks.PARENT, BrowserContract.Bookmarks.FIXED_ROOT_ID);
michael@0 889 String title = getApplicationContext().getResources().getString(R.string.bookmarks_folder_mobile);
michael@0 890 values.put(BrowserContract.Bookmarks.TITLE, title);
michael@0 891 updateRow(values);
michael@0 892 return fennecGetMobileBookmarksFolderId(cr);
michael@0 893 }
michael@0 894
michael@0 895 protected void setUpFennecPinnedItemsRecord() {
michael@0 896 insertRow(fennecPinnedItemsRecord());
michael@0 897 insertRow(fennecPinnedChildItemRecord());
michael@0 898 }
michael@0 899
michael@0 900 protected void setUpFennecReadingListRecord() {
michael@0 901 insertRow(fennecReadingListRecord());
michael@0 902 }
michael@0 903
michael@0 904 //
michael@0 905 // Fennec fake layer.
michael@0 906 //
michael@0 907 private Uri appendProfile(Uri uri) {
michael@0 908 final String defaultProfile = "default"; // Fennec constant removed in Bug 715307.
michael@0 909 return uri.buildUpon().appendQueryParameter(BrowserContract.PARAM_PROFILE, defaultProfile).build();
michael@0 910 }
michael@0 911
michael@0 912 private long fennecGetFolderId(ContentResolver cr, String guid) {
michael@0 913 Cursor c = null;
michael@0 914 try {
michael@0 915 c = cr.query(appendProfile(BrowserContractHelpers.BOOKMARKS_CONTENT_URI),
michael@0 916 new String[] { BrowserContract.Bookmarks._ID },
michael@0 917 BrowserContract.Bookmarks.GUID + " = ?",
michael@0 918 new String[] { guid },
michael@0 919 null);
michael@0 920
michael@0 921 if (c.moveToFirst()) {
michael@0 922 return c.getLong(c.getColumnIndexOrThrow(BrowserContract.Bookmarks._ID));
michael@0 923 }
michael@0 924 return -1;
michael@0 925 } finally {
michael@0 926 if (c != null) {
michael@0 927 c.close();
michael@0 928 }
michael@0 929 }
michael@0 930 }
michael@0 931
michael@0 932 private long fennecGetMobileBookmarksFolderId(ContentResolver cr) {
michael@0 933 return fennecGetFolderId(cr, BrowserContract.Bookmarks.MOBILE_FOLDER_GUID);
michael@0 934 }
michael@0 935
michael@0 936 public void fennecAddBookmark(String title, String uri) {
michael@0 937 ContentResolver cr = getApplicationContext().getContentResolver();
michael@0 938
michael@0 939 long folderId = fennecGetMobileBookmarksFolderId(cr);
michael@0 940 if (folderId < 0) {
michael@0 941 return;
michael@0 942 }
michael@0 943
michael@0 944 ContentValues values = new ContentValues();
michael@0 945 values.put(BrowserContract.Bookmarks.TITLE, title);
michael@0 946 values.put(BrowserContract.Bookmarks.URL, uri);
michael@0 947 values.put(BrowserContract.Bookmarks.PARENT, folderId);
michael@0 948
michael@0 949 // Restore deleted record if possible
michael@0 950 values.put(BrowserContract.Bookmarks.IS_DELETED, 0);
michael@0 951
michael@0 952 Logger.debug(getName(), "Adding bookmark " + title + ", " + uri + " in " + folderId);
michael@0 953 int updated = cr.update(appendProfile(BrowserContractHelpers.BOOKMARKS_CONTENT_URI),
michael@0 954 values,
michael@0 955 BrowserContract.Bookmarks.URL + " = ?",
michael@0 956 new String[] { uri });
michael@0 957
michael@0 958 if (updated == 0) {
michael@0 959 Uri insert = cr.insert(appendProfile(BrowserContractHelpers.BOOKMARKS_CONTENT_URI), values);
michael@0 960 long idFromUri = ContentUris.parseId(insert);
michael@0 961 Logger.debug(getName(), "Inserted " + uri + " as " + idFromUri);
michael@0 962 Logger.debug(getName(), "Position is " + getPosition(idFromUri));
michael@0 963 }
michael@0 964 }
michael@0 965
michael@0 966 private long getPosition(long idFromUri) {
michael@0 967 ContentResolver cr = getApplicationContext().getContentResolver();
michael@0 968 Cursor c = cr.query(appendProfile(BrowserContractHelpers.BOOKMARKS_CONTENT_URI),
michael@0 969 new String[] { BrowserContract.Bookmarks.POSITION },
michael@0 970 BrowserContract.Bookmarks._ID + " = ?",
michael@0 971 new String[] { String.valueOf(idFromUri) },
michael@0 972 null);
michael@0 973 if (!c.moveToFirst()) {
michael@0 974 return -2;
michael@0 975 }
michael@0 976 return c.getLong(0);
michael@0 977 }
michael@0 978
michael@0 979 protected AndroidBrowserBookmarksDataAccessor dataAccessor = null;
michael@0 980 protected AndroidBrowserBookmarksDataAccessor getDataAccessor() {
michael@0 981 if (dataAccessor == null) {
michael@0 982 dataAccessor = new AndroidBrowserBookmarksDataAccessor(getApplicationContext());
michael@0 983 }
michael@0 984 return dataAccessor;
michael@0 985 }
michael@0 986
michael@0 987 protected void wipe() {
michael@0 988 Logger.debug(getName(), "Wiping.");
michael@0 989 getDataAccessor().wipe();
michael@0 990 }
michael@0 991
michael@0 992 protected void assertChildrenAreOrdered(AndroidBrowserBookmarksRepository repo, String guid, Record[] expected) {
michael@0 993 Logger.debug(getName(), "Fetching children...");
michael@0 994 JSONArray folderChildren = fetchChildrenForGUID(repo, guid);
michael@0 995
michael@0 996 assertTrue(folderChildren != null);
michael@0 997 Logger.debug(getName(), "Children are " + folderChildren.toJSONString());
michael@0 998 assertEquals(expected.length, folderChildren.size());
michael@0 999 for (int i = 0; i < expected.length; ++i) {
michael@0 1000 assertEquals(expected[i].guid, ((String) folderChildren.get(i)));
michael@0 1001 }
michael@0 1002 }
michael@0 1003
michael@0 1004 protected void assertChildrenAreUnordered(AndroidBrowserBookmarksRepository repo, String guid, Record[] expected) {
michael@0 1005 Logger.debug(getName(), "Fetching children...");
michael@0 1006 JSONArray folderChildren = fetchChildrenForGUID(repo, guid);
michael@0 1007
michael@0 1008 assertTrue(folderChildren != null);
michael@0 1009 Logger.debug(getName(), "Children are " + folderChildren.toJSONString());
michael@0 1010 assertEquals(expected.length, folderChildren.size());
michael@0 1011 for (Record record : expected) {
michael@0 1012 folderChildren.contains(record.guid);
michael@0 1013 }
michael@0 1014 }
michael@0 1015
michael@0 1016 /**
michael@0 1017 * Return a sequence of children GUIDs for the provided folder ID.
michael@0 1018 */
michael@0 1019 protected ArrayList<String> fetchChildrenDirect(long id) {
michael@0 1020 Logger.debug(getName(), "Fetching children directly from DB...");
michael@0 1021 final ArrayList<String> out = new ArrayList<String>();
michael@0 1022 final AndroidBrowserBookmarksDataAccessor accessor = new AndroidBrowserBookmarksDataAccessor(getApplicationContext());
michael@0 1023 Cursor cur = null;
michael@0 1024 try {
michael@0 1025 cur = accessor.getChildren(id);
michael@0 1026 } catch (NullCursorException e) {
michael@0 1027 fail("Got null cursor.");
michael@0 1028 }
michael@0 1029 try {
michael@0 1030 if (!cur.moveToFirst()) {
michael@0 1031 return out;
michael@0 1032 }
michael@0 1033 final int guidCol = cur.getColumnIndex(BrowserContract.SyncColumns.GUID);
michael@0 1034 while (!cur.isAfterLast()) {
michael@0 1035 out.add(cur.getString(guidCol));
michael@0 1036 cur.moveToNext();
michael@0 1037 }
michael@0 1038 } finally {
michael@0 1039 cur.close();
michael@0 1040 }
michael@0 1041 return out;
michael@0 1042 }
michael@0 1043
michael@0 1044 /**
michael@0 1045 * Assert that the children of the provided ID are correct and positioned in the database.
michael@0 1046 * @param id
michael@0 1047 * @param guids
michael@0 1048 */
michael@0 1049 protected void assertChildrenAreDirect(long id, String[] guids) {
michael@0 1050 Logger.debug(getName(), "Fetching children directly from DB...");
michael@0 1051 AndroidBrowserBookmarksDataAccessor accessor = new AndroidBrowserBookmarksDataAccessor(getApplicationContext());
michael@0 1052 Cursor cur = null;
michael@0 1053 try {
michael@0 1054 cur = accessor.getChildren(id);
michael@0 1055 } catch (NullCursorException e) {
michael@0 1056 fail("Got null cursor.");
michael@0 1057 }
michael@0 1058 try {
michael@0 1059 if (guids == null || guids.length == 0) {
michael@0 1060 assertFalse(cur.moveToFirst());
michael@0 1061 return;
michael@0 1062 }
michael@0 1063
michael@0 1064 assertTrue(cur.moveToFirst());
michael@0 1065 int i = 0;
michael@0 1066 final int guidCol = cur.getColumnIndex(BrowserContract.SyncColumns.GUID);
michael@0 1067 final int posCol = cur.getColumnIndex(BrowserContract.Bookmarks.POSITION);
michael@0 1068 while (!cur.isAfterLast()) {
michael@0 1069 assertTrue(i < guids.length);
michael@0 1070 final String guid = cur.getString(guidCol);
michael@0 1071 final int pos = cur.getInt(posCol);
michael@0 1072 Logger.debug(getName(), "Fetched child: " + guid + " has position " + pos);
michael@0 1073 assertEquals(guids[i], guid);
michael@0 1074 assertEquals(i, pos);
michael@0 1075
michael@0 1076 ++i;
michael@0 1077 cur.moveToNext();
michael@0 1078 }
michael@0 1079 assertEquals(guids.length, i);
michael@0 1080 } finally {
michael@0 1081 cur.close();
michael@0 1082 }
michael@0 1083 }
michael@0 1084 }
michael@0 1085
michael@0 1086 /**
michael@0 1087 TODO
michael@0 1088
michael@0 1089 Test for storing a record that will reconcile to mobile; postcondition is
michael@0 1090 that there's still a directory called mobile that includes all the items that
michael@0 1091 it used to.
michael@0 1092
michael@0 1093 mobile folder created without title.
michael@0 1094 Unsorted put in mobile???
michael@0 1095 Tests for children retrieval
michael@0 1096 Tests for children merge
michael@0 1097 Tests for modify retrieve parent when child added, removed, reordered (oh, reorder is hard! Any change, then.)
michael@0 1098 Safety mode?
michael@0 1099 Test storing folder first, contents first.
michael@0 1100 Store folder in next session. Verify order recovery.
michael@0 1101
michael@0 1102
michael@0 1103 */

mercurial