mobile/android/tests/background/junit3/src/db/AndroidBrowserRepositoryTestCase.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.concurrent.ExecutorService;
michael@0 7
michael@0 8 import org.mozilla.gecko.background.common.log.Logger;
michael@0 9 import org.mozilla.gecko.background.helpers.AndroidSyncTestCase;
michael@0 10 import org.mozilla.gecko.background.sync.helpers.DefaultBeginDelegate;
michael@0 11 import org.mozilla.gecko.background.sync.helpers.DefaultCleanDelegate;
michael@0 12 import org.mozilla.gecko.background.sync.helpers.DefaultFetchDelegate;
michael@0 13 import org.mozilla.gecko.background.sync.helpers.DefaultFinishDelegate;
michael@0 14 import org.mozilla.gecko.background.sync.helpers.DefaultSessionCreationDelegate;
michael@0 15 import org.mozilla.gecko.background.sync.helpers.DefaultStoreDelegate;
michael@0 16 import org.mozilla.gecko.background.sync.helpers.ExpectBeginDelegate;
michael@0 17 import org.mozilla.gecko.background.sync.helpers.ExpectBeginFailDelegate;
michael@0 18 import org.mozilla.gecko.background.sync.helpers.ExpectFetchDelegate;
michael@0 19 import org.mozilla.gecko.background.sync.helpers.ExpectFetchSinceDelegate;
michael@0 20 import org.mozilla.gecko.background.sync.helpers.ExpectFinishDelegate;
michael@0 21 import org.mozilla.gecko.background.sync.helpers.ExpectFinishFailDelegate;
michael@0 22 import org.mozilla.gecko.background.sync.helpers.ExpectGuidsSinceDelegate;
michael@0 23 import org.mozilla.gecko.background.sync.helpers.ExpectInvalidRequestFetchDelegate;
michael@0 24 import org.mozilla.gecko.background.sync.helpers.ExpectManyStoredDelegate;
michael@0 25 import org.mozilla.gecko.background.sync.helpers.ExpectStoreCompletedDelegate;
michael@0 26 import org.mozilla.gecko.background.sync.helpers.ExpectStoredDelegate;
michael@0 27 import org.mozilla.gecko.background.sync.helpers.SessionTestHelper;
michael@0 28 import org.mozilla.gecko.background.testhelpers.WaitHelper;
michael@0 29 import org.mozilla.gecko.db.BrowserContract;
michael@0 30 import org.mozilla.gecko.sync.Utils;
michael@0 31 import org.mozilla.gecko.sync.repositories.InactiveSessionException;
michael@0 32 import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
michael@0 33 import org.mozilla.gecko.sync.repositories.NoStoreDelegateException;
michael@0 34 import org.mozilla.gecko.sync.repositories.Repository;
michael@0 35 import org.mozilla.gecko.sync.repositories.RepositorySession;
michael@0 36 import org.mozilla.gecko.sync.repositories.android.AndroidBrowserRepositoryDataAccessor;
michael@0 37 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionGuidsSinceDelegate;
michael@0 38 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionWipeDelegate;
michael@0 39 import org.mozilla.gecko.sync.repositories.domain.Record;
michael@0 40
michael@0 41 import android.content.ContentValues;
michael@0 42 import android.content.Context;
michael@0 43
michael@0 44 public abstract class AndroidBrowserRepositoryTestCase extends AndroidSyncTestCase {
michael@0 45 protected static String LOG_TAG = "BrowserRepositoryTest";
michael@0 46
michael@0 47 protected static void wipe(AndroidBrowserRepositoryDataAccessor helper) {
michael@0 48 Logger.debug(LOG_TAG, "Wiping.");
michael@0 49 try {
michael@0 50 helper.wipe();
michael@0 51 } catch (NullPointerException e) {
michael@0 52 // This will be handled in begin, here we can just ignore
michael@0 53 // the error if it actually occurs since this is just test
michael@0 54 // code. We will throw a ProfileDatabaseException. This
michael@0 55 // error shouldn't occur in the future, but results from
michael@0 56 // trying to access content providers before Fennec has
michael@0 57 // been run at least once.
michael@0 58 Logger.error(LOG_TAG, "ProfileDatabaseException seen in wipe. Begin should fail");
michael@0 59 fail("NullPointerException in wipe.");
michael@0 60 }
michael@0 61 }
michael@0 62
michael@0 63 @Override
michael@0 64 public void setUp() {
michael@0 65 AndroidBrowserRepositoryDataAccessor helper = getDataAccessor();
michael@0 66 wipe(helper);
michael@0 67 assertTrue(WaitHelper.getTestWaiter().isIdle());
michael@0 68 closeDataAccessor(helper);
michael@0 69 }
michael@0 70
michael@0 71 public void tearDown() {
michael@0 72 assertTrue(WaitHelper.getTestWaiter().isIdle());
michael@0 73 }
michael@0 74
michael@0 75 protected RepositorySession createSession() {
michael@0 76 return SessionTestHelper.createSession(
michael@0 77 getApplicationContext(),
michael@0 78 getRepository());
michael@0 79 }
michael@0 80
michael@0 81 protected RepositorySession createAndBeginSession() {
michael@0 82 return SessionTestHelper.createAndBeginSession(
michael@0 83 getApplicationContext(),
michael@0 84 getRepository());
michael@0 85 }
michael@0 86
michael@0 87 protected static void dispose(RepositorySession session) {
michael@0 88 if (session != null) {
michael@0 89 session.abort();
michael@0 90 }
michael@0 91 }
michael@0 92
michael@0 93 /**
michael@0 94 * Hook to return an ExpectFetchDelegate, possibly with special GUIDs ignored.
michael@0 95 */
michael@0 96 public ExpectFetchDelegate preparedExpectFetchDelegate(Record[] expected) {
michael@0 97 return new ExpectFetchDelegate(expected);
michael@0 98 }
michael@0 99
michael@0 100 /**
michael@0 101 * Hook to return an ExpectGuidsSinceDelegate, possibly with special GUIDs ignored.
michael@0 102 */
michael@0 103 public ExpectGuidsSinceDelegate preparedExpectGuidsSinceDelegate(String[] expected) {
michael@0 104 return new ExpectGuidsSinceDelegate(expected);
michael@0 105 }
michael@0 106
michael@0 107 /**
michael@0 108 * Hook to return an ExpectGuidsSinceDelegate expecting only special GUIDs (if there are any).
michael@0 109 */
michael@0 110 public ExpectGuidsSinceDelegate preparedExpectOnlySpecialGuidsSinceDelegate() {
michael@0 111 return new ExpectGuidsSinceDelegate(new String[] {});
michael@0 112 }
michael@0 113
michael@0 114 /**
michael@0 115 * Hook to return an ExpectFetchSinceDelegate, possibly with special GUIDs ignored.
michael@0 116 */
michael@0 117 public ExpectFetchSinceDelegate preparedExpectFetchSinceDelegate(long timestamp, String[] expected) {
michael@0 118 return new ExpectFetchSinceDelegate(timestamp, expected);
michael@0 119 }
michael@0 120
michael@0 121 public static Runnable storeRunnable(final RepositorySession session, final Record record, final DefaultStoreDelegate delegate) {
michael@0 122 return new Runnable() {
michael@0 123 @Override
michael@0 124 public void run() {
michael@0 125 session.setStoreDelegate(delegate);
michael@0 126 try {
michael@0 127 session.store(record);
michael@0 128 session.storeDone();
michael@0 129 } catch (NoStoreDelegateException e) {
michael@0 130 fail("NoStoreDelegateException should not occur.");
michael@0 131 }
michael@0 132 }
michael@0 133 };
michael@0 134 }
michael@0 135
michael@0 136 public static Runnable storeRunnable(final RepositorySession session, final Record record) {
michael@0 137 return storeRunnable(session, record, new ExpectStoredDelegate(record.guid));
michael@0 138 }
michael@0 139
michael@0 140 public static Runnable storeManyRunnable(final RepositorySession session, final Record[] records, final DefaultStoreDelegate delegate) {
michael@0 141 return new Runnable() {
michael@0 142 @Override
michael@0 143 public void run() {
michael@0 144 session.setStoreDelegate(delegate);
michael@0 145 try {
michael@0 146 for (Record record : records) {
michael@0 147 session.store(record);
michael@0 148 }
michael@0 149 session.storeDone();
michael@0 150 } catch (NoStoreDelegateException e) {
michael@0 151 fail("NoStoreDelegateException should not occur.");
michael@0 152 }
michael@0 153 }
michael@0 154 };
michael@0 155 }
michael@0 156
michael@0 157 public static Runnable storeManyRunnable(final RepositorySession session, final Record[] records) {
michael@0 158 return storeManyRunnable(session, records, new ExpectManyStoredDelegate(records));
michael@0 159 }
michael@0 160
michael@0 161 /**
michael@0 162 * Store a record and don't expect a store callback until we're done.
michael@0 163 *
michael@0 164 * @param session
michael@0 165 * @param record
michael@0 166 * @return Runnable.
michael@0 167 */
michael@0 168 public static Runnable quietStoreRunnable(final RepositorySession session, final Record record) {
michael@0 169 return storeRunnable(session, record, new ExpectStoreCompletedDelegate());
michael@0 170 }
michael@0 171
michael@0 172 public static Runnable beginRunnable(final RepositorySession session, final DefaultBeginDelegate delegate) {
michael@0 173 return new Runnable() {
michael@0 174 @Override
michael@0 175 public void run() {
michael@0 176 try {
michael@0 177 session.begin(delegate);
michael@0 178 } catch (InvalidSessionTransitionException e) {
michael@0 179 performNotify(e);
michael@0 180 }
michael@0 181 }
michael@0 182 };
michael@0 183 }
michael@0 184
michael@0 185 public static Runnable finishRunnable(final RepositorySession session, final DefaultFinishDelegate delegate) {
michael@0 186 return new Runnable() {
michael@0 187 @Override
michael@0 188 public void run() {
michael@0 189 try {
michael@0 190 session.finish(delegate);
michael@0 191 } catch (InactiveSessionException e) {
michael@0 192 performNotify(e);
michael@0 193 }
michael@0 194 }
michael@0 195 };
michael@0 196 }
michael@0 197
michael@0 198 public static Runnable fetchAllRunnable(final RepositorySession session, final ExpectFetchDelegate delegate) {
michael@0 199 return new Runnable() {
michael@0 200 @Override
michael@0 201 public void run() {
michael@0 202 session.fetchAll(delegate);
michael@0 203 }
michael@0 204 };
michael@0 205 }
michael@0 206
michael@0 207 public Runnable fetchAllRunnable(final RepositorySession session, final Record[] expectedRecords) {
michael@0 208 return fetchAllRunnable(session, preparedExpectFetchDelegate(expectedRecords));
michael@0 209 }
michael@0 210
michael@0 211 public Runnable guidsSinceRunnable(final RepositorySession session, final long timestamp, final String[] expected) {
michael@0 212 return new Runnable() {
michael@0 213 @Override
michael@0 214 public void run() {
michael@0 215 session.guidsSince(timestamp, preparedExpectGuidsSinceDelegate(expected));
michael@0 216 }
michael@0 217 };
michael@0 218 }
michael@0 219
michael@0 220 public Runnable fetchSinceRunnable(final RepositorySession session, final long timestamp, final String[] expected) {
michael@0 221 return new Runnable() {
michael@0 222 @Override
michael@0 223 public void run() {
michael@0 224 session.fetchSince(timestamp, preparedExpectFetchSinceDelegate(timestamp, expected));
michael@0 225 }
michael@0 226 };
michael@0 227 }
michael@0 228
michael@0 229 public static Runnable fetchRunnable(final RepositorySession session, final String[] guids, final DefaultFetchDelegate delegate) {
michael@0 230 return new Runnable() {
michael@0 231 @Override
michael@0 232 public void run() {
michael@0 233 try {
michael@0 234 session.fetch(guids, delegate);
michael@0 235 } catch (InactiveSessionException e) {
michael@0 236 performNotify(e);
michael@0 237 }
michael@0 238 }
michael@0 239 };
michael@0 240 }
michael@0 241 public Runnable fetchRunnable(final RepositorySession session, final String[] guids, final Record[] expected) {
michael@0 242 return fetchRunnable(session, guids, preparedExpectFetchDelegate(expected));
michael@0 243 }
michael@0 244
michael@0 245 public static Runnable cleanRunnable(final Repository repository, final boolean success, final Context context, final DefaultCleanDelegate delegate) {
michael@0 246 return new Runnable() {
michael@0 247 @Override
michael@0 248 public void run() {
michael@0 249 repository.clean(success, delegate, context);
michael@0 250 }
michael@0 251 };
michael@0 252 }
michael@0 253
michael@0 254 protected abstract Repository getRepository();
michael@0 255 protected abstract AndroidBrowserRepositoryDataAccessor getDataAccessor();
michael@0 256
michael@0 257 protected static void doStore(RepositorySession session, Record[] records) {
michael@0 258 performWait(storeManyRunnable(session, records));
michael@0 259 }
michael@0 260
michael@0 261 // Tests to implement
michael@0 262 public abstract void testFetchAll();
michael@0 263 public abstract void testGuidsSinceReturnMultipleRecords();
michael@0 264 public abstract void testGuidsSinceReturnNoRecords();
michael@0 265 public abstract void testFetchSinceOneRecord();
michael@0 266 public abstract void testFetchSinceReturnNoRecords();
michael@0 267 public abstract void testFetchOneRecordByGuid();
michael@0 268 public abstract void testFetchMultipleRecordsByGuids();
michael@0 269 public abstract void testFetchNoRecordByGuid();
michael@0 270 public abstract void testWipe();
michael@0 271 public abstract void testStore();
michael@0 272 public abstract void testRemoteNewerTimeStamp();
michael@0 273 public abstract void testLocalNewerTimeStamp();
michael@0 274 public abstract void testDeleteRemoteNewer();
michael@0 275 public abstract void testDeleteLocalNewer();
michael@0 276 public abstract void testDeleteRemoteLocalNonexistent();
michael@0 277 public abstract void testStoreIdenticalExceptGuid();
michael@0 278 public abstract void testCleanMultipleRecords();
michael@0 279
michael@0 280
michael@0 281 /*
michael@0 282 * Test abstractions
michael@0 283 */
michael@0 284 protected void basicStoreTest(Record record) {
michael@0 285 final RepositorySession session = createAndBeginSession();
michael@0 286 performWait(storeRunnable(session, record));
michael@0 287 }
michael@0 288
michael@0 289 protected void basicFetchAllTest(Record[] expected) {
michael@0 290 Logger.debug("rnewman", "Starting testFetchAll.");
michael@0 291 RepositorySession session = createAndBeginSession();
michael@0 292 Logger.debug("rnewman", "Prepared.");
michael@0 293
michael@0 294 AndroidBrowserRepositoryDataAccessor helper = getDataAccessor();
michael@0 295 helper.dumpDB();
michael@0 296 performWait(storeManyRunnable(session, expected));
michael@0 297
michael@0 298 helper.dumpDB();
michael@0 299 performWait(fetchAllRunnable(session, expected));
michael@0 300
michael@0 301 closeDataAccessor(helper);
michael@0 302 dispose(session);
michael@0 303 }
michael@0 304
michael@0 305 /*
michael@0 306 * Tests for clean
michael@0 307 */
michael@0 308 // Input: 4 records; 2 which are to be cleaned, 2 which should remain after the clean
michael@0 309 protected void cleanMultipleRecords(Record delete0, Record delete1, Record keep0, Record keep1, Record keep2) {
michael@0 310 RepositorySession session = createAndBeginSession();
michael@0 311 doStore(session, new Record[] {
michael@0 312 delete0, delete1, keep0, keep1, keep2
michael@0 313 });
michael@0 314
michael@0 315 // Force two records to appear deleted.
michael@0 316 AndroidBrowserRepositoryDataAccessor db = getDataAccessor();
michael@0 317 ContentValues cv = new ContentValues();
michael@0 318 cv.put(BrowserContract.SyncColumns.IS_DELETED, 1);
michael@0 319 db.updateByGuid(delete0.guid, cv);
michael@0 320 db.updateByGuid(delete1.guid, cv);
michael@0 321
michael@0 322 final DefaultCleanDelegate delegate = new DefaultCleanDelegate() {
michael@0 323 public void onCleaned(Repository repo) {
michael@0 324 performNotify();
michael@0 325 }
michael@0 326 };
michael@0 327
michael@0 328 final Runnable cleanRunnable = cleanRunnable(
michael@0 329 getRepository(),
michael@0 330 true,
michael@0 331 getApplicationContext(),
michael@0 332 delegate);
michael@0 333
michael@0 334 performWait(cleanRunnable);
michael@0 335 performWait(fetchAllRunnable(session, preparedExpectFetchDelegate(new Record[] { keep0, keep1, keep2})));
michael@0 336 closeDataAccessor(db);
michael@0 337 dispose(session);
michael@0 338 }
michael@0 339
michael@0 340 /*
michael@0 341 * Tests for guidsSince
michael@0 342 */
michael@0 343 protected void guidsSinceReturnMultipleRecords(Record record0, Record record1) {
michael@0 344 RepositorySession session = createAndBeginSession();
michael@0 345 long timestamp = System.currentTimeMillis();
michael@0 346
michael@0 347 String[] expected = new String[2];
michael@0 348 expected[0] = record0.guid;
michael@0 349 expected[1] = record1.guid;
michael@0 350
michael@0 351 Logger.debug(getName(), "Storing two records...");
michael@0 352 performWait(storeManyRunnable(session, new Record[] { record0, record1 }));
michael@0 353 Logger.debug(getName(), "Getting guids since " + timestamp + "; expecting " + expected.length);
michael@0 354 performWait(guidsSinceRunnable(session, timestamp, expected));
michael@0 355 dispose(session);
michael@0 356 }
michael@0 357
michael@0 358 protected void guidsSinceReturnNoRecords(Record record0) {
michael@0 359 RepositorySession session = createAndBeginSession();
michael@0 360
michael@0 361 // Store 1 record in the past.
michael@0 362 performWait(storeRunnable(session, record0));
michael@0 363
michael@0 364 String[] expected = {};
michael@0 365 performWait(guidsSinceRunnable(session, System.currentTimeMillis() + 1000, expected));
michael@0 366 dispose(session);
michael@0 367 }
michael@0 368
michael@0 369 /*
michael@0 370 * Tests for fetchSince
michael@0 371 */
michael@0 372 protected void fetchSinceOneRecord(Record record0, Record record1) {
michael@0 373 RepositorySession session = createAndBeginSession();
michael@0 374
michael@0 375 performWait(storeRunnable(session, record0));
michael@0 376 long timestamp = System.currentTimeMillis();
michael@0 377 Logger.debug("fetchSinceOneRecord", "Entering synchronized section. Timestamp " + timestamp);
michael@0 378 synchronized(this) {
michael@0 379 try {
michael@0 380 wait(1000);
michael@0 381 } catch (InterruptedException e) {
michael@0 382 Logger.warn("fetchSinceOneRecord", "Interrupted.", e);
michael@0 383 }
michael@0 384 }
michael@0 385 Logger.debug("fetchSinceOneRecord", "Storing.");
michael@0 386 performWait(storeRunnable(session, record1));
michael@0 387
michael@0 388 Logger.debug("fetchSinceOneRecord", "Fetching record 1.");
michael@0 389 String[] expectedOne = new String[] { record1.guid };
michael@0 390 performWait(fetchSinceRunnable(session, timestamp + 10, expectedOne));
michael@0 391
michael@0 392 Logger.debug("fetchSinceOneRecord", "Fetching both, relying on inclusiveness.");
michael@0 393 String[] expectedBoth = new String[] { record0.guid, record1.guid };
michael@0 394 performWait(fetchSinceRunnable(session, timestamp - 3000, expectedBoth));
michael@0 395
michael@0 396 Logger.debug("fetchSinceOneRecord", "Done.");
michael@0 397 dispose(session);
michael@0 398 }
michael@0 399
michael@0 400 protected void fetchSinceReturnNoRecords(Record record) {
michael@0 401 RepositorySession session = createAndBeginSession();
michael@0 402
michael@0 403 performWait(storeRunnable(session, record));
michael@0 404
michael@0 405 long timestamp = System.currentTimeMillis();
michael@0 406
michael@0 407 performWait(fetchSinceRunnable(session, timestamp + 2000, new String[] {}));
michael@0 408 dispose(session);
michael@0 409 }
michael@0 410
michael@0 411 protected void fetchOneRecordByGuid(Record record0, Record record1) {
michael@0 412 RepositorySession session = createAndBeginSession();
michael@0 413
michael@0 414 Record[] store = new Record[] { record0, record1 };
michael@0 415 performWait(storeManyRunnable(session, store));
michael@0 416
michael@0 417 String[] guids = new String[] { record0.guid };
michael@0 418 Record[] expected = new Record[] { record0 };
michael@0 419 performWait(fetchRunnable(session, guids, expected));
michael@0 420 dispose(session);
michael@0 421 }
michael@0 422
michael@0 423 protected void fetchMultipleRecordsByGuids(Record record0,
michael@0 424 Record record1, Record record2) {
michael@0 425 RepositorySession session = createAndBeginSession();
michael@0 426
michael@0 427 Record[] store = new Record[] { record0, record1, record2 };
michael@0 428 performWait(storeManyRunnable(session, store));
michael@0 429
michael@0 430 String[] guids = new String[] { record0.guid, record2.guid };
michael@0 431 Record[] expected = new Record[] { record0, record2 };
michael@0 432 performWait(fetchRunnable(session, guids, expected));
michael@0 433 dispose(session);
michael@0 434 }
michael@0 435
michael@0 436 protected void fetchNoRecordByGuid(Record record) {
michael@0 437 RepositorySession session = createAndBeginSession();
michael@0 438
michael@0 439 performWait(storeRunnable(session, record));
michael@0 440 performWait(fetchRunnable(session,
michael@0 441 new String[] { Utils.generateGuid() },
michael@0 442 new Record[] {}));
michael@0 443 dispose(session);
michael@0 444 }
michael@0 445
michael@0 446 /*
michael@0 447 * Test wipe
michael@0 448 */
michael@0 449 protected void doWipe(final Record record0, final Record record1) {
michael@0 450 final RepositorySession session = createAndBeginSession();
michael@0 451 final Runnable run = new Runnable() {
michael@0 452 @Override
michael@0 453 public void run() {
michael@0 454 session.wipe(new RepositorySessionWipeDelegate() {
michael@0 455 public void onWipeSucceeded() {
michael@0 456 performNotify();
michael@0 457 }
michael@0 458 public void onWipeFailed(Exception ex) {
michael@0 459 fail("wipe should have succeeded");
michael@0 460 performNotify();
michael@0 461 }
michael@0 462 @Override
michael@0 463 public RepositorySessionWipeDelegate deferredWipeDelegate(final ExecutorService executor) {
michael@0 464 final RepositorySessionWipeDelegate self = this;
michael@0 465 return new RepositorySessionWipeDelegate() {
michael@0 466
michael@0 467 @Override
michael@0 468 public void onWipeSucceeded() {
michael@0 469 new Thread(new Runnable() {
michael@0 470 @Override
michael@0 471 public void run() {
michael@0 472 self.onWipeSucceeded();
michael@0 473 }}).start();
michael@0 474 }
michael@0 475
michael@0 476 @Override
michael@0 477 public void onWipeFailed(final Exception ex) {
michael@0 478 new Thread(new Runnable() {
michael@0 479 @Override
michael@0 480 public void run() {
michael@0 481 self.onWipeFailed(ex);
michael@0 482 }}).start();
michael@0 483 }
michael@0 484
michael@0 485 @Override
michael@0 486 public RepositorySessionWipeDelegate deferredWipeDelegate(ExecutorService newExecutor) {
michael@0 487 if (newExecutor == executor) {
michael@0 488 return this;
michael@0 489 }
michael@0 490 throw new IllegalArgumentException("Can't re-defer this delegate.");
michael@0 491 }
michael@0 492 };
michael@0 493 }
michael@0 494 });
michael@0 495 }
michael@0 496 };
michael@0 497
michael@0 498 // Store 2 records.
michael@0 499 Record[] records = new Record[] { record0, record1 };
michael@0 500 performWait(storeManyRunnable(session, records));
michael@0 501 performWait(fetchAllRunnable(session, records));
michael@0 502
michael@0 503 // Wipe.
michael@0 504 performWait(run);
michael@0 505 dispose(session);
michael@0 506 }
michael@0 507
michael@0 508 /*
michael@0 509 * TODO adding or subtracting from lastModified timestamps does NOTHING
michael@0 510 * since it gets overwritten when we store stuff. See other tests
michael@0 511 * for ways to do this properly.
michael@0 512 */
michael@0 513
michael@0 514 /*
michael@0 515 * Record being stored has newer timestamp than existing local record, local
michael@0 516 * record has not been modified since last sync.
michael@0 517 */
michael@0 518 protected void remoteNewerTimeStamp(Record local, Record remote) {
michael@0 519 final RepositorySession session = createAndBeginSession();
michael@0 520
michael@0 521 // Record existing and hasn't changed since before lastSync.
michael@0 522 // Automatically will be assigned lastModified = current time.
michael@0 523 performWait(storeRunnable(session, local));
michael@0 524
michael@0 525 remote.guid = local.guid;
michael@0 526
michael@0 527 // Get the timestamp and make remote newer than it
michael@0 528 ExpectFetchDelegate timestampDelegate = preparedExpectFetchDelegate(new Record[] { local });
michael@0 529 performWait(fetchRunnable(session, new String[] { remote.guid }, timestampDelegate));
michael@0 530 remote.lastModified = timestampDelegate.records.get(0).lastModified + 1000;
michael@0 531 performWait(storeRunnable(session, remote));
michael@0 532
michael@0 533 Record[] expected = new Record[] { remote };
michael@0 534 ExpectFetchDelegate delegate = preparedExpectFetchDelegate(expected);
michael@0 535 performWait(fetchAllRunnable(session, delegate));
michael@0 536 dispose(session);
michael@0 537 }
michael@0 538
michael@0 539 /*
michael@0 540 * Local record has a newer timestamp than the record being stored. For now,
michael@0 541 * we just take newer (local) record)
michael@0 542 */
michael@0 543 protected void localNewerTimeStamp(Record local, Record remote) {
michael@0 544 final RepositorySession session = createAndBeginSession();
michael@0 545
michael@0 546 performWait(storeRunnable(session, local));
michael@0 547
michael@0 548 remote.guid = local.guid;
michael@0 549
michael@0 550 // Get the timestamp and make remote older than it
michael@0 551 ExpectFetchDelegate timestampDelegate = preparedExpectFetchDelegate(new Record[] { local });
michael@0 552 performWait(fetchRunnable(session, new String[] { remote.guid }, timestampDelegate));
michael@0 553 remote.lastModified = timestampDelegate.records.get(0).lastModified - 1000;
michael@0 554 performWait(storeRunnable(session, remote));
michael@0 555
michael@0 556 // Do a fetch and make sure that we get back the local record.
michael@0 557 Record[] expected = new Record[] { local };
michael@0 558 performWait(fetchAllRunnable(session, preparedExpectFetchDelegate(expected)));
michael@0 559 dispose(session);
michael@0 560 }
michael@0 561
michael@0 562 /*
michael@0 563 * Insert a record that is marked as deleted, remote has newer timestamp
michael@0 564 */
michael@0 565 protected void deleteRemoteNewer(Record local, Record remote) {
michael@0 566 final RepositorySession session = createAndBeginSession();
michael@0 567
michael@0 568 // Record existing and hasn't changed since before lastSync.
michael@0 569 // Automatically will be assigned lastModified = current time.
michael@0 570 performWait(storeRunnable(session, local));
michael@0 571
michael@0 572 // Pass the same record to store, but mark it deleted and modified
michael@0 573 // more recently
michael@0 574 ExpectFetchDelegate timestampDelegate = preparedExpectFetchDelegate(new Record[] { local });
michael@0 575 performWait(fetchRunnable(session, new String[] { local.guid }, timestampDelegate));
michael@0 576 remote.lastModified = timestampDelegate.records.get(0).lastModified + 1000;
michael@0 577 remote.deleted = true;
michael@0 578 remote.guid = local.guid;
michael@0 579 performWait(storeRunnable(session, remote));
michael@0 580
michael@0 581 performWait(fetchAllRunnable(session, preparedExpectFetchDelegate(new Record[]{})));
michael@0 582 dispose(session);
michael@0 583 }
michael@0 584
michael@0 585 // Store two records that are identical (this has different meanings based on the
michael@0 586 // type of record) other than their guids. The record existing locally already
michael@0 587 // should have its guid replaced (the assumption is that the record existed locally
michael@0 588 // and then sync was enabled and this record existed on another sync'd device).
michael@0 589 public void storeIdenticalExceptGuid(Record record0) {
michael@0 590 Logger.debug("storeIdenticalExceptGuid", "Started.");
michael@0 591 final RepositorySession session = createAndBeginSession();
michael@0 592 Logger.debug("storeIdenticalExceptGuid", "Session is " + session);
michael@0 593 performWait(storeRunnable(session, record0));
michael@0 594 Logger.debug("storeIdenticalExceptGuid", "Stored record0.");
michael@0 595 DefaultFetchDelegate timestampDelegate = getTimestampDelegate(record0.guid);
michael@0 596
michael@0 597 performWait(fetchRunnable(session, new String[] { record0.guid }, timestampDelegate));
michael@0 598 Logger.debug("storeIdenticalExceptGuid", "fetchRunnable done.");
michael@0 599 record0.lastModified = timestampDelegate.records.get(0).lastModified + 3000;
michael@0 600 record0.guid = Utils.generateGuid();
michael@0 601 Logger.debug("storeIdenticalExceptGuid", "Storing modified...");
michael@0 602 performWait(storeRunnable(session, record0));
michael@0 603 Logger.debug("storeIdenticalExceptGuid", "Stored modified.");
michael@0 604
michael@0 605 Record[] expected = new Record[] { record0 };
michael@0 606 performWait(fetchAllRunnable(session, preparedExpectFetchDelegate(expected)));
michael@0 607 Logger.debug("storeIdenticalExceptGuid", "Fetched all. Returning.");
michael@0 608 dispose(session);
michael@0 609 }
michael@0 610
michael@0 611 // Special delegate so that we don't verify parenting is correct since
michael@0 612 // at some points it won't be since parent folder hasn't been stored.
michael@0 613 private DefaultFetchDelegate getTimestampDelegate(final String guid) {
michael@0 614 return new DefaultFetchDelegate() {
michael@0 615 @Override
michael@0 616 public void onFetchCompleted(final long fetchEnd) {
michael@0 617 assertEquals(guid, this.records.get(0).guid);
michael@0 618 performNotify();
michael@0 619 }
michael@0 620 };
michael@0 621 }
michael@0 622
michael@0 623 /*
michael@0 624 * Insert a record that is marked as deleted, local has newer timestamp
michael@0 625 * and was not marked deleted (so keep it)
michael@0 626 */
michael@0 627 protected void deleteLocalNewer(Record local, Record remote) {
michael@0 628 Logger.debug("deleteLocalNewer", "Begin.");
michael@0 629 final RepositorySession session = createAndBeginSession();
michael@0 630
michael@0 631 Logger.debug("deleteLocalNewer", "Storing local...");
michael@0 632 performWait(storeRunnable(session, local));
michael@0 633
michael@0 634 // Create an older version of a record with the same GUID.
michael@0 635 remote.guid = local.guid;
michael@0 636
michael@0 637 Logger.debug("deleteLocalNewer", "Fetching...");
michael@0 638
michael@0 639 // Get the timestamp and make remote older than it
michael@0 640 Record[] expected = new Record[] { local };
michael@0 641 ExpectFetchDelegate timestampDelegate = preparedExpectFetchDelegate(expected);
michael@0 642 performWait(fetchRunnable(session, new String[] { remote.guid }, timestampDelegate));
michael@0 643
michael@0 644 Logger.debug("deleteLocalNewer", "Fetched.");
michael@0 645 remote.lastModified = timestampDelegate.records.get(0).lastModified - 1000;
michael@0 646
michael@0 647 Logger.debug("deleteLocalNewer", "Last modified is " + remote.lastModified);
michael@0 648 remote.deleted = true;
michael@0 649 Logger.debug("deleteLocalNewer", "Storing deleted...");
michael@0 650 performWait(quietStoreRunnable(session, remote)); // This appears to do a lot of work...?!
michael@0 651
michael@0 652 // Do a fetch and make sure that we get back the first (local) record.
michael@0 653 performWait(fetchAllRunnable(session, preparedExpectFetchDelegate(expected)));
michael@0 654 Logger.debug("deleteLocalNewer", "Fetched and done!");
michael@0 655 dispose(session);
michael@0 656 }
michael@0 657
michael@0 658 /*
michael@0 659 * Insert a record that is marked as deleted, record never existed locally
michael@0 660 */
michael@0 661 protected void deleteRemoteLocalNonexistent(Record remote) {
michael@0 662 final RepositorySession session = createAndBeginSession();
michael@0 663
michael@0 664 long timestamp = 1000000000;
michael@0 665
michael@0 666 // Pass a record marked deleted to store, doesn't exist locally
michael@0 667 remote.lastModified = timestamp;
michael@0 668 remote.deleted = true;
michael@0 669 performWait(quietStoreRunnable(session, remote));
michael@0 670
michael@0 671 ExpectFetchDelegate delegate = preparedExpectFetchDelegate(new Record[]{});
michael@0 672 performWait(fetchAllRunnable(session, delegate));
michael@0 673 dispose(session);
michael@0 674 }
michael@0 675
michael@0 676 /*
michael@0 677 * Tests that don't require specific records based on type of repository.
michael@0 678 * These tests don't need to be overriden in subclasses, they will just work.
michael@0 679 */
michael@0 680 public void testCreateSessionNullContext() {
michael@0 681 Logger.debug(LOG_TAG, "In testCreateSessionNullContext.");
michael@0 682 Repository repo = getRepository();
michael@0 683 try {
michael@0 684 repo.createSession(new DefaultSessionCreationDelegate(), null);
michael@0 685 fail("Should throw.");
michael@0 686 } catch (Exception ex) {
michael@0 687 assertNotNull(ex);
michael@0 688 }
michael@0 689 }
michael@0 690
michael@0 691 public void testStoreNullRecord() {
michael@0 692 final RepositorySession session = createAndBeginSession();
michael@0 693 try {
michael@0 694 session.setStoreDelegate(new DefaultStoreDelegate());
michael@0 695 session.store(null);
michael@0 696 fail("Should throw.");
michael@0 697 } catch (Exception ex) {
michael@0 698 assertNotNull(ex);
michael@0 699 }
michael@0 700 dispose(session);
michael@0 701 }
michael@0 702
michael@0 703 public void testFetchNoGuids() {
michael@0 704 final RepositorySession session = createAndBeginSession();
michael@0 705 performWait(fetchRunnable(session, new String[] {}, new ExpectInvalidRequestFetchDelegate()));
michael@0 706 dispose(session);
michael@0 707 }
michael@0 708
michael@0 709 public void testFetchNullGuids() {
michael@0 710 final RepositorySession session = createAndBeginSession();
michael@0 711 performWait(fetchRunnable(session, null, new ExpectInvalidRequestFetchDelegate()));
michael@0 712 dispose(session);
michael@0 713 }
michael@0 714
michael@0 715 public void testBeginOnNewSession() {
michael@0 716 final RepositorySession session = createSession();
michael@0 717 performWait(beginRunnable(session, new ExpectBeginDelegate()));
michael@0 718 dispose(session);
michael@0 719 }
michael@0 720
michael@0 721 public void testBeginOnRunningSession() {
michael@0 722 final RepositorySession session = createAndBeginSession();
michael@0 723 try {
michael@0 724 session.begin(new ExpectBeginFailDelegate());
michael@0 725 } catch (InvalidSessionTransitionException e) {
michael@0 726 dispose(session);
michael@0 727 return;
michael@0 728 }
michael@0 729 fail("Should have caught InvalidSessionTransitionException.");
michael@0 730 }
michael@0 731
michael@0 732 public void testBeginOnFinishedSession() throws InactiveSessionException {
michael@0 733 final RepositorySession session = createAndBeginSession();
michael@0 734 performWait(finishRunnable(session, new ExpectFinishDelegate()));
michael@0 735 try {
michael@0 736 session.begin(new ExpectBeginFailDelegate());
michael@0 737 } catch (InvalidSessionTransitionException e) {
michael@0 738 Logger.debug(getName(), "Yay! Got an exception.", e);
michael@0 739 dispose(session);
michael@0 740 return;
michael@0 741 } catch (Exception e) {
michael@0 742 Logger.debug(getName(), "Yay! Got an exception.", e);
michael@0 743 dispose(session);
michael@0 744 return;
michael@0 745 }
michael@0 746 fail("Should have caught InvalidSessionTransitionException.");
michael@0 747 }
michael@0 748
michael@0 749 public void testFinishOnFinishedSession() throws InactiveSessionException {
michael@0 750 final RepositorySession session = createAndBeginSession();
michael@0 751 performWait(finishRunnable(session, new ExpectFinishDelegate()));
michael@0 752 try {
michael@0 753 session.finish(new ExpectFinishFailDelegate());
michael@0 754 } catch (InactiveSessionException e) {
michael@0 755 dispose(session);
michael@0 756 return;
michael@0 757 }
michael@0 758 fail("Should have caught InactiveSessionException.");
michael@0 759 }
michael@0 760
michael@0 761 public void testFetchOnInactiveSession() throws InactiveSessionException {
michael@0 762 final RepositorySession session = createSession();
michael@0 763 try {
michael@0 764 session.fetch(new String[] { Utils.generateGuid() }, new DefaultFetchDelegate());
michael@0 765 } catch (InactiveSessionException e) {
michael@0 766 // Yay.
michael@0 767 dispose(session);
michael@0 768 return;
michael@0 769 };
michael@0 770 fail("Should have caught InactiveSessionException.");
michael@0 771 }
michael@0 772
michael@0 773 public void testFetchOnFinishedSession() {
michael@0 774 final RepositorySession session = createAndBeginSession();
michael@0 775 Logger.debug(getName(), "Finishing...");
michael@0 776 performWait(finishRunnable(session, new ExpectFinishDelegate()));
michael@0 777 try {
michael@0 778 session.fetch(new String[] { Utils.generateGuid() }, new DefaultFetchDelegate());
michael@0 779 } catch (InactiveSessionException e) {
michael@0 780 // Yay.
michael@0 781 dispose(session);
michael@0 782 return;
michael@0 783 };
michael@0 784 fail("Should have caught InactiveSessionException.");
michael@0 785 }
michael@0 786
michael@0 787 public void testGuidsSinceOnUnstartedSession() {
michael@0 788 final RepositorySession session = createSession();
michael@0 789 Runnable run = new Runnable() {
michael@0 790 @Override
michael@0 791 public void run() {
michael@0 792 session.guidsSince(System.currentTimeMillis(),
michael@0 793 new RepositorySessionGuidsSinceDelegate() {
michael@0 794 public void onGuidsSinceSucceeded(String[] guids) {
michael@0 795 fail("Session inactive, should fail");
michael@0 796 performNotify();
michael@0 797 }
michael@0 798
michael@0 799 public void onGuidsSinceFailed(Exception ex) {
michael@0 800 verifyInactiveException(ex);
michael@0 801 performNotify();
michael@0 802 }
michael@0 803 });
michael@0 804 }
michael@0 805 };
michael@0 806 performWait(run);
michael@0 807 dispose(session);
michael@0 808 }
michael@0 809
michael@0 810 private static void verifyInactiveException(Exception ex) {
michael@0 811 if (!(ex instanceof InactiveSessionException)) {
michael@0 812 fail("Wrong exception type");
michael@0 813 }
michael@0 814 }
michael@0 815
michael@0 816 protected void closeDataAccessor(AndroidBrowserRepositoryDataAccessor dataAccessor) {
michael@0 817 }
michael@0 818 }

mercurial