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.
5 package org.chromium.sync.test.util;
8 import android.accounts.Account;
9 import android.content.ContentResolver;
10 import android.content.SyncStatusObserver;
11 import android.os.AsyncTask;
13 import junit.framework.Assert;
15 import org.chromium.base.ThreadUtils;
16 import org.chromium.sync.notifier.SyncContentResolverDelegate;
18 import java.util.HashMap;
19 import java.util.HashSet;
22 import java.util.concurrent.Semaphore;
23 import java.util.concurrent.TimeUnit;
27 * Mock implementation of the SyncContentResolverWrapper.
29 * This implementation only supports status change listeners for the type
30 * SYNC_OBSERVER_TYPE_SETTINGS.
32 public class MockSyncContentResolverDelegate implements SyncContentResolverDelegate {
34 private final Set<String> mSyncAutomaticallySet;
35 private final Map<String, Boolean> mIsSyncableMap;
36 private final Object mSyncableMapLock = new Object();
38 private final Set<AsyncSyncStatusObserver> mObservers;
40 private boolean mMasterSyncAutomatically;
41 private boolean mDisableObserverNotifications;
43 private Semaphore mPendingObserverCount;
45 public MockSyncContentResolverDelegate() {
46 mSyncAutomaticallySet = new HashSet<String>();
47 mIsSyncableMap = new HashMap<String, Boolean>();
48 mObservers = new HashSet<AsyncSyncStatusObserver>();
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");
57 AsyncSyncStatusObserver asyncSyncStatusObserver = new AsyncSyncStatusObserver(callback);
58 synchronized (mObservers) {
59 mObservers.add(asyncSyncStatusObserver);
61 return asyncSyncStatusObserver;
65 public void removeStatusChangeListener(Object handle) {
66 synchronized (mObservers) {
67 mObservers.remove(handle);
72 public void setMasterSyncAutomatically(boolean sync) {
73 if (mMasterSyncAutomatically == sync) return;
75 mMasterSyncAutomatically = sync;
80 public boolean getMasterSyncAutomatically() {
81 return mMasterSyncAutomatically;
85 public boolean getSyncAutomatically(Account account, String authority) {
86 String key = createKey(account, authority);
87 synchronized (mSyncableMapLock) {
88 return mSyncAutomaticallySet.contains(key);
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);
102 mSyncAutomaticallySet.add(key);
103 } else if (mSyncAutomaticallySet.contains(key)) {
104 mSyncAutomaticallySet.remove(key);
111 public void setIsSyncable(Account account, String authority, int syncable) {
112 String key = createKey(account, authority);
114 synchronized (mSyncableMapLock) {
117 if (mSyncAutomaticallySet.contains(key)) {
118 mSyncAutomaticallySet.remove(key);
121 mIsSyncableMap.put(key, false);
124 mIsSyncableMap.put(key, true);
127 if (mIsSyncableMap.containsKey(key)) {
128 mIsSyncableMap.remove(key);
132 throw new IllegalArgumentException("Unable to understand syncable argument: " +
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;
151 private static String createKey(Account account, String authority) {
152 return account.name + "@@@" + account.type + "@@@" + authority;
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);
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.
170 * @throws InterruptedException
172 public void waitForLastNotificationCompleted() throws InterruptedException {
173 Assert.assertTrue("Timed out waiting for notifications to complete.",
174 mPendingObserverCount.tryAcquire(5, TimeUnit.SECONDS));
177 public void disableObserverNotifications() {
178 mDisableObserverNotifications = true;
181 private static class AsyncSyncStatusObserver {
183 private final SyncStatusObserver mSyncStatusObserver;
185 private AsyncSyncStatusObserver(SyncStatusObserver syncStatusObserver) {
186 mSyncStatusObserver = syncStatusObserver;
189 private void notifyObserverAsync(final Semaphore pendingObserverCount) {
190 if (ThreadUtils.runningOnUiThread()) {
191 new AsyncTask<Void, Void, Void>() {
193 protected Void doInBackground(Void... params) {
194 mSyncStatusObserver.onStatusChanged(
195 ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
200 protected void onPostExecute(Void result) {
201 pendingObserverCount.release();
205 mSyncStatusObserver.onStatusChanged(
206 ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
207 pendingObserverCount.release();