mobile/android/tests/background/junit3/src/sync/TestStoreTracking.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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.sync;
michael@0 5
michael@0 6 import java.util.concurrent.atomic.AtomicBoolean;
michael@0 7 import java.util.concurrent.atomic.AtomicLong;
michael@0 8
michael@0 9 import junit.framework.AssertionFailedError;
michael@0 10
michael@0 11 import org.mozilla.gecko.background.common.log.Logger;
michael@0 12 import org.mozilla.gecko.background.helpers.AndroidSyncTestCase;
michael@0 13 import org.mozilla.gecko.background.sync.helpers.SimpleSuccessBeginDelegate;
michael@0 14 import org.mozilla.gecko.background.sync.helpers.SimpleSuccessCreationDelegate;
michael@0 15 import org.mozilla.gecko.background.sync.helpers.SimpleSuccessFetchDelegate;
michael@0 16 import org.mozilla.gecko.background.sync.helpers.SimpleSuccessFinishDelegate;
michael@0 17 import org.mozilla.gecko.background.sync.helpers.SimpleSuccessStoreDelegate;
michael@0 18 import org.mozilla.gecko.background.testhelpers.WBORepository;
michael@0 19 import org.mozilla.gecko.sync.CryptoRecord;
michael@0 20 import org.mozilla.gecko.sync.ExtendedJSONObject;
michael@0 21 import org.mozilla.gecko.sync.repositories.InactiveSessionException;
michael@0 22 import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException;
michael@0 23 import org.mozilla.gecko.sync.repositories.NoStoreDelegateException;
michael@0 24 import org.mozilla.gecko.sync.repositories.RepositorySession;
michael@0 25 import org.mozilla.gecko.sync.repositories.RepositorySessionBundle;
michael@0 26 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionCreationDelegate;
michael@0 27 import org.mozilla.gecko.sync.repositories.domain.BookmarkRecord;
michael@0 28 import org.mozilla.gecko.sync.repositories.domain.Record;
michael@0 29 import org.mozilla.gecko.sync.synchronizer.Synchronizer;
michael@0 30 import org.mozilla.gecko.sync.synchronizer.SynchronizerDelegate;
michael@0 31
michael@0 32 import android.content.Context;
michael@0 33
michael@0 34 public class TestStoreTracking extends AndroidSyncTestCase {
michael@0 35 public void assertEq(Object expected, Object actual) {
michael@0 36 try {
michael@0 37 assertEquals(expected, actual);
michael@0 38 } catch (AssertionFailedError e) {
michael@0 39 performNotify(e);
michael@0 40 }
michael@0 41 }
michael@0 42
michael@0 43 public class TrackingWBORepository extends WBORepository {
michael@0 44 @Override
michael@0 45 public synchronized boolean shouldTrack() {
michael@0 46 return true;
michael@0 47 }
michael@0 48 }
michael@0 49
michael@0 50 public void doTestStoreRetrieveByGUID(final WBORepository repository,
michael@0 51 final RepositorySession session,
michael@0 52 final String expectedGUID,
michael@0 53 final Record record) {
michael@0 54
michael@0 55 final SimpleSuccessStoreDelegate storeDelegate = new SimpleSuccessStoreDelegate() {
michael@0 56
michael@0 57 @Override
michael@0 58 public void onRecordStoreSucceeded(String guid) {
michael@0 59 Logger.debug(getName(), "Stored " + guid);
michael@0 60 assertEq(expectedGUID, guid);
michael@0 61 }
michael@0 62
michael@0 63 @Override
michael@0 64 public void onStoreCompleted(long storeEnd) {
michael@0 65 Logger.debug(getName(), "Store completed at " + storeEnd + ".");
michael@0 66 try {
michael@0 67 session.fetch(new String[] { expectedGUID }, new SimpleSuccessFetchDelegate() {
michael@0 68 @Override
michael@0 69 public void onFetchedRecord(Record record) {
michael@0 70 Logger.debug(getName(), "Hurrah! Fetched record " + record.guid);
michael@0 71 assertEq(expectedGUID, record.guid);
michael@0 72 }
michael@0 73
michael@0 74 @Override
michael@0 75 public void onFetchCompleted(final long fetchEnd) {
michael@0 76 Logger.debug(getName(), "Fetch completed at " + fetchEnd + ".");
michael@0 77
michael@0 78 // But fetching by time returns nothing.
michael@0 79 session.fetchSince(0, new SimpleSuccessFetchDelegate() {
michael@0 80 private AtomicBoolean fetched = new AtomicBoolean(false);
michael@0 81
michael@0 82 @Override
michael@0 83 public void onFetchedRecord(Record record) {
michael@0 84 Logger.debug(getName(), "Fetched record " + record.guid);
michael@0 85 fetched.set(true);
michael@0 86 performNotify(new AssertionFailedError("Should have fetched no record!"));
michael@0 87 }
michael@0 88
michael@0 89 @Override
michael@0 90 public void onFetchCompleted(final long fetchEnd) {
michael@0 91 if (fetched.get()) {
michael@0 92 Logger.debug(getName(), "Not finishing session: record retrieved.");
michael@0 93 return;
michael@0 94 }
michael@0 95 try {
michael@0 96 session.finish(new SimpleSuccessFinishDelegate() {
michael@0 97 @Override
michael@0 98 public void onFinishSucceeded(RepositorySession session,
michael@0 99 RepositorySessionBundle bundle) {
michael@0 100 performNotify();
michael@0 101 }
michael@0 102 });
michael@0 103 } catch (InactiveSessionException e) {
michael@0 104 performNotify(e);
michael@0 105 }
michael@0 106 }
michael@0 107 });
michael@0 108 }
michael@0 109 });
michael@0 110 } catch (InactiveSessionException e) {
michael@0 111 performNotify(e);
michael@0 112 }
michael@0 113 }
michael@0 114 };
michael@0 115
michael@0 116 session.setStoreDelegate(storeDelegate);
michael@0 117 try {
michael@0 118 Logger.debug(getName(), "Storing...");
michael@0 119 session.store(record);
michael@0 120 session.storeDone();
michael@0 121 } catch (NoStoreDelegateException e) {
michael@0 122 // Should not happen.
michael@0 123 }
michael@0 124 }
michael@0 125
michael@0 126 private void doTestNewSessionRetrieveByTime(final WBORepository repository,
michael@0 127 final String expectedGUID) {
michael@0 128 final SimpleSuccessCreationDelegate createDelegate = new SimpleSuccessCreationDelegate() {
michael@0 129 @Override
michael@0 130 public void onSessionCreated(final RepositorySession session) {
michael@0 131 Logger.debug(getName(), "Session created.");
michael@0 132 try {
michael@0 133 session.begin(new SimpleSuccessBeginDelegate() {
michael@0 134 @Override
michael@0 135 public void onBeginSucceeded(final RepositorySession session) {
michael@0 136 // Now we get a result.
michael@0 137 session.fetchSince(0, new SimpleSuccessFetchDelegate() {
michael@0 138
michael@0 139 @Override
michael@0 140 public void onFetchedRecord(Record record) {
michael@0 141 assertEq(expectedGUID, record.guid);
michael@0 142 }
michael@0 143
michael@0 144 @Override
michael@0 145 public void onFetchCompleted(long end) {
michael@0 146 try {
michael@0 147 session.finish(new SimpleSuccessFinishDelegate() {
michael@0 148 @Override
michael@0 149 public void onFinishSucceeded(RepositorySession session,
michael@0 150 RepositorySessionBundle bundle) {
michael@0 151 // Hooray!
michael@0 152 performNotify();
michael@0 153 }
michael@0 154 });
michael@0 155 } catch (InactiveSessionException e) {
michael@0 156 performNotify(e);
michael@0 157 }
michael@0 158 }
michael@0 159 });
michael@0 160 }
michael@0 161 });
michael@0 162 } catch (InvalidSessionTransitionException e) {
michael@0 163 performNotify(e);
michael@0 164 }
michael@0 165 }
michael@0 166 };
michael@0 167 Runnable create = new Runnable() {
michael@0 168 @Override
michael@0 169 public void run() {
michael@0 170 repository.createSession(createDelegate, getApplicationContext());
michael@0 171 }
michael@0 172 };
michael@0 173
michael@0 174 performWait(create);
michael@0 175 }
michael@0 176
michael@0 177 /**
michael@0 178 * Store a record in one session. Verify that fetching by GUID returns
michael@0 179 * the record. Verify that fetching by timestamp fails to return records.
michael@0 180 * Start a new session. Verify that fetching by timestamp returns the
michael@0 181 * stored record.
michael@0 182 *
michael@0 183 * Invokes doTestStoreRetrieveByGUID, doTestNewSessionRetrieveByTime.
michael@0 184 */
michael@0 185 public void testStoreRetrieveByGUID() {
michael@0 186 Logger.debug(getName(), "Started.");
michael@0 187 final WBORepository r = new TrackingWBORepository();
michael@0 188 final long now = System.currentTimeMillis();
michael@0 189 final String expectedGUID = "abcdefghijkl";
michael@0 190 final Record record = new BookmarkRecord(expectedGUID, "bookmarks", now , false);
michael@0 191
michael@0 192 final RepositorySessionCreationDelegate createDelegate = new SimpleSuccessCreationDelegate() {
michael@0 193 @Override
michael@0 194 public void onSessionCreated(RepositorySession session) {
michael@0 195 Logger.debug(getName(), "Session created: " + session);
michael@0 196 try {
michael@0 197 session.begin(new SimpleSuccessBeginDelegate() {
michael@0 198 @Override
michael@0 199 public void onBeginSucceeded(final RepositorySession session) {
michael@0 200 doTestStoreRetrieveByGUID(r, session, expectedGUID, record);
michael@0 201 }
michael@0 202 });
michael@0 203 } catch (InvalidSessionTransitionException e) {
michael@0 204 performNotify(e);
michael@0 205 }
michael@0 206 }
michael@0 207 };
michael@0 208
michael@0 209 final Context applicationContext = getApplicationContext();
michael@0 210
michael@0 211 // This has to happen on a new thread so that we
michael@0 212 // can wait for it!
michael@0 213 Runnable create = onThreadRunnable(new Runnable() {
michael@0 214 @Override
michael@0 215 public void run() {
michael@0 216 r.createSession(createDelegate, applicationContext);
michael@0 217 }
michael@0 218 });
michael@0 219
michael@0 220 Runnable retrieve = onThreadRunnable(new Runnable() {
michael@0 221 @Override
michael@0 222 public void run() {
michael@0 223 doTestNewSessionRetrieveByTime(r, expectedGUID);
michael@0 224 performNotify();
michael@0 225 }
michael@0 226 });
michael@0 227
michael@0 228 performWait(create);
michael@0 229 performWait(retrieve);
michael@0 230 }
michael@0 231
michael@0 232 private Runnable onThreadRunnable(final Runnable r) {
michael@0 233 return new Runnable() {
michael@0 234 @Override
michael@0 235 public void run() {
michael@0 236 new Thread(r).start();
michael@0 237 }
michael@0 238 };
michael@0 239 }
michael@0 240
michael@0 241
michael@0 242 public class CountingWBORepository extends TrackingWBORepository {
michael@0 243 public AtomicLong counter = new AtomicLong(0L);
michael@0 244 public class CountingWBORepositorySession extends WBORepositorySession {
michael@0 245 private static final String LOG_TAG = "CountingRepoSession";
michael@0 246
michael@0 247 public CountingWBORepositorySession(WBORepository repository) {
michael@0 248 super(repository);
michael@0 249 }
michael@0 250
michael@0 251 @Override
michael@0 252 public void store(final Record record) throws NoStoreDelegateException {
michael@0 253 Logger.debug(LOG_TAG, "Counter now " + counter.incrementAndGet());
michael@0 254 super.store(record);
michael@0 255 }
michael@0 256 }
michael@0 257
michael@0 258 @Override
michael@0 259 public void createSession(RepositorySessionCreationDelegate delegate,
michael@0 260 Context context) {
michael@0 261 delegate.deferredCreationDelegate().onSessionCreated(new CountingWBORepositorySession(this));
michael@0 262 }
michael@0 263 }
michael@0 264
michael@0 265 public class TestRecord extends Record {
michael@0 266 public TestRecord(String guid, String collection, long lastModified,
michael@0 267 boolean deleted) {
michael@0 268 super(guid, collection, lastModified, deleted);
michael@0 269 }
michael@0 270
michael@0 271 @Override
michael@0 272 public void initFromEnvelope(CryptoRecord payload) {
michael@0 273 return;
michael@0 274 }
michael@0 275
michael@0 276 @Override
michael@0 277 public CryptoRecord getEnvelope() {
michael@0 278 return null;
michael@0 279 }
michael@0 280
michael@0 281 @Override
michael@0 282 protected void populatePayload(ExtendedJSONObject payload) {
michael@0 283 }
michael@0 284
michael@0 285 @Override
michael@0 286 protected void initFromPayload(ExtendedJSONObject payload) {
michael@0 287 }
michael@0 288
michael@0 289 @Override
michael@0 290 public Record copyWithIDs(String guid, long androidID) {
michael@0 291 return new TestRecord(guid, this.collection, this.lastModified, this.deleted);
michael@0 292 }
michael@0 293 }
michael@0 294
michael@0 295 /**
michael@0 296 * Create two repositories, syncing from one to the other. Ensure
michael@0 297 * that records stored from one aren't re-uploaded.
michael@0 298 */
michael@0 299 public void testStoreBetweenRepositories() {
michael@0 300 final CountingWBORepository repoA = new CountingWBORepository(); // "Remote". First source.
michael@0 301 final CountingWBORepository repoB = new CountingWBORepository(); // "Local". First sink.
michael@0 302 long now = System.currentTimeMillis();
michael@0 303
michael@0 304 TestRecord recordA1 = new TestRecord("aacdefghiaaa", "coll", now - 30, false);
michael@0 305 TestRecord recordA2 = new TestRecord("aacdefghibbb", "coll", now - 20, false);
michael@0 306 TestRecord recordB1 = new TestRecord("aacdefghiaaa", "coll", now - 10, false);
michael@0 307 TestRecord recordB2 = new TestRecord("aacdefghibbb", "coll", now - 40, false);
michael@0 308
michael@0 309 TestRecord recordA3 = new TestRecord("nncdefghibbb", "coll", now, false);
michael@0 310 TestRecord recordB3 = new TestRecord("nncdefghiaaa", "coll", now, false);
michael@0 311
michael@0 312 // A1 and B1 are the same, but B's version is newer. We expect A1 to be downloaded
michael@0 313 // and B1 to be uploaded.
michael@0 314 // A2 and B2 are the same, but A's version is newer. We expect A2 to be downloaded
michael@0 315 // and B2 to not be uploaded.
michael@0 316 // Both A3 and B3 are new. We expect them to go in each direction.
michael@0 317 // Expected counts, then:
michael@0 318 // Repo A: B1 + B3
michael@0 319 // Repo B: A1 + A2 + A3
michael@0 320 repoB.wbos.put(recordB1.guid, recordB1);
michael@0 321 repoB.wbos.put(recordB2.guid, recordB2);
michael@0 322 repoB.wbos.put(recordB3.guid, recordB3);
michael@0 323 repoA.wbos.put(recordA1.guid, recordA1);
michael@0 324 repoA.wbos.put(recordA2.guid, recordA2);
michael@0 325 repoA.wbos.put(recordA3.guid, recordA3);
michael@0 326
michael@0 327 final Synchronizer s = new Synchronizer();
michael@0 328 s.repositoryA = repoA;
michael@0 329 s.repositoryB = repoB;
michael@0 330
michael@0 331 Runnable r = new Runnable() {
michael@0 332 @Override
michael@0 333 public void run() {
michael@0 334 s.synchronize(getApplicationContext(), new SynchronizerDelegate() {
michael@0 335
michael@0 336 @Override
michael@0 337 public void onSynchronized(Synchronizer synchronizer) {
michael@0 338 long countA = repoA.counter.get();
michael@0 339 long countB = repoB.counter.get();
michael@0 340 Logger.debug(getName(), "Counts: " + countA + ", " + countB);
michael@0 341 assertEq(2L, countA);
michael@0 342 assertEq(3L, countB);
michael@0 343
michael@0 344 // Testing for store timestamp 'hack'.
michael@0 345 // We fetched from A first, and so its bundle timestamp will be the last
michael@0 346 // stored time. We fetched from B second, so its bundle timestamp will be
michael@0 347 // the last fetched time.
michael@0 348 final long timestampA = synchronizer.bundleA.getTimestamp();
michael@0 349 final long timestampB = synchronizer.bundleB.getTimestamp();
michael@0 350 Logger.debug(getName(), "Repo A timestamp: " + timestampA);
michael@0 351 Logger.debug(getName(), "Repo B timestamp: " + timestampB);
michael@0 352 Logger.debug(getName(), "Repo A fetch done: " + repoA.stats.fetchCompleted);
michael@0 353 Logger.debug(getName(), "Repo A store done: " + repoA.stats.storeCompleted);
michael@0 354 Logger.debug(getName(), "Repo B fetch done: " + repoB.stats.fetchCompleted);
michael@0 355 Logger.debug(getName(), "Repo B store done: " + repoB.stats.storeCompleted);
michael@0 356
michael@0 357 assertTrue(timestampB <= timestampA);
michael@0 358 assertTrue(repoA.stats.fetchCompleted <= timestampA);
michael@0 359 assertTrue(repoA.stats.storeCompleted >= repoA.stats.fetchCompleted);
michael@0 360 assertEquals(repoA.stats.storeCompleted, timestampA);
michael@0 361 assertEquals(repoB.stats.fetchCompleted, timestampB);
michael@0 362 performNotify();
michael@0 363 }
michael@0 364
michael@0 365 @Override
michael@0 366 public void onSynchronizeFailed(Synchronizer synchronizer,
michael@0 367 Exception lastException, String reason) {
michael@0 368 Logger.debug(getName(), "Failed.");
michael@0 369 performNotify(new AssertionFailedError("Should not fail."));
michael@0 370 }
michael@0 371 });
michael@0 372 }
michael@0 373 };
michael@0 374
michael@0 375 performWait(onThreadRunnable(r));
michael@0 376 }
michael@0 377 }

mercurial