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