| OLD | NEW |
| (Empty) |
| 1 // Copyright 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 import android.accounts.Account; | |
| 8 import android.accounts.AccountManager; | |
| 9 import android.accounts.AuthenticatorDescription; | |
| 10 import android.content.Context; | |
| 11 import android.content.Intent; | |
| 12 import android.os.AsyncTask; | |
| 13 | |
| 14 import org.chromium.base.Callback; | |
| 15 import org.chromium.base.Log; | |
| 16 import org.chromium.base.VisibleForTesting; | |
| 17 import org.chromium.sync.signin.AccountManagerDelegate; | |
| 18 import org.chromium.sync.signin.AccountManagerHelper; | |
| 19 | |
| 20 import java.util.ArrayList; | |
| 21 import java.util.HashSet; | |
| 22 import java.util.Set; | |
| 23 import java.util.UUID; | |
| 24 import java.util.concurrent.LinkedBlockingDeque; | |
| 25 import java.util.concurrent.ThreadPoolExecutor; | |
| 26 import java.util.concurrent.TimeUnit; | |
| 27 | |
| 28 /** | |
| 29 * The MockAccountManager helps out if you want to mock out all calls to the And
roid AccountManager. | |
| 30 * | |
| 31 * You should provide a set of accounts as a constructor argument, or use the mo
re direct approach | |
| 32 * and provide an array of AccountHolder objects. | |
| 33 * | |
| 34 * Currently, this implementation supports adding and removing accounts, handlin
g credentials | |
| 35 * (including confirming them), and handling of dummy auth tokens. | |
| 36 * | |
| 37 * If you want to auto-approve a given authtokentype, use addAccountHolderExplic
itly(...) with | |
| 38 * an AccountHolder you have built with hasBeenAccepted("yourAuthTokenType", tru
e). | |
| 39 * | |
| 40 * If you want to auto-approve all auth token types for a given account, use the
{@link | |
| 41 * AccountHolder} builder method alwaysAccept(true). | |
| 42 */ | |
| 43 public class MockAccountManager implements AccountManagerDelegate { | |
| 44 | |
| 45 private static final String TAG = "MockAccountManager"; | |
| 46 | |
| 47 protected final Context mContext; | |
| 48 | |
| 49 private final Set<AccountHolder> mAccounts; | |
| 50 | |
| 51 // Tracks the number of in-progress getAccountsByType() tasks so that tests
can wait for | |
| 52 // their completion. | |
| 53 private final ZeroCounter mGetAccountsTaskCounter; | |
| 54 | |
| 55 @VisibleForTesting | |
| 56 public MockAccountManager(Context context, Context testContext, Account... a
ccounts) { | |
| 57 mContext = context; | |
| 58 mGetAccountsTaskCounter = new ZeroCounter(); | |
| 59 mAccounts = new HashSet<AccountHolder>(); | |
| 60 if (accounts != null) { | |
| 61 for (Account account : accounts) { | |
| 62 mAccounts.add(AccountHolder.create().account(account).alwaysAcce
pt(true).build()); | |
| 63 } | |
| 64 } | |
| 65 } | |
| 66 | |
| 67 private static class SingleThreadedExecutor extends ThreadPoolExecutor { | |
| 68 public SingleThreadedExecutor() { | |
| 69 super(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>()
); | |
| 70 } | |
| 71 } | |
| 72 | |
| 73 @Override | |
| 74 public Account[] getAccountsByType(String type) { | |
| 75 if (!AccountManagerHelper.GOOGLE_ACCOUNT_TYPE.equals(type)) { | |
| 76 throw new IllegalArgumentException("Invalid account type: " + type); | |
| 77 } | |
| 78 if (mAccounts == null) { | |
| 79 return new Account[0]; | |
| 80 } else { | |
| 81 ArrayList<Account> validAccounts = new ArrayList<Account>(); | |
| 82 for (AccountHolder ah : mAccounts) { | |
| 83 if (type.equals(ah.getAccount().type)) { | |
| 84 validAccounts.add(ah.getAccount()); | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 Account[] accounts = new Account[validAccounts.size()]; | |
| 89 int i = 0; | |
| 90 for (Account account : validAccounts) { | |
| 91 accounts[i++] = account; | |
| 92 } | |
| 93 return accounts; | |
| 94 } | |
| 95 } | |
| 96 | |
| 97 @Override | |
| 98 public void getAccountsByType(final String type, final Callback<Account[]> c
allback) { | |
| 99 mGetAccountsTaskCounter.increment(); | |
| 100 new AsyncTask<Void, Void, Account[]>() { | |
| 101 @Override | |
| 102 protected Account[] doInBackground(Void... params) { | |
| 103 return getAccountsByType(type); | |
| 104 } | |
| 105 | |
| 106 @Override | |
| 107 protected void onPostExecute(Account[] accounts) { | |
| 108 callback.onResult(accounts); | |
| 109 mGetAccountsTaskCounter.decrement(); | |
| 110 } | |
| 111 }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); | |
| 112 } | |
| 113 | |
| 114 @VisibleForTesting | |
| 115 public void waitForGetAccountsTask() throws InterruptedException { | |
| 116 // Wait until all tasks are done because we don't know which is being wa
ited for. | |
| 117 mGetAccountsTaskCounter.waitUntilZero(); | |
| 118 } | |
| 119 | |
| 120 @VisibleForTesting | |
| 121 public boolean addAccountHolderExplicitly(AccountHolder accountHolder) { | |
| 122 return addAccountHolderExplicitly(accountHolder, false); | |
| 123 } | |
| 124 | |
| 125 /** | |
| 126 * Add an AccountHolder directly. | |
| 127 * | |
| 128 * @param accountHolder the account holder to add | |
| 129 * @param broadcastEvent whether to broadcast an AccountChangedEvent | |
| 130 * @return whether the account holder was added successfully | |
| 131 */ | |
| 132 public boolean addAccountHolderExplicitly(AccountHolder accountHolder, | |
| 133 boolean broadcastEvent) { | |
| 134 boolean result = mAccounts.add(accountHolder); | |
| 135 if (broadcastEvent) { | |
| 136 postAsyncAccountChangedEvent(); | |
| 137 } | |
| 138 return result; | |
| 139 } | |
| 140 | |
| 141 @VisibleForTesting | |
| 142 public boolean removeAccountHolderExplicitly(AccountHolder accountHolder) { | |
| 143 return removeAccountHolderExplicitly(accountHolder, false); | |
| 144 } | |
| 145 | |
| 146 /** | |
| 147 * Remove an AccountHolder directly. | |
| 148 * | |
| 149 * @param accountHolder the account holder to remove | |
| 150 * @param broadcastEvent whether to broadcast an AccountChangedEvent | |
| 151 * @return whether the account holder was removed successfully | |
| 152 */ | |
| 153 public boolean removeAccountHolderExplicitly(AccountHolder accountHolder, | |
| 154 boolean broadcastEvent) { | |
| 155 boolean result = mAccounts.remove(accountHolder); | |
| 156 if (broadcastEvent) { | |
| 157 postAsyncAccountChangedEvent(); | |
| 158 } | |
| 159 return result; | |
| 160 } | |
| 161 | |
| 162 @Override | |
| 163 public String getAuthToken(Account account, String authTokenScope) { | |
| 164 AccountHolder ah = getAccountHolder(account); | |
| 165 assert ah.hasBeenAccepted(authTokenScope); | |
| 166 synchronized (mAccounts) { | |
| 167 // Some tests register auth tokens with value null, and those should
be preserved. | |
| 168 if (!ah.hasAuthTokenRegistered(authTokenScope) | |
| 169 && ah.getAuthToken(authTokenScope) == null) { | |
| 170 // No authtoken registered. Need to create one. | |
| 171 String authToken = UUID.randomUUID().toString(); | |
| 172 Log.d(TAG, "Created new auth token for " + ah.getAccount() + ":
authTokenScope = " | |
| 173 + authTokenScope + ", authToken = " + authToken)
; | |
| 174 ah = ah.withAuthToken(authTokenScope, authToken); | |
| 175 mAccounts.add(ah); | |
| 176 } | |
| 177 } | |
| 178 return ah.getAuthToken(authTokenScope); | |
| 179 } | |
| 180 | |
| 181 @Override | |
| 182 public void invalidateAuthToken(String authToken) { | |
| 183 if (authToken == null) { | |
| 184 throw new IllegalArgumentException("AuthToken can not be null"); | |
| 185 } | |
| 186 for (AccountHolder ah : mAccounts) { | |
| 187 if (ah.removeAuthToken(authToken)) { | |
| 188 break; | |
| 189 } | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 @Override | |
| 194 public AuthenticatorDescription[] getAuthenticatorTypes() { | |
| 195 AuthenticatorDescription googleAuthenticator = new AuthenticatorDescript
ion( | |
| 196 AccountManagerHelper.GOOGLE_ACCOUNT_TYPE, "p1", 0, 0, 0, 0); | |
| 197 | |
| 198 return new AuthenticatorDescription[] { googleAuthenticator }; | |
| 199 } | |
| 200 | |
| 201 @Override | |
| 202 public void hasFeatures( | |
| 203 Account account, final String[] features, final Callback<Boolean> ca
llback) { | |
| 204 final AccountHolder accountHolder = getAccountHolder(account); | |
| 205 accountHolder.addFeaturesCallback(new Runnable() { | |
| 206 @Override | |
| 207 public void run() { | |
| 208 Set<String> accountFeatures = accountHolder.getFeatures(); | |
| 209 boolean hasAllFeatures = true; | |
| 210 for (String feature : features) { | |
| 211 if (!accountFeatures.contains(feature)) { | |
| 212 Log.d(TAG, accountFeatures + " does not contain " + feat
ure); | |
| 213 hasAllFeatures = false; | |
| 214 } | |
| 215 } | |
| 216 callback.onResult(hasAllFeatures); | |
| 217 } | |
| 218 }); | |
| 219 } | |
| 220 | |
| 221 private AccountHolder getAccountHolder(Account account) { | |
| 222 if (account == null) { | |
| 223 throw new IllegalArgumentException("Account can not be null"); | |
| 224 } | |
| 225 for (AccountHolder accountHolder : mAccounts) { | |
| 226 if (account.equals(accountHolder.getAccount())) { | |
| 227 return accountHolder; | |
| 228 } | |
| 229 } | |
| 230 throw new IllegalArgumentException("Can not find AccountHolder for accou
nt " + account); | |
| 231 } | |
| 232 | |
| 233 private void postAsyncAccountChangedEvent() { | |
| 234 // Mimic that this does not happen on the main thread. | |
| 235 new AsyncTask<Void, Void, Void>() { | |
| 236 @Override | |
| 237 protected Void doInBackground(Void... params) { | |
| 238 mContext.sendBroadcast(new Intent(AccountManager.LOGIN_ACCOUNTS_
CHANGED_ACTION)); | |
| 239 return null; | |
| 240 } | |
| 241 }.execute(); | |
| 242 } | |
| 243 | |
| 244 /** | |
| 245 * Simple concurrency helper class for waiting until a resource count become
s zero. | |
| 246 */ | |
| 247 private static class ZeroCounter { | |
| 248 private static final int WAIT_TIMEOUT_MS = 10000; | |
| 249 | |
| 250 private final Object mLock = new Object(); | |
| 251 private int mCount = 0; | |
| 252 | |
| 253 public void increment() { | |
| 254 synchronized (mLock) { | |
| 255 mCount++; | |
| 256 } | |
| 257 } | |
| 258 | |
| 259 public void decrement() { | |
| 260 synchronized (mLock) { | |
| 261 if (--mCount == 0) { | |
| 262 mLock.notifyAll(); | |
| 263 } | |
| 264 } | |
| 265 } | |
| 266 | |
| 267 public void waitUntilZero() throws InterruptedException { | |
| 268 synchronized (mLock) { | |
| 269 while (mCount != 0) { | |
| 270 mLock.wait(WAIT_TIMEOUT_MS); | |
| 271 } | |
| 272 } | |
| 273 } | |
| 274 } | |
| 275 } | |
| OLD | NEW |