1 package org.robolectric.shadows;
2 
3 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
4 import static android.os.Build.VERSION_CODES.LOLLIPOP;
5 import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
6 import static com.google.common.truth.Truth.assertThat;
7 import static org.junit.Assert.fail;
8 import static org.robolectric.Shadows.shadowOf;
9 
10 import android.accounts.Account;
11 import android.accounts.AccountManager;
12 import android.accounts.AccountManagerCallback;
13 import android.accounts.AccountManagerFuture;
14 import android.accounts.AuthenticatorDescription;
15 import android.accounts.AuthenticatorException;
16 import android.accounts.OnAccountsUpdateListener;
17 import android.accounts.OperationCanceledException;
18 import android.app.Activity;
19 import android.content.Context;
20 import android.content.Intent;
21 import android.os.Bundle;
22 import android.os.Handler;
23 import androidx.test.core.app.ApplicationProvider;
24 import androidx.test.ext.junit.runners.AndroidJUnit4;
25 import java.io.IOException;
26 import org.junit.Before;
27 import org.junit.Test;
28 import org.junit.runner.RunWith;
29 import org.robolectric.Robolectric;
30 import org.robolectric.annotation.Config;
31 import org.robolectric.util.Scheduler;
32 
33 @RunWith(AndroidJUnit4.class)
34 public class ShadowAccountManagerTest {
35   private AccountManager am;
36   private Scheduler scheduler;
37   private Activity activity;
38 
39   @Before
setUp()40   public void setUp() throws Exception {
41     am = AccountManager.get(ApplicationProvider.getApplicationContext());
42     scheduler = Robolectric.getForegroundThreadScheduler();
43     activity = new Activity();
44   }
45 
46   @Test
testGet()47   public void testGet() {
48     assertThat(am).isNotNull();
49     assertThat(am).isSameAs(AccountManager.get(ApplicationProvider.getApplicationContext()));
50 
51     AccountManager activityAM = AccountManager.get(ApplicationProvider.getApplicationContext());
52     assertThat(activityAM).isNotNull();
53     assertThat(activityAM).isSameAs(am);
54   }
55 
56   @Test
testGetAccounts()57   public void testGetAccounts() {
58     assertThat(am.getAccounts()).isNotNull();
59     assertThat(am.getAccounts().length).isEqualTo(0);
60 
61     Account a1 = new Account("name_a", "type_a");
62     shadowOf(am).addAccount(a1);
63     assertThat(am.getAccounts()).isNotNull();
64     assertThat(am.getAccounts().length).isEqualTo(1);
65     assertThat(am.getAccounts()[0]).isSameAs(a1);
66 
67     Account a2 = new Account("name_b", "type_b");
68     shadowOf(am).addAccount(a2);
69     assertThat(am.getAccounts()).isNotNull();
70     assertThat(am.getAccounts().length).isEqualTo(2);
71     assertThat(am.getAccounts()[1]).isSameAs(a2);
72   }
73 
74   @Test
getAccountsByType_nullTypeReturnsAllAccounts()75   public void getAccountsByType_nullTypeReturnsAllAccounts() {
76     shadowOf(am).addAccount(new Account("name_1", "type_1"));
77     shadowOf(am).addAccount(new Account("name_2", "type_2"));
78     shadowOf(am).addAccount(new Account("name_3", "type_3"));
79 
80     assertThat(am.getAccountsByType(null)).asList().containsAllIn(am.getAccounts());
81   }
82 
83   @Test
testGetAccountsByType()84   public void testGetAccountsByType() {
85     assertThat(am.getAccountsByType("name_a")).isNotNull();
86     assertThat(am.getAccounts().length).isEqualTo(0);
87 
88     Account a1 = new Account("name_a", "type_a");
89     shadowOf(am).addAccount(a1);
90     Account[] accounts = am.getAccountsByType("type_a");
91     assertThat(accounts).isNotNull();
92     assertThat(accounts.length).isEqualTo(1);
93     assertThat(accounts[0]).isSameAs(a1);
94 
95     Account a2 = new Account("name_b", "type_b");
96     shadowOf(am).addAccount(a2);
97     accounts = am.getAccountsByType("type_a");
98     assertThat(accounts).isNotNull();
99     assertThat(accounts.length).isEqualTo(1);
100     assertThat(accounts[0]).isSameAs(a1);
101 
102     Account a3 = new Account("name_c", "type_a");
103     shadowOf(am).addAccount(a3);
104     accounts = am.getAccountsByType("type_a");
105     assertThat(accounts).isNotNull();
106     assertThat(accounts.length).isEqualTo(2);
107     assertThat(accounts[0]).isSameAs(a1);
108     assertThat(accounts[1]).isSameAs(a3);
109   }
110 
111   @Test
addAuthToken()112   public void addAuthToken() {
113     Account account = new Account("name", "type");
114     shadowOf(am).addAccount(account);
115 
116     am.setAuthToken(account, "token_type_1", "token1");
117     am.setAuthToken(account, "token_type_2", "token2");
118 
119     assertThat(am.peekAuthToken(account, "token_type_1")).isEqualTo("token1");
120     assertThat(am.peekAuthToken(account, "token_type_2")).isEqualTo("token2");
121   }
122 
123   @Test
setAuthToken_shouldNotAddTokenIfAccountNotPresent()124   public void setAuthToken_shouldNotAddTokenIfAccountNotPresent() {
125     Account account = new Account("name", "type");
126     am.setAuthToken(account, "token_type_1", "token1");
127     assertThat(am.peekAuthToken(account, "token_type_1")).isNull();
128   }
129 
130   @Test
testAddAccountExplicitly_noPasswordNoExtras()131   public void testAddAccountExplicitly_noPasswordNoExtras() {
132     Account account = new Account("name", "type");
133     boolean accountAdded = am.addAccountExplicitly(account, null, null);
134 
135     assertThat(accountAdded).isTrue();
136     assertThat(am.getAccountsByType("type").length).isEqualTo(1);
137     assertThat(am.getAccountsByType("type")[0].name).isEqualTo("name");
138 
139     boolean accountAddedTwice = am.addAccountExplicitly(account, null, null);
140     assertThat(accountAddedTwice).isFalse();
141 
142     account = new Account("another_name", "type");
143     accountAdded = am.addAccountExplicitly(account, null, null);
144     assertThat(accountAdded).isTrue();
145     assertThat(am.getAccountsByType("type").length).isEqualTo(2);
146     assertThat(am.getAccountsByType("type")[0].name).isEqualTo("name");
147     assertThat(am.getAccountsByType("type")[1].name).isEqualTo("another_name");
148     assertThat(am.getPassword(account)).isNull();
149 
150     try {
151       am.addAccountExplicitly(null, null, null);
152       fail("An illegal argument exception should have been thrown when trying to add a null account");
153     } catch (IllegalArgumentException iae) {
154       // NOP
155     }
156   }
157 
158   @Test
testAddAccountExplicitly_withPassword()159   public void testAddAccountExplicitly_withPassword() {
160     Account account = new Account("name", "type");
161     boolean accountAdded = am.addAccountExplicitly(account, "passwd", null);
162 
163     assertThat(accountAdded).isTrue();
164     assertThat(am.getPassword(account)).isEqualTo("passwd");
165   }
166 
167   @Test
testAddAccountExplicitly_withExtras()168   public void testAddAccountExplicitly_withExtras() {
169     Account account = new Account("name", "type");
170     Bundle extras = new Bundle();
171     extras.putString("key123", "value123");
172     boolean accountAdded = am.addAccountExplicitly(account, null, extras);
173 
174     assertThat(accountAdded).isTrue();
175     assertThat(am.getUserData(account, "key123")).isEqualTo("value123");
176     assertThat(am.getUserData(account, "key456")).isNull();
177   }
178 
179   @Test
testAddAccountExplicitly_notifiesListenersIfSuccessful()180   public void testAddAccountExplicitly_notifiesListenersIfSuccessful() {
181     TestOnAccountsUpdateListener listener = new TestOnAccountsUpdateListener();
182     am.addOnAccountsUpdatedListener(listener, null, false);
183     assertThat(listener.getInvocationCount()).isEqualTo(0);
184 
185     Account account = new Account("name", "type");
186     boolean accountAdded = am.addAccountExplicitly(account, "passwd", null);
187 
188     assertThat(accountAdded).isTrue();
189     assertThat(listener.getInvocationCount()).isEqualTo(1);
190   }
191 
192   @Test
testAddAccountExplicitly_doesNotNotifyListenersIfUnsuccessful()193   public void testAddAccountExplicitly_doesNotNotifyListenersIfUnsuccessful() {
194     Account account = new Account("name", "type");
195     boolean accountAdded = am.addAccountExplicitly(account, "passwd", null);
196     assertThat(accountAdded).isTrue();
197 
198     TestOnAccountsUpdateListener listener = new TestOnAccountsUpdateListener();
199     am.addOnAccountsUpdatedListener(listener, null, false);
200     assertThat(listener.getInvocationCount()).isEqualTo(0);
201 
202     // This account is added already, so it'll fail
203     boolean accountAdded2 = am.addAccountExplicitly(account, "passwd", null);
204     assertThat(accountAdded2).isFalse();
205     assertThat(listener.getInvocationCount()).isEqualTo(0);
206   }
207 
208   @Test
testGetSetUserData_addToInitiallyEmptyExtras()209   public void testGetSetUserData_addToInitiallyEmptyExtras() {
210     Account account = new Account("name", "type");
211     boolean accountAdded = am.addAccountExplicitly(account, null, null);
212 
213     assertThat(accountAdded).isTrue();
214 
215     am.setUserData(account, "key123", "value123");
216     assertThat(am.getUserData(account, "key123")).isEqualTo("value123");
217   }
218 
219   @Test
testGetSetUserData_overwrite()220   public void testGetSetUserData_overwrite() {
221     Account account = new Account("name", "type");
222     boolean accountAdded = am.addAccountExplicitly(account, null, null);
223 
224     assertThat(accountAdded).isTrue();
225 
226     am.setUserData(account, "key123", "value123");
227     assertThat(am.getUserData(account, "key123")).isEqualTo("value123");
228 
229     am.setUserData(account, "key123", "value456");
230     assertThat(am.getUserData(account, "key123")).isEqualTo("value456");
231   }
232 
233   @Test
testGetSetUserData_remove()234   public void testGetSetUserData_remove() {
235     Account account = new Account("name", "type");
236     boolean accountAdded = am.addAccountExplicitly(account, null, null);
237 
238     assertThat(accountAdded).isTrue();
239 
240     am.setUserData(account, "key123", "value123");
241     assertThat(am.getUserData(account, "key123")).isEqualTo("value123");
242 
243     am.setUserData(account, "key123", null);
244     assertThat(am.getUserData(account, "key123")).isNull();
245   }
246 
247   @Test
testGetSetPassword_setInAccountInitiallyWithNoPassword()248   public void testGetSetPassword_setInAccountInitiallyWithNoPassword() {
249     Account account = new Account("name", "type");
250     boolean accountAdded = am.addAccountExplicitly(account, null, null);
251 
252     assertThat(accountAdded).isTrue();
253     assertThat(am.getPassword(account)).isNull();
254 
255     am.setPassword(account, "passwd");
256     assertThat(am.getPassword(account)).isEqualTo("passwd");
257   }
258 
259   @Test
testGetSetPassword_overwrite()260   public void testGetSetPassword_overwrite() {
261     Account account = new Account("name", "type");
262     boolean accountAdded = am.addAccountExplicitly(account, "passwd1", null);
263 
264     assertThat(accountAdded).isTrue();
265     assertThat(am.getPassword(account)).isEqualTo("passwd1");
266 
267     am.setPassword(account, "passwd2");
268     assertThat(am.getPassword(account)).isEqualTo("passwd2");
269   }
270 
271   @Test
testGetSetPassword_remove()272   public void testGetSetPassword_remove() {
273     Account account = new Account("name", "type");
274     boolean accountAdded = am.addAccountExplicitly(account, "passwd1", null);
275 
276     assertThat(accountAdded).isTrue();
277     assertThat(am.getPassword(account)).isEqualTo("passwd1");
278 
279     am.setPassword(account, null);
280     assertThat(am.getPassword(account)).isNull();
281   }
282 
283   @Test
testBlockingGetAuthToken()284   public void testBlockingGetAuthToken() throws AuthenticatorException, OperationCanceledException, IOException {
285     Account account = new Account("name", "type");
286     shadowOf(am).addAccount(account);
287 
288     am.setAuthToken(account, "token_type_1", "token1");
289     am.setAuthToken(account, "token_type_2", "token2");
290 
291     assertThat(am.blockingGetAuthToken(account, "token_type_1", false)).isEqualTo("token1");
292     assertThat(am.blockingGetAuthToken(account, "token_type_2", false)).isEqualTo("token2");
293 
294     try {
295       am.blockingGetAuthToken(null, "token_type_1", false);
296       fail("blockingGetAuthToken() should throw an illegal argument exception if the account is null");
297     } catch (IllegalArgumentException iae) {
298       // Expected
299     }
300     try {
301       am.blockingGetAuthToken(account, null, false);
302       fail("blockingGetAuthToken() should throw an illegal argument exception if the auth token type is null");
303     } catch (IllegalArgumentException iae) {
304       // Expected
305     }
306 
307     Account account1 = new Account("unknown", "type");
308     assertThat(am.blockingGetAuthToken(account1, "token_type_1", false)).isNull();
309   }
310 
311   @Test
removeAccount_throwsIllegalArgumentException_whenPassedNullAccount()312   public void removeAccount_throwsIllegalArgumentException_whenPassedNullAccount() {
313     Account account = new Account("name", "type");
314     shadowOf(am).addAccount(account);
315 
316     try {
317       am.removeAccount(null, null, null);
318       fail("removeAccount() should throw an illegal argument exception if the account is null");
319     } catch (IllegalArgumentException iae) {
320       // Expected
321     }
322   }
323 
324   @Test
removeAccount_doesNotRemoveAccountOfDifferentName()325   public void removeAccount_doesNotRemoveAccountOfDifferentName() throws Exception {
326     Account account = new Account("name", "type");
327     shadowOf(am).addAccount(account);
328 
329     Account wrongAccount = new Account("wrong_name", "type");
330     AccountManagerFuture<Boolean> future = am.removeAccount(wrongAccount, null, null);
331     assertThat(future.getResult()).isFalse();
332     assertThat(am.getAccountsByType("type")).isNotEmpty();
333   }
334 
335   @Test
removeAccount_does()336   public void removeAccount_does() throws Exception {
337     Account account = new Account("name", "type");
338     shadowOf(am).addAccount(account);
339 
340     TestAccountManagerCallback<Boolean> testAccountManagerCallback = new TestAccountManagerCallback<>();
341     AccountManagerFuture<Boolean> future = am.removeAccount(account, testAccountManagerCallback, null);
342     assertThat(future.getResult()).isTrue();
343     assertThat(am.getAccountsByType("type")).isEmpty();
344 
345     assertThat(testAccountManagerCallback.accountManagerFuture).isNotNull();
346   }
347 
348   @Test
removeAccount_notifiesListenersIfSuccessful()349   public void removeAccount_notifiesListenersIfSuccessful() {
350     Account account = new Account("name", "type");
351     am.addAccountExplicitly(account, "passwd", null);
352 
353     TestOnAccountsUpdateListener listener = new TestOnAccountsUpdateListener();
354     am.addOnAccountsUpdatedListener(listener, null, false);
355     assertThat(listener.getInvocationCount()).isEqualTo(0);
356 
357     am.removeAccount(account, null, null);
358 
359     assertThat(listener.getInvocationCount()).isEqualTo(1);
360   }
361 
362   @Test
removeAccount_doesNotNotifyIfUnuccessful()363   public void removeAccount_doesNotNotifyIfUnuccessful() {
364     Account account = new Account("name", "type");
365 
366     TestOnAccountsUpdateListener listener = new TestOnAccountsUpdateListener();
367     am.addOnAccountsUpdatedListener(listener, null, false);
368     assertThat(listener.getInvocationCount()).isEqualTo(0);
369 
370     // The account has not been added
371     am.removeAccount(account, null, null);
372 
373     assertThat(listener.getInvocationCount()).isEqualTo(0);
374   }
375 
376   private static class TestOnAccountsUpdateListener implements OnAccountsUpdateListener {
377     private int invocationCount = 0;
378 
379     @Override
onAccountsUpdated(Account[] accounts)380     public void onAccountsUpdated(Account[] accounts) {
381       invocationCount++;
382     }
383 
getInvocationCount()384     public int getInvocationCount() {
385       return invocationCount;
386     }
387   }
388 
389   @Test
testAccountsUpdateListener()390   public void testAccountsUpdateListener() {
391     TestOnAccountsUpdateListener listener = new TestOnAccountsUpdateListener();
392     am.addOnAccountsUpdatedListener(listener, null, false);
393     assertThat(listener.getInvocationCount()).isEqualTo(0);
394 
395     Account account = new Account("name", "type");
396     shadowOf(am).addAccount(account);
397     assertThat(listener.getInvocationCount()).isEqualTo(1);
398   }
399 
400   @Test
testAccountsUpdateListener_duplicate()401   public void testAccountsUpdateListener_duplicate() {
402     TestOnAccountsUpdateListener listener = new TestOnAccountsUpdateListener();
403     am.addOnAccountsUpdatedListener(listener, null, false);
404     am.addOnAccountsUpdatedListener(listener, null, false);
405     assertThat(listener.getInvocationCount()).isEqualTo(0);
406 
407     Account account = new Account("name", "type");
408     shadowOf(am).addAccount(account);
409     assertThat(listener.getInvocationCount()).isEqualTo(1);
410   }
411 
412   @Test
testAccountsUpdateListener_updateImmediately()413   public void testAccountsUpdateListener_updateImmediately() {
414     TestOnAccountsUpdateListener listener = new TestOnAccountsUpdateListener();
415     am.addOnAccountsUpdatedListener(listener, null, true);
416     assertThat(listener.getInvocationCount()).isEqualTo(1);
417   }
418 
419   @Test
testAccountsUpdateListener_listenerNotInvokedAfterRemoval()420   public void testAccountsUpdateListener_listenerNotInvokedAfterRemoval() {
421     TestOnAccountsUpdateListener listener = new TestOnAccountsUpdateListener();
422     am.addOnAccountsUpdatedListener(listener, null, false);
423     assertThat(listener.getInvocationCount()).isEqualTo(0);
424 
425     Account account = new Account("name", "type");
426     shadowOf(am).addAccount(account);
427 
428     assertThat(listener.getInvocationCount()).isEqualTo(1);
429 
430     am.removeOnAccountsUpdatedListener(listener);
431 
432     shadowOf(am).addAccount(account);
433 
434     assertThat(listener.getInvocationCount()).isEqualTo(1);
435   }
436 
437   @Test
testAddAuthenticator()438   public void testAddAuthenticator() {
439     shadowOf(am).addAuthenticator("type");
440     AuthenticatorDescription[] result = am.getAuthenticatorTypes();
441     assertThat(result.length).isEqualTo(1);
442     assertThat(result[0].type).isEqualTo("type");
443   }
444 
445   @Test
invalidateAuthToken_noAccount()446   public void invalidateAuthToken_noAccount() {
447     am.invalidateAuthToken("type1", "token1");
448   }
449 
450   @Test
invalidateAuthToken_noToken()451   public void invalidateAuthToken_noToken() {
452     Account account1 = new Account("name", "type1");
453     shadowOf(am).addAccount(account1);
454     am.invalidateAuthToken("type1", "token1");
455   }
456 
457   @Test
invalidateAuthToken_multipleAccounts()458   public void invalidateAuthToken_multipleAccounts() {
459     Account account1 = new Account("name", "type1");
460     shadowOf(am).addAccount(account1);
461 
462     Account account2 = new Account("name", "type2");
463     shadowOf(am).addAccount(account2);
464 
465     am.setAuthToken(account1, "token_type_1", "token1");
466     am.setAuthToken(account2, "token_type_1", "token1");
467 
468     assertThat(am.peekAuthToken(account1, "token_type_1")).isEqualTo("token1");
469     assertThat(am.peekAuthToken(account2, "token_type_1")).isEqualTo("token1");
470 
471     // invalidate token for type1 account
472     am.invalidateAuthToken("type1", "token1");
473     assertThat(am.peekAuthToken(account1, "token_type_1")).isNull();
474     assertThat(am.peekAuthToken(account2, "token_type_1")).isEqualTo("token1");
475 
476     // invalidate token for type2 account
477     am.invalidateAuthToken("type2", "token1");
478     assertThat(am.peekAuthToken(account1, "token_type_1")).isNull();
479     assertThat(am.peekAuthToken(account2, "token_type_1")).isNull();
480   }
481 
482   @Test
invalidateAuthToken_multipleTokens()483   public void invalidateAuthToken_multipleTokens() {
484     Account account = new Account("name", "type1");
485     shadowOf(am).addAccount(account);
486 
487     am.setAuthToken(account, "token_type_1", "token1");
488     am.setAuthToken(account, "token_type_2", "token2");
489 
490     assertThat(am.peekAuthToken(account, "token_type_1")).isEqualTo("token1");
491     assertThat(am.peekAuthToken(account, "token_type_2")).isEqualTo("token2");
492 
493     // invalidate token1
494     am.invalidateAuthToken("type1", "token1");
495     assertThat(am.peekAuthToken(account, "token_type_1")).isNull();
496     assertThat(am.peekAuthToken(account, "token_type_2")).isEqualTo("token2");
497 
498     // invalidate token2
499     am.invalidateAuthToken("type1", "token2");
500     assertThat(am.peekAuthToken(account, "token_type_1")).isNull();
501     assertThat(am.peekAuthToken(account, "token_type_2")).isNull();
502   }
503 
504   @Test
invalidateAuthToken_multipleTokenTypesSameToken()505   public void invalidateAuthToken_multipleTokenTypesSameToken() {
506     Account account = new Account("name", "type1");
507     shadowOf(am).addAccount(account);
508 
509     am.setAuthToken(account, "token_type_1", "token1");
510     am.setAuthToken(account, "token_type_2", "token1");
511 
512     assertThat(am.peekAuthToken(account, "token_type_1")).isEqualTo("token1");
513     assertThat(am.peekAuthToken(account, "token_type_2")).isEqualTo("token1");
514 
515     // invalidate token1
516     am.invalidateAuthToken("type1", "token1");
517     assertThat(am.peekAuthToken(account, "token_type_1")).isNull();
518     assertThat(am.peekAuthToken(account, "token_type_2")).isNull();
519   }
520 
521   @Test
addAccount_noActivitySpecified()522   public void addAccount_noActivitySpecified() throws Exception {
523     shadowOf(am).addAuthenticator("google.com");
524 
525     AccountManagerFuture<Bundle> result = am.addAccount("google.com", "auth_token_type", null, null, null, null, null);
526 
527     Bundle resultBundle = result.getResult();
528 
529     assertThat((Intent) resultBundle.getParcelable(AccountManager.KEY_INTENT)).isNotNull();
530   }
531 
532   @Test
addAccount_activitySpecified()533   public void addAccount_activitySpecified() throws Exception {
534     shadowOf(am).addAuthenticator("google.com");
535 
536     AccountManagerFuture<Bundle> result = am.addAccount("google.com", "auth_token_type", null, null, activity, null, null);
537     Bundle resultBundle = result.getResult();
538 
539     assertThat(resultBundle.getString(AccountManager.KEY_ACCOUNT_TYPE)).isEqualTo("google.com");
540     assertThat(resultBundle.getString(AccountManager.KEY_ACCOUNT_NAME)).isEqualTo("some_user@gmail.com");
541   }
542 
543   @Test
addAccount_shouldCallCallback()544   public void addAccount_shouldCallCallback() throws Exception {
545     shadowOf(am).addAuthenticator("google.com");
546 
547     TestAccountManagerCallback<Bundle> callback = new TestAccountManagerCallback<>();
548     AccountManagerFuture<Bundle> result = am.addAccount("google.com", "auth_token_type", null, null, activity, callback, new Handler());
549 
550     assertThat(callback.hasBeenCalled()).isFalse();
551     assertThat(result.isDone()).isFalse();
552 
553     shadowOf(am).addAccount(new Account("thebomb@google.com", "google.com"));
554     assertThat(result.isDone()).isTrue();
555     assertThat(callback.accountManagerFuture).isNotNull();
556 
557     Bundle resultBundle = callback.getResult();
558     assertThat(resultBundle.getString(AccountManager.KEY_ACCOUNT_TYPE)).isEqualTo("google.com");
559     assertThat(resultBundle.getString(AccountManager.KEY_ACCOUNT_NAME)).isEqualTo("thebomb@google.com");
560   }
561 
562   @Test
addAccount_whenSchedulerPaused_shouldCallCallbackAfterSchedulerUnpaused()563   public void addAccount_whenSchedulerPaused_shouldCallCallbackAfterSchedulerUnpaused() throws Exception {
564     scheduler.pause();
565     shadowOf(am).addAuthenticator("google.com");
566 
567     TestAccountManagerCallback<Bundle> callback = new TestAccountManagerCallback<>();
568     am.addAccount("google.com", "auth_token_type", null, null, activity, callback, new Handler());
569     assertThat(callback.hasBeenCalled()).isFalse();
570 
571     shadowOf(am).addAccount(new Account("thebomb@google.com", "google.com"));
572 
573     scheduler.unPause();
574     assertThat(callback.hasBeenCalled()).isTrue();
575 
576     Bundle resultBundle = callback.getResult();
577     assertThat(resultBundle.getString(AccountManager.KEY_ACCOUNT_TYPE)).isEqualTo("google.com");
578     assertThat(resultBundle.getString(AccountManager.KEY_ACCOUNT_NAME)).isEqualTo("thebomb@google.com");
579   }
580 
581   @Test
addAccount_noAuthenticatorDefined()582   public void addAccount_noAuthenticatorDefined() throws Exception {
583     AccountManagerFuture<Bundle> future = am.addAccount("unknown_account_type", "auth_token_type", null, null, activity, null, null);
584     try {
585       future.getResult();
586       fail("addAccount() should throw an authenticator exception if no authenticator was registered for this account type");
587     } catch(AuthenticatorException e) {
588       // Expected
589     }
590     assertThat(future.isDone()).isTrue();
591   }
592 
593   @Test
addAccount_withOptionsShouldSupportGetNextAddAccountOptions()594   public void addAccount_withOptionsShouldSupportGetNextAddAccountOptions() throws Exception {
595     assertThat(shadowOf(am).getNextAddAccountOptions()).isNull();
596 
597     shadowOf(am).addAuthenticator("google.com");
598 
599     Bundle expectedAddAccountOptions = new Bundle();
600     expectedAddAccountOptions.putString("option", "value");
601 
602     am.addAccount("google.com", "auth_token_type", null, expectedAddAccountOptions, activity, null, null);
603 
604     Bundle actualAddAccountOptions = shadowOf(am).getNextAddAccountOptions();
605     assertThat(shadowOf(am).getNextAddAccountOptions()).isNull();
606     assertThat(actualAddAccountOptions).isEqualTo(expectedAddAccountOptions);
607   }
608 
609   @Test
addAccount_withOptionsShouldSupportPeekNextAddAccountOptions()610   public void addAccount_withOptionsShouldSupportPeekNextAddAccountOptions() throws Exception {
611     assertThat(shadowOf(am).peekNextAddAccountOptions()).isNull();
612 
613     shadowOf(am).addAuthenticator("google.com");
614 
615     Bundle expectedAddAccountOptions = new Bundle();
616     expectedAddAccountOptions.putString("option", "value");
617     am.addAccount("google.com", "auth_token_type", null, expectedAddAccountOptions, activity, null, null);
618 
619     Bundle actualAddAccountOptions = shadowOf(am).peekNextAddAccountOptions();
620     assertThat(shadowOf(am).peekNextAddAccountOptions()).isNotNull();
621     assertThat(actualAddAccountOptions).isEqualTo(expectedAddAccountOptions);
622   }
623 
624   @Test
addAccount_withNoAuthenticatorForType_throwsExceptionInGetResult()625   public void addAccount_withNoAuthenticatorForType_throwsExceptionInGetResult() throws Exception {
626     assertThat(shadowOf(am).peekNextAddAccountOptions()).isNull();
627 
628     AccountManagerFuture<Bundle> futureResult = am.addAccount("google.com", "auth_token_type", null, null, activity, null, null);
629     try {
630       futureResult.getResult();
631       fail("should have thrown");
632     } catch (AuthenticatorException expected) { }
633   }
634 
635   @Test
636   @Config(minSdk = LOLLIPOP)
addPreviousAccount()637   public void addPreviousAccount() {
638     Account account = new Account("name_a", "type_a");
639     shadowOf(am).setPreviousAccountName(account, "old_name");
640     assertThat(am.getPreviousName(account)).isEqualTo("old_name");
641   }
642 
643   @Test
testGetAsSystemService()644   public void testGetAsSystemService() throws Exception {
645     AccountManager systemService =
646         (AccountManager)
647             ApplicationProvider.getApplicationContext().getSystemService(Context.ACCOUNT_SERVICE);
648     assertThat(systemService).isNotNull();
649     assertThat(am).isEqualTo(systemService);
650   }
651 
652   @Test
getAuthToken_withActivity_returnsCorrectToken()653   public void getAuthToken_withActivity_returnsCorrectToken() throws Exception {
654     Account account = new Account("name", "google.com");
655     shadowOf(am).addAccount(account);
656     shadowOf(am).addAuthenticator("google.com");
657 
658     am.setAuthToken(account, "auth_token_type", "token1");
659 
660     TestAccountManagerCallback<Bundle> callback = new TestAccountManagerCallback<>();
661     AccountManagerFuture<Bundle> future = am.getAuthToken(account,
662         "auth_token_type",
663         new Bundle(),
664         activity,
665         callback,
666         new Handler());
667 
668     assertThat(future.isDone()).isTrue();
669     assertThat(future.getResult().getString(AccountManager.KEY_ACCOUNT_NAME)).isEqualTo(account.name);
670     assertThat(future.getResult().getString(AccountManager.KEY_ACCOUNT_TYPE)).isEqualTo(account.type);
671     assertThat(future.getResult().getString(AccountManager.KEY_AUTHTOKEN)).isEqualTo("token1");
672 
673     assertThat(callback.hasBeenCalled()).isTrue();
674   }
675 
676   @Test
getAuthToken_withActivity_returnsAuthIntent()677   public void getAuthToken_withActivity_returnsAuthIntent() throws Exception {
678     Account account = new Account("name", "google.com");
679     shadowOf(am).addAccount(account);
680     shadowOf(am).addAuthenticator("google.com");
681 
682     TestAccountManagerCallback<Bundle> callback = new TestAccountManagerCallback<>();
683     AccountManagerFuture<Bundle> future = am.getAuthToken(account,
684         "auth_token_type",
685         new Bundle(),
686         activity,
687         callback,
688         new Handler());
689 
690     assertThat(future.isDone()).isTrue();
691     assertThat(future.getResult().getString(AccountManager.KEY_ACCOUNT_NAME))
692         .isEqualTo(account.name);
693     assertThat(future.getResult().getString(AccountManager.KEY_ACCOUNT_TYPE))
694         .isEqualTo(account.type);
695     assertThat(future.getResult().getString(AccountManager.KEY_AUTHTOKEN)).isNull();
696     assertThat((Intent) future.getResult().getParcelable(AccountManager.KEY_INTENT)).isNotNull();
697 
698     assertThat(callback.hasBeenCalled()).isTrue();
699   }
700 
701   @Test
getAuthToken_withNotifyAuthFailureSetToFalse_returnsCorrectToken()702   public void getAuthToken_withNotifyAuthFailureSetToFalse_returnsCorrectToken() throws Exception {
703     Account account = new Account("name", "google.com");
704     shadowOf(am).addAccount(account);
705     shadowOf(am).addAuthenticator("google.com");
706 
707     am.setAuthToken(account, "auth_token_type", "token1");
708 
709     TestAccountManagerCallback<Bundle> callback = new TestAccountManagerCallback<>();
710     AccountManagerFuture<Bundle> future =
711         am.getAuthToken(
712             account,
713             "auth_token_type",
714             new Bundle(),
715             /* notifyAuthFailure= */ false,
716             callback,
717             new Handler());
718 
719     assertThat(future.isDone()).isTrue();
720     assertThat(future.getResult().getString(AccountManager.KEY_ACCOUNT_NAME))
721         .isEqualTo(account.name);
722     assertThat(future.getResult().getString(AccountManager.KEY_ACCOUNT_TYPE))
723         .isEqualTo(account.type);
724     assertThat(future.getResult().getString(AccountManager.KEY_AUTHTOKEN)).isEqualTo("token1");
725 
726     assertThat(callback.hasBeenCalled()).isTrue();
727   }
728 
729   @Test
getAuthToken_withNotifyAuthFailureSetToFalse_returnsAuthIntent()730   public void getAuthToken_withNotifyAuthFailureSetToFalse_returnsAuthIntent() throws Exception {
731     Account account = new Account("name", "google.com");
732     shadowOf(am).addAccount(account);
733     shadowOf(am).addAuthenticator("google.com");
734 
735     TestAccountManagerCallback<Bundle> callback = new TestAccountManagerCallback<>();
736     AccountManagerFuture<Bundle> future =
737         am.getAuthToken(
738             account,
739             "auth_token_type",
740             new Bundle(),
741             /* notifyAuthFailure= */ false,
742             callback,
743             new Handler());
744 
745     assertThat(future.isDone()).isTrue();
746     assertThat(future.getResult().getString(AccountManager.KEY_ACCOUNT_NAME))
747         .isEqualTo(account.name);
748     assertThat(future.getResult().getString(AccountManager.KEY_ACCOUNT_TYPE))
749         .isEqualTo(account.type);
750     assertThat(future.getResult().getString(AccountManager.KEY_AUTHTOKEN)).isNull();
751     assertThat((Intent) future.getResult().getParcelable(AccountManager.KEY_INTENT)).isNotNull();
752 
753     assertThat(callback.hasBeenCalled()).isTrue();
754   }
755 
756   @Test
getHasFeatures_returnsTrueWhenAllFeaturesSatisfied()757   public void getHasFeatures_returnsTrueWhenAllFeaturesSatisfied() throws Exception {
758     Account account = new Account("name", "google.com");
759     shadowOf(am).addAccount(account);
760     shadowOf(am).setFeatures(account, new String[] { "FEATURE_1", "FEATURE_2" });
761 
762     TestAccountManagerCallback<Boolean> callback = new TestAccountManagerCallback<>();
763     AccountManagerFuture<Boolean> future = am.hasFeatures(account, new String[] { "FEATURE_1", "FEATURE_2" }, callback, new Handler());
764 
765     assertThat(future.isDone()).isTrue();
766     assertThat(future.getResult().booleanValue()).isEqualTo(true);
767 
768     assertThat(callback.hasBeenCalled()).isTrue();
769   }
770 
771   @Test
getHasFeatures_returnsFalseWhenAllFeaturesNotSatisfied()772   public void getHasFeatures_returnsFalseWhenAllFeaturesNotSatisfied() throws Exception {
773     Account account = new Account("name", "google.com");
774     shadowOf(am).addAccount(account);
775     shadowOf(am).setFeatures(account, new String[] { "FEATURE_1" });
776 
777     TestAccountManagerCallback<Boolean> callback = new TestAccountManagerCallback<>();
778     AccountManagerFuture<Boolean> future = am.hasFeatures(account, new String[] { "FEATURE_1", "FEATURE_2" }, callback, new Handler());
779 
780     assertThat(future.isDone()).isTrue();
781     assertThat(future.getResult().booleanValue()).isEqualTo(false);
782     assertThat(callback.hasBeenCalled()).isTrue();
783   }
784 
785   @Test
getAccountsByTypeAndFeatures()786   public void getAccountsByTypeAndFeatures() throws Exception {
787 
788     Account accountWithCorrectTypeAndFeatures = new Account("account_1", "google.com");
789     shadowOf(am).addAccount(accountWithCorrectTypeAndFeatures);
790     shadowOf(am).setFeatures(accountWithCorrectTypeAndFeatures, new String[] { "FEATURE_1", "FEATURE_2" });
791 
792     Account accountWithCorrectTypeButNotFeatures = new Account("account_2", "google.com");
793     shadowOf(am).addAccount(accountWithCorrectTypeButNotFeatures);
794     shadowOf(am).setFeatures(accountWithCorrectTypeButNotFeatures, new String[] { "FEATURE_1" });
795 
796     Account accountWithCorrectFeaturesButNotType = new Account("account_3", "facebook.com");
797     shadowOf(am).addAccount(accountWithCorrectFeaturesButNotType);
798     shadowOf(am).setFeatures(accountWithCorrectFeaturesButNotType, new String[] { "FEATURE_1", "FEATURE_2" });
799 
800 
801     TestAccountManagerCallback<Account[]> callback = new TestAccountManagerCallback<>();
802 
803     AccountManagerFuture<Account[]> future = am.getAccountsByTypeAndFeatures("google.com", new String[] { "FEATURE_1", "FEATURE_2" }, callback, new Handler());
804 
805     assertThat(future.isDone()).isTrue();
806     assertThat(future.getResult()).asList().containsExactly(accountWithCorrectTypeAndFeatures);
807 
808     assertThat(callback.hasBeenCalled()).isTrue();
809   }
810 
811   @Test
getAccountsByTypeAndFeatures_returnsAllAccountsForNullFeature()812   public void getAccountsByTypeAndFeatures_returnsAllAccountsForNullFeature() throws Exception {
813 
814     Account accountWithCorrectTypeAndFeatures = new Account("account_1", "google.com");
815     shadowOf(am).addAccount(accountWithCorrectTypeAndFeatures);
816     shadowOf(am).setFeatures(
817         accountWithCorrectTypeAndFeatures, new String[] { "FEATURE_1", "FEATURE_2" });
818 
819     Account accountWithCorrectTypeButNotFeatures = new Account("account_2", "google.com");
820     shadowOf(am).addAccount(accountWithCorrectTypeButNotFeatures);
821     shadowOf(am).setFeatures(accountWithCorrectTypeButNotFeatures, new String[] { "FEATURE_1" });
822 
823     Account accountWithCorrectFeaturesButNotType = new Account("account_3", "facebook.com");
824     shadowOf(am).addAccount(accountWithCorrectFeaturesButNotType);
825     shadowOf(am).setFeatures(
826         accountWithCorrectFeaturesButNotType, new String[] { "FEATURE_1", "FEATURE_2" });
827 
828 
829     TestAccountManagerCallback<Account[]> callback = new TestAccountManagerCallback<>();
830 
831     AccountManagerFuture<Account[]> future =
832         am.getAccountsByTypeAndFeatures("google.com", null, callback, new Handler());
833 
834     assertThat(future.isDone()).isTrue();
835     assertThat(future.getResult()).asList()
836         .containsExactly(accountWithCorrectTypeAndFeatures, accountWithCorrectTypeButNotFeatures);
837 
838     assertThat(callback.hasBeenCalled()).isTrue();
839   }
840 
841   @Test
842   @Config(minSdk = JELLY_BEAN_MR2)
getAccountsByTypeForPackage()843   public void getAccountsByTypeForPackage() {
844     Account[] accountsByTypeForPackage = am.getAccountsByTypeForPackage(null, "org.somepackage");
845 
846     assertThat(accountsByTypeForPackage).isEmpty();
847 
848     Account accountVisibleToPackage = new Account("user@gmail.com", "gmail.com");
849     shadowOf(am).addAccount(accountVisibleToPackage, "org.somepackage");
850 
851     accountsByTypeForPackage = am.getAccountsByTypeForPackage("other_type", "org.somepackage");
852     assertThat(accountsByTypeForPackage).isEmpty();
853 
854     accountsByTypeForPackage = am.getAccountsByTypeForPackage("gmail.com", "org.somepackage");
855     assertThat(accountsByTypeForPackage).asList().containsExactly(accountVisibleToPackage);
856 
857     accountsByTypeForPackage = am.getAccountsByTypeForPackage(null, "org.somepackage");
858     assertThat(accountsByTypeForPackage).asList().containsExactly(accountVisibleToPackage);
859   }
860 
861   @Test
862   @Config(minSdk = LOLLIPOP_MR1)
removeAccountExplicitly()863   public void removeAccountExplicitly() {
864     assertThat(am.removeAccountExplicitly(new Account("non_existant_account@gmail.com", "gmail.com"))).isFalse();
865     assertThat(am.removeAccountExplicitly(null)).isFalse();
866 
867     Account account = new Account("name@gmail.com", "gmail.com");
868     shadowOf(am).addAccount(account);
869 
870     assertThat(am.removeAccountExplicitly(account)).isTrue();
871   }
872 
873   @Test
removeAllAccounts()874   public void removeAllAccounts() throws Exception {
875 
876     Account account = new Account("name@gmail.com", "gmail.com");
877     shadowOf(am).addAccount(account);
878 
879     assertThat(am.getAccounts()).isNotEmpty();
880 
881     shadowOf(am).removeAllAccounts();
882 
883     assertThat(am.getAccounts()).isEmpty();
884   }
885 
886   @Test
testSetAuthenticationErrorOnNextResponse()887   public void testSetAuthenticationErrorOnNextResponse()
888       throws AuthenticatorException, IOException, OperationCanceledException {
889 
890     shadowOf(am).setAuthenticationErrorOnNextResponse(true);
891 
892     try {
893       am.getAccountsByTypeAndFeatures(null, null, null, null).getResult();
894       fail("should have thrown");
895     } catch (AuthenticatorException expected) {
896       // Expected
897     }
898 
899     am.getAccountsByTypeAndFeatures(null, null, null, null).getResult();
900   }
901 
902   private static class TestAccountManagerCallback<T> implements AccountManagerCallback<T> {
903     private AccountManagerFuture<T> accountManagerFuture;
904 
905     @Override
run(AccountManagerFuture<T> accountManagerFuture)906     public void run(AccountManagerFuture<T> accountManagerFuture) {
907       this.accountManagerFuture = accountManagerFuture;
908     }
909 
hasBeenCalled()910     boolean hasBeenCalled() {
911       return accountManagerFuture != null;
912     }
913 
getResult()914     T getResult() throws Exception {
915       return accountManagerFuture.getResult();
916     }
917   }
918 }
919