1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/tests/background/junit3/src/testhelpers/WBORepository.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,231 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +package org.mozilla.gecko.background.testhelpers; 1.8 + 1.9 +import java.util.Map.Entry; 1.10 +import java.util.concurrent.ConcurrentHashMap; 1.11 +import java.util.concurrent.ExecutorService; 1.12 +import java.util.concurrent.Executors; 1.13 + 1.14 +import org.mozilla.gecko.background.common.log.Logger; 1.15 +import org.mozilla.gecko.sync.repositories.InactiveSessionException; 1.16 +import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException; 1.17 +import org.mozilla.gecko.sync.repositories.NoStoreDelegateException; 1.18 +import org.mozilla.gecko.sync.repositories.RecordFilter; 1.19 +import org.mozilla.gecko.sync.repositories.Repository; 1.20 +import org.mozilla.gecko.sync.repositories.StoreTrackingRepositorySession; 1.21 +import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionBeginDelegate; 1.22 +import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionCreationDelegate; 1.23 +import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFetchRecordsDelegate; 1.24 +import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFinishDelegate; 1.25 +import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionGuidsSinceDelegate; 1.26 +import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionWipeDelegate; 1.27 +import org.mozilla.gecko.sync.repositories.domain.Record; 1.28 + 1.29 +import android.content.Context; 1.30 + 1.31 +public class WBORepository extends Repository { 1.32 + 1.33 + public class WBORepositoryStats { 1.34 + public long created = -1; 1.35 + public long begun = -1; 1.36 + public long fetchBegan = -1; 1.37 + public long fetchCompleted = -1; 1.38 + public long storeBegan = -1; 1.39 + public long storeCompleted = -1; 1.40 + public long finished = -1; 1.41 + } 1.42 + 1.43 + public static final String LOG_TAG = "WBORepository"; 1.44 + 1.45 + // Access to stats is not guarded. 1.46 + public WBORepositoryStats stats; 1.47 + 1.48 + // Whether or not to increment the timestamp of stored records. 1.49 + public final boolean bumpTimestamps; 1.50 + 1.51 + public class WBORepositorySession extends StoreTrackingRepositorySession { 1.52 + 1.53 + protected WBORepository wboRepository; 1.54 + protected ExecutorService delegateExecutor = Executors.newSingleThreadExecutor(); 1.55 + public ConcurrentHashMap<String, Record> wbos; 1.56 + 1.57 + public WBORepositorySession(WBORepository repository) { 1.58 + super(repository); 1.59 + 1.60 + wboRepository = repository; 1.61 + wbos = new ConcurrentHashMap<String, Record>(); 1.62 + stats = new WBORepositoryStats(); 1.63 + stats.created = now(); 1.64 + } 1.65 + 1.66 + @Override 1.67 + protected synchronized void trackGUID(String guid) { 1.68 + if (wboRepository.shouldTrack()) { 1.69 + super.trackGUID(guid); 1.70 + } 1.71 + } 1.72 + 1.73 + @Override 1.74 + public void guidsSince(long timestamp, 1.75 + RepositorySessionGuidsSinceDelegate delegate) { 1.76 + throw new RuntimeException("guidsSince not implemented."); 1.77 + } 1.78 + 1.79 + @Override 1.80 + public void fetchSince(long timestamp, 1.81 + RepositorySessionFetchRecordsDelegate delegate) { 1.82 + long fetchBegan = now(); 1.83 + stats.fetchBegan = fetchBegan; 1.84 + RecordFilter filter = storeTracker.getFilter(); 1.85 + 1.86 + for (Entry<String, Record> entry : wbos.entrySet()) { 1.87 + Record record = entry.getValue(); 1.88 + if (record.lastModified >= timestamp) { 1.89 + if (filter != null && 1.90 + filter.excludeRecord(record)) { 1.91 + Logger.debug(LOG_TAG, "Excluding record " + record.guid); 1.92 + continue; 1.93 + } 1.94 + delegate.deferredFetchDelegate(delegateExecutor).onFetchedRecord(record); 1.95 + } 1.96 + } 1.97 + long fetchCompleted = now(); 1.98 + stats.fetchCompleted = fetchCompleted; 1.99 + delegate.deferredFetchDelegate(delegateExecutor).onFetchCompleted(fetchCompleted); 1.100 + } 1.101 + 1.102 + @Override 1.103 + public void fetch(final String[] guids, 1.104 + final RepositorySessionFetchRecordsDelegate delegate) { 1.105 + long fetchBegan = now(); 1.106 + stats.fetchBegan = fetchBegan; 1.107 + for (String guid : guids) { 1.108 + if (wbos.containsKey(guid)) { 1.109 + delegate.deferredFetchDelegate(delegateExecutor).onFetchedRecord(wbos.get(guid)); 1.110 + } 1.111 + } 1.112 + long fetchCompleted = now(); 1.113 + stats.fetchCompleted = fetchCompleted; 1.114 + delegate.deferredFetchDelegate(delegateExecutor).onFetchCompleted(fetchCompleted); 1.115 + } 1.116 + 1.117 + @Override 1.118 + public void fetchAll(final RepositorySessionFetchRecordsDelegate delegate) { 1.119 + long fetchBegan = now(); 1.120 + stats.fetchBegan = fetchBegan; 1.121 + for (Entry<String, Record> entry : wbos.entrySet()) { 1.122 + Record record = entry.getValue(); 1.123 + delegate.deferredFetchDelegate(delegateExecutor).onFetchedRecord(record); 1.124 + } 1.125 + long fetchCompleted = now(); 1.126 + stats.fetchCompleted = fetchCompleted; 1.127 + delegate.deferredFetchDelegate(delegateExecutor).onFetchCompleted(fetchCompleted); 1.128 + } 1.129 + 1.130 + @Override 1.131 + public void store(final Record record) throws NoStoreDelegateException { 1.132 + if (delegate == null) { 1.133 + throw new NoStoreDelegateException(); 1.134 + } 1.135 + final long now = now(); 1.136 + if (stats.storeBegan < 0) { 1.137 + stats.storeBegan = now; 1.138 + } 1.139 + Record existing = wbos.get(record.guid); 1.140 + Logger.debug(LOG_TAG, "Existing record is " + (existing == null ? "<null>" : (existing.guid + ", " + existing))); 1.141 + if (existing != null && 1.142 + existing.lastModified > record.lastModified) { 1.143 + Logger.debug(LOG_TAG, "Local record is newer. Not storing."); 1.144 + delegate.deferredStoreDelegate(delegateExecutor).onRecordStoreSucceeded(record.guid); 1.145 + return; 1.146 + } 1.147 + if (existing != null) { 1.148 + Logger.debug(LOG_TAG, "Replacing local record."); 1.149 + } 1.150 + 1.151 + // Store a copy of the record with an updated modified time. 1.152 + Record toStore = record.copyWithIDs(record.guid, record.androidID); 1.153 + if (bumpTimestamps) { 1.154 + toStore.lastModified = now; 1.155 + } 1.156 + wbos.put(record.guid, toStore); 1.157 + 1.158 + trackRecord(toStore); 1.159 + delegate.deferredStoreDelegate(delegateExecutor).onRecordStoreSucceeded(record.guid); 1.160 + } 1.161 + 1.162 + @Override 1.163 + public void wipe(final RepositorySessionWipeDelegate delegate) { 1.164 + if (!isActive()) { 1.165 + delegate.onWipeFailed(new InactiveSessionException(null)); 1.166 + return; 1.167 + } 1.168 + 1.169 + Logger.info(LOG_TAG, "Wiping WBORepositorySession."); 1.170 + this.wbos = new ConcurrentHashMap<String, Record>(); 1.171 + 1.172 + // Wipe immediately for the convenience of test code. 1.173 + wboRepository.wbos = new ConcurrentHashMap<String, Record>(); 1.174 + delegate.deferredWipeDelegate(delegateExecutor).onWipeSucceeded(); 1.175 + } 1.176 + 1.177 + @Override 1.178 + public void finish(RepositorySessionFinishDelegate delegate) throws InactiveSessionException { 1.179 + Logger.info(LOG_TAG, "Finishing WBORepositorySession: handing back " + this.wbos.size() + " WBOs."); 1.180 + wboRepository.wbos = this.wbos; 1.181 + stats.finished = now(); 1.182 + super.finish(delegate); 1.183 + } 1.184 + 1.185 + @Override 1.186 + public void begin(RepositorySessionBeginDelegate delegate) throws InvalidSessionTransitionException { 1.187 + this.wbos = wboRepository.cloneWBOs(); 1.188 + stats.begun = now(); 1.189 + super.begin(delegate); 1.190 + } 1.191 + 1.192 + @Override 1.193 + public void storeDone(long end) { 1.194 + // TODO: this is not guaranteed to be called after all of the record 1.195 + // store callbacks have completed! 1.196 + if (stats.storeBegan < 0) { 1.197 + stats.storeBegan = end; 1.198 + } 1.199 + stats.storeCompleted = end; 1.200 + delegate.deferredStoreDelegate(delegateExecutor).onStoreCompleted(end); 1.201 + } 1.202 + } 1.203 + 1.204 + public ConcurrentHashMap<String, Record> wbos; 1.205 + 1.206 + public WBORepository(boolean bumpTimestamps) { 1.207 + super(); 1.208 + this.bumpTimestamps = bumpTimestamps; 1.209 + this.wbos = new ConcurrentHashMap<String, Record>(); 1.210 + } 1.211 + 1.212 + public WBORepository() { 1.213 + this(false); 1.214 + } 1.215 + 1.216 + public synchronized boolean shouldTrack() { 1.217 + return false; 1.218 + } 1.219 + 1.220 + @Override 1.221 + public void createSession(RepositorySessionCreationDelegate delegate, 1.222 + Context context) { 1.223 + delegate.deferredCreationDelegate().onSessionCreated(new WBORepositorySession(this)); 1.224 + } 1.225 + 1.226 + public ConcurrentHashMap<String, Record> cloneWBOs() { 1.227 + ConcurrentHashMap<String, Record> out = new ConcurrentHashMap<String, Record>(); 1.228 + for (Entry<String, Record> entry : wbos.entrySet()) { 1.229 + out.put(entry.getKey(), entry.getValue()); // Assume that records are 1.230 + // immutable. 1.231 + } 1.232 + return out; 1.233 + } 1.234 +}