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