- add sources.
[platform/framework/web/crosswalk.git] / src / sync / test / android / javatests / src / org / chromium / sync / test / util / MockSyncContentResolverDelegate.java
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.sync.test.util;
6
7
8 import android.accounts.Account;
9 import android.content.ContentResolver;
10 import android.content.SyncStatusObserver;
11 import android.os.AsyncTask;
12
13 import junit.framework.Assert;
14
15 import org.chromium.base.ThreadUtils;
16 import org.chromium.sync.notifier.SyncContentResolverDelegate;
17
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.concurrent.Semaphore;
23 import java.util.concurrent.TimeUnit;
24
25
26 /**
27  * Mock implementation of the SyncContentResolverWrapper.
28  *
29  * This implementation only supports status change listeners for the type
30  * SYNC_OBSERVER_TYPE_SETTINGS.
31  */
32 public class MockSyncContentResolverDelegate implements SyncContentResolverDelegate {
33
34     private final Set<String> mSyncAutomaticallySet;
35     private final Map<String, Boolean> mIsSyncableMap;
36     private final Object mSyncableMapLock = new Object();
37
38     private final Set<AsyncSyncStatusObserver> mObservers;
39
40     private boolean mMasterSyncAutomatically;
41     private boolean mDisableObserverNotifications;
42
43     private Semaphore mPendingObserverCount;
44
45     public MockSyncContentResolverDelegate() {
46         mSyncAutomaticallySet = new HashSet<String>();
47         mIsSyncableMap = new HashMap<String, Boolean>();
48         mObservers = new HashSet<AsyncSyncStatusObserver>();
49     }
50
51     @Override
52     public Object addStatusChangeListener(int mask, SyncStatusObserver callback) {
53         if (mask != ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS) {
54             throw new IllegalArgumentException("This implementation only supports "
55                     + "ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS as the mask");
56         }
57         AsyncSyncStatusObserver asyncSyncStatusObserver = new AsyncSyncStatusObserver(callback);
58         synchronized (mObservers) {
59             mObservers.add(asyncSyncStatusObserver);
60         }
61         return asyncSyncStatusObserver;
62     }
63
64     @Override
65     public void removeStatusChangeListener(Object handle) {
66         synchronized (mObservers) {
67             mObservers.remove(handle);
68         }
69     }
70
71     @Override
72     public void setMasterSyncAutomatically(boolean sync) {
73         if (mMasterSyncAutomatically == sync) return;
74
75         mMasterSyncAutomatically = sync;
76         notifyObservers();
77     }
78
79     @Override
80     public boolean getMasterSyncAutomatically() {
81         return mMasterSyncAutomatically;
82     }
83
84     @Override
85     public boolean getSyncAutomatically(Account account, String authority) {
86         String key = createKey(account, authority);
87         synchronized (mSyncableMapLock) {
88             return mSyncAutomaticallySet.contains(key);
89         }
90     }
91
92     @Override
93     public void setSyncAutomatically(Account account, String authority, boolean sync) {
94         String key = createKey(account, authority);
95         synchronized (mSyncableMapLock) {
96             if (!mIsSyncableMap.containsKey(key) || !mIsSyncableMap.get(key)) {
97                 throw new IllegalArgumentException("Account " + account +
98                         " is not syncable for authority " + authority +
99                         ". Can not set sync state to " + sync);
100             }
101             if (sync) {
102                 mSyncAutomaticallySet.add(key);
103             } else if (mSyncAutomaticallySet.contains(key)) {
104                 mSyncAutomaticallySet.remove(key);
105             }
106         }
107         notifyObservers();
108     }
109
110     @Override
111     public void setIsSyncable(Account account, String authority, int syncable) {
112         String key = createKey(account, authority);
113
114         synchronized (mSyncableMapLock) {
115             switch (syncable) {
116                 case 0:
117                     if (mSyncAutomaticallySet.contains(key)) {
118                         mSyncAutomaticallySet.remove(key);
119                     }
120
121                     mIsSyncableMap.put(key, false);
122                     break;
123                 case 1:
124                     mIsSyncableMap.put(key, true);
125                     break;
126                 case -1:
127                     if (mIsSyncableMap.containsKey(key)) {
128                         mIsSyncableMap.remove(key);
129                     }
130                     break;
131                 default:
132                     throw new IllegalArgumentException("Unable to understand syncable argument: " +
133                             syncable);
134             }
135         }
136         notifyObservers();
137     }
138
139     @Override
140     public int getIsSyncable(Account account, String authority) {
141         String key = createKey(account, authority);
142         synchronized (mSyncableMapLock) {
143             if (mIsSyncableMap.containsKey(key)) {
144                 return mIsSyncableMap.containsKey(key) ? 1 : 0;
145             } else {
146                 return -1;
147             }
148         }
149     }
150
151     private static String createKey(Account account, String authority) {
152         return account.name + "@@@" + account.type + "@@@" + authority;
153     }
154
155     private void notifyObservers() {
156         if (mDisableObserverNotifications) return;
157         synchronized (mObservers) {
158             mPendingObserverCount = new Semaphore(1 - mObservers.size());
159             for (AsyncSyncStatusObserver observer : mObservers) {
160                 observer.notifyObserverAsync(mPendingObserverCount);
161             }
162         }
163     }
164
165     /**
166      * Blocks until the last notification has been issued to all registered observers.
167      * Note that if an observer is removed while a notification is being handled this can
168      * fail to return correctly.
169      *
170      * @throws InterruptedException
171      */
172     public void waitForLastNotificationCompleted() throws InterruptedException {
173         Assert.assertTrue("Timed out waiting for notifications to complete.",
174                 mPendingObserverCount.tryAcquire(5, TimeUnit.SECONDS));
175     }
176
177     public void disableObserverNotifications() {
178         mDisableObserverNotifications = true;
179     }
180
181     private static class AsyncSyncStatusObserver {
182
183         private final SyncStatusObserver mSyncStatusObserver;
184
185         private AsyncSyncStatusObserver(SyncStatusObserver syncStatusObserver) {
186             mSyncStatusObserver = syncStatusObserver;
187         }
188
189         private void notifyObserverAsync(final Semaphore pendingObserverCount) {
190             if (ThreadUtils.runningOnUiThread()) {
191                 new AsyncTask<Void, Void, Void>() {
192                     @Override
193                     protected Void doInBackground(Void... params) {
194                         mSyncStatusObserver.onStatusChanged(
195                                 ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
196                         return null;
197                     }
198
199                     @Override
200                     protected void onPostExecute(Void result) {
201                         pendingObserverCount.release();
202                     }
203                 }.execute();
204             } else {
205                 mSyncStatusObserver.onStatusChanged(
206                         ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
207                 pendingObserverCount.release();
208             }
209         }
210     }
211 }