1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 package org.mozilla.android.sync.test; 5 6 import android.content.Context; 7 import org.mozilla.gecko.background.common.log.Logger; 8 import org.mozilla.gecko.background.testhelpers.WBORepository; 9 import org.mozilla.gecko.sync.repositories.FetchFailedException; 10 import org.mozilla.gecko.sync.repositories.InactiveSessionException; 11 import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException; 12 import org.mozilla.gecko.sync.repositories.NoStoreDelegateException; 13 import org.mozilla.gecko.sync.repositories.StoreFailedException; 14 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionBeginDelegate; 15 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionCreationDelegate; 16 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFetchRecordsDelegate; 17 import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFinishDelegate; 18 import org.mozilla.gecko.sync.repositories.domain.Record; 19 20 import java.util.ArrayList; 21 import java.util.concurrent.ExecutorService; 22 23 public class SynchronizerHelpers { 24 public static final String FAIL_SENTINEL = "Fail"; 25 26 /** 27 * Store one at a time, failing if the guid contains FAIL_SENTINEL. 28 */ 29 public static class FailFetchWBORepository extends WBORepository { 30 @Override createSession(RepositorySessionCreationDelegate delegate, Context context)31 public void createSession(RepositorySessionCreationDelegate delegate, 32 Context context) { 33 delegate.deferredCreationDelegate().onSessionCreated(new WBORepositorySession(this) { 34 @Override 35 public void fetchSince(long timestamp, 36 final RepositorySessionFetchRecordsDelegate delegate) { 37 super.fetchSince(timestamp, new RepositorySessionFetchRecordsDelegate() { 38 @Override 39 public void onFetchedRecord(Record record) { 40 if (record.guid.contains(FAIL_SENTINEL)) { 41 delegate.onFetchFailed(new FetchFailedException(), record); 42 } else { 43 delegate.onFetchedRecord(record); 44 } 45 } 46 47 @Override 48 public void onFetchFailed(Exception ex, Record record) { 49 delegate.onFetchFailed(ex, record); 50 } 51 52 @Override 53 public void onFetchCompleted(long fetchEnd) { 54 delegate.onFetchCompleted(fetchEnd); 55 } 56 57 @Override 58 public RepositorySessionFetchRecordsDelegate deferredFetchDelegate(ExecutorService executor) { 59 return this; 60 } 61 }); 62 } 63 }); 64 } 65 } 66 67 /** 68 * Store one at a time, failing if the guid contains FAIL_SENTINEL. 69 */ 70 public static class SerialFailStoreWBORepository extends WBORepository { 71 @Override createSession(RepositorySessionCreationDelegate delegate, Context context)72 public void createSession(RepositorySessionCreationDelegate delegate, 73 Context context) { 74 delegate.deferredCreationDelegate().onSessionCreated(new WBORepositorySession(this) { 75 @Override 76 public void store(final Record record) throws NoStoreDelegateException { 77 if (delegate == null) { 78 throw new NoStoreDelegateException(); 79 } 80 if (record.guid.contains(FAIL_SENTINEL)) { 81 delegate.onRecordStoreFailed(new StoreFailedException(), record.guid); 82 } else { 83 super.store(record); 84 } 85 } 86 }); 87 } 88 } 89 90 /** 91 * Store in batches, failing if any of the batch guids contains "Fail". 92 * <p> 93 * This will drop the final batch. 94 */ 95 public static class BatchFailStoreWBORepository extends WBORepository { 96 public final int batchSize; 97 public ArrayList<Record> batch = new ArrayList<Record>(); 98 public boolean batchShouldFail = false; 99 100 public class BatchFailStoreWBORepositorySession extends WBORepositorySession { BatchFailStoreWBORepositorySession(WBORepository repository)101 public BatchFailStoreWBORepositorySession(WBORepository repository) { 102 super(repository); 103 } 104 superStore(final Record record)105 public void superStore(final Record record) throws NoStoreDelegateException { 106 super.store(record); 107 } 108 109 @Override store(final Record record)110 public void store(final Record record) throws NoStoreDelegateException { 111 if (delegate == null) { 112 throw new NoStoreDelegateException(); 113 } 114 synchronized (batch) { 115 batch.add(record); 116 if (record.guid.contains("Fail")) { 117 batchShouldFail = true; 118 } 119 120 if (batch.size() >= batchSize) { 121 flush(); 122 } 123 } 124 } 125 flush()126 public void flush() { 127 final ArrayList<Record> thisBatch = new ArrayList<Record>(batch); 128 final boolean thisBatchShouldFail = batchShouldFail; 129 batchShouldFail = false; 130 batch.clear(); 131 storeWorkQueue.execute(new Runnable() { 132 @Override 133 public void run() { 134 Logger.trace("XXX", "Notifying about batch. Failure? " + thisBatchShouldFail); 135 for (Record batchRecord : thisBatch) { 136 if (thisBatchShouldFail) { 137 delegate.onRecordStoreFailed(new StoreFailedException(), batchRecord.guid); 138 } else { 139 try { 140 superStore(batchRecord); 141 } catch (NoStoreDelegateException e) { 142 delegate.onRecordStoreFailed(e, batchRecord.guid); 143 } 144 } 145 } 146 } 147 }); 148 } 149 150 @Override storeDone()151 public void storeDone() { 152 synchronized (batch) { 153 flush(); 154 // Do this in a Runnable so that the timestamp is grabbed after any upload. 155 final Runnable r = new Runnable() { 156 @Override 157 public void run() { 158 synchronized (batch) { 159 Logger.trace("XXX", "Calling storeDone."); 160 storeDone(now()); 161 } 162 } 163 }; 164 storeWorkQueue.execute(r); 165 } 166 } 167 } BatchFailStoreWBORepository(int batchSize)168 public BatchFailStoreWBORepository(int batchSize) { 169 super(); 170 this.batchSize = batchSize; 171 } 172 173 @Override createSession(RepositorySessionCreationDelegate delegate, Context context)174 public void createSession(RepositorySessionCreationDelegate delegate, 175 Context context) { 176 delegate.deferredCreationDelegate().onSessionCreated(new BatchFailStoreWBORepositorySession(this)); 177 } 178 } 179 180 public static class TrackingWBORepository extends WBORepository { 181 @Override shouldTrack()182 public synchronized boolean shouldTrack() { 183 return true; 184 } 185 } 186 187 public static class BeginFailedException extends Exception { 188 private static final long serialVersionUID = -2349459755976915096L; 189 } 190 191 public static class FinishFailedException extends Exception { 192 private static final long serialVersionUID = -4644528423867070934L; 193 } 194 195 public static class BeginErrorWBORepository extends TrackingWBORepository { 196 @Override createSession(RepositorySessionCreationDelegate delegate, Context context)197 public void createSession(RepositorySessionCreationDelegate delegate, 198 Context context) { 199 delegate.deferredCreationDelegate().onSessionCreated(new BeginErrorWBORepositorySession(this)); 200 } 201 202 public class BeginErrorWBORepositorySession extends WBORepositorySession { BeginErrorWBORepositorySession(WBORepository repository)203 public BeginErrorWBORepositorySession(WBORepository repository) { 204 super(repository); 205 } 206 207 @Override begin(RepositorySessionBeginDelegate delegate)208 public void begin(RepositorySessionBeginDelegate delegate) throws InvalidSessionTransitionException { 209 delegate.onBeginFailed(new BeginFailedException()); 210 } 211 } 212 } 213 214 public static class FinishErrorWBORepository extends TrackingWBORepository { 215 @Override createSession(RepositorySessionCreationDelegate delegate, Context context)216 public void createSession(RepositorySessionCreationDelegate delegate, 217 Context context) { 218 delegate.deferredCreationDelegate().onSessionCreated(new FinishErrorWBORepositorySession(this)); 219 } 220 221 public class FinishErrorWBORepositorySession extends WBORepositorySession { FinishErrorWBORepositorySession(WBORepository repository)222 public FinishErrorWBORepositorySession(WBORepository repository) { 223 super(repository); 224 } 225 226 @Override finish(final RepositorySessionFinishDelegate delegate)227 public void finish(final RepositorySessionFinishDelegate delegate) throws InactiveSessionException { 228 delegate.onFinishFailed(new FinishFailedException()); 229 } 230 } 231 } 232 233 public static class DataAvailableWBORepository extends TrackingWBORepository { 234 public boolean dataAvailable = true; 235 DataAvailableWBORepository(boolean dataAvailable)236 public DataAvailableWBORepository(boolean dataAvailable) { 237 this.dataAvailable = dataAvailable; 238 } 239 240 @Override createSession(RepositorySessionCreationDelegate delegate, Context context)241 public void createSession(RepositorySessionCreationDelegate delegate, 242 Context context) { 243 delegate.deferredCreationDelegate().onSessionCreated(new DataAvailableWBORepositorySession(this)); 244 } 245 246 public class DataAvailableWBORepositorySession extends WBORepositorySession { DataAvailableWBORepositorySession(WBORepository repository)247 public DataAvailableWBORepositorySession(WBORepository repository) { 248 super(repository); 249 } 250 251 @Override dataAvailable()252 public boolean dataAvailable() { 253 return dataAvailable; 254 } 255 } 256 } 257 258 public static class ShouldSkipWBORepository extends TrackingWBORepository { 259 public boolean shouldSkip = true; 260 ShouldSkipWBORepository(boolean shouldSkip)261 public ShouldSkipWBORepository(boolean shouldSkip) { 262 this.shouldSkip = shouldSkip; 263 } 264 265 @Override createSession(RepositorySessionCreationDelegate delegate, Context context)266 public void createSession(RepositorySessionCreationDelegate delegate, 267 Context context) { 268 delegate.deferredCreationDelegate().onSessionCreated(new ShouldSkipWBORepositorySession(this)); 269 } 270 271 public class ShouldSkipWBORepositorySession extends WBORepositorySession { ShouldSkipWBORepositorySession(WBORepository repository)272 public ShouldSkipWBORepositorySession(WBORepository repository) { 273 super(repository); 274 } 275 276 @Override shouldSkip()277 public boolean shouldSkip() { 278 return shouldSkip; 279 } 280 } 281 } 282 } 283