OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.net; | 5 package org.chromium.net; |
6 | 6 |
7 import static org.hamcrest.CoreMatchers.equalTo; | 7 import static org.hamcrest.CoreMatchers.equalTo; |
8 import static org.hamcrest.CoreMatchers.notNullValue; | 8 import static org.hamcrest.CoreMatchers.notNullValue; |
9 import static org.hamcrest.CoreMatchers.nullValue; | 9 import static org.hamcrest.CoreMatchers.nullValue; |
10 import static org.junit.Assert.assertThat; | 10 import static org.junit.Assert.assertThat; |
11 import static org.junit.Assert.fail; | 11 import static org.junit.Assert.fail; |
| 12 import static org.mockito.Matchers.any; |
12 import static org.mockito.Matchers.anyInt; | 13 import static org.mockito.Matchers.anyInt; |
13 import static org.mockito.Matchers.anyLong; | 14 import static org.mockito.Matchers.anyLong; |
14 import static org.mockito.Matchers.anyString; | 15 import static org.mockito.Matchers.anyString; |
15 import static org.mockito.Matchers.eq; | 16 import static org.mockito.Matchers.eq; |
| 17 import static org.mockito.Matchers.isNull; |
16 import static org.mockito.Mockito.doNothing; | 18 import static org.mockito.Mockito.doNothing; |
| 19 import static org.mockito.Mockito.doReturn; |
17 import static org.mockito.Mockito.mock; | 20 import static org.mockito.Mockito.mock; |
18 import static org.mockito.Mockito.spy; | 21 import static org.mockito.Mockito.spy; |
| 22 import static org.mockito.Mockito.times; |
19 import static org.mockito.Mockito.verify; | 23 import static org.mockito.Mockito.verify; |
| 24 import static org.mockito.Mockito.verifyZeroInteractions; |
20 import static org.mockito.Mockito.when; | 25 import static org.mockito.Mockito.when; |
21 | 26 |
| 27 import android.accounts.Account; |
22 import android.accounts.AccountManager; | 28 import android.accounts.AccountManager; |
23 import android.accounts.AccountManagerCallback; | 29 import android.accounts.AccountManagerCallback; |
24 import android.accounts.AccountManagerFuture; | 30 import android.accounts.AccountManagerFuture; |
25 import android.accounts.AuthenticatorException; | 31 import android.accounts.AuthenticatorException; |
26 import android.accounts.OperationCanceledException; | 32 import android.accounts.OperationCanceledException; |
27 import android.app.Activity; | 33 import android.app.Activity; |
| 34 import android.app.Application; |
| 35 import android.content.BroadcastReceiver; |
| 36 import android.content.Context; |
| 37 import android.content.Intent; |
28 import android.os.Bundle; | 38 import android.os.Bundle; |
29 import android.os.Handler; | 39 import android.os.Handler; |
30 | 40 |
| 41 import junit.framework.Assert; |
| 42 |
| 43 import org.chromium.base.ApplicationStatus; |
31 import org.chromium.base.BaseChromiumApplication; | 44 import org.chromium.base.BaseChromiumApplication; |
| 45 import org.chromium.base.ContextUtils; |
32 import org.chromium.base.test.shadows.ShadowMultiDex; | 46 import org.chromium.base.test.shadows.ShadowMultiDex; |
| 47 import org.chromium.net.HttpNegotiateAuthenticator.GetAccountsCallback; |
| 48 import org.chromium.net.HttpNegotiateAuthenticator.RequestData; |
33 import org.chromium.testing.local.LocalRobolectricTestRunner; | 49 import org.chromium.testing.local.LocalRobolectricTestRunner; |
| 50 import org.junit.After; |
34 import org.junit.Before; | 51 import org.junit.Before; |
35 import org.junit.Test; | 52 import org.junit.Test; |
36 import org.junit.runner.RunWith; | 53 import org.junit.runner.RunWith; |
| 54 import org.mockito.ArgumentCaptor; |
| 55 import org.mockito.Captor; |
| 56 import org.mockito.Mock; |
| 57 import org.mockito.MockitoAnnotations; |
37 import org.robolectric.Robolectric; | 58 import org.robolectric.Robolectric; |
38 import org.robolectric.annotation.Config; | 59 import org.robolectric.annotation.Config; |
39 import org.robolectric.annotation.Implementation; | 60 import org.robolectric.annotation.Implementation; |
40 import org.robolectric.annotation.Implements; | 61 import org.robolectric.annotation.Implements; |
41 import org.robolectric.shadows.ShadowAccountManager; | 62 import org.robolectric.shadows.ShadowAccountManager; |
| 63 import org.robolectric.shadows.ShadowApplication; |
42 | 64 |
43 import java.io.IOException; | 65 import java.io.IOException; |
| 66 import java.util.List; |
44 | 67 |
45 /** | 68 /** |
46 * Robolectric tests for HttpNegotiateAuthenticator | 69 * Robolectric tests for HttpNegotiateAuthenticator |
47 */ | 70 */ |
48 @RunWith(LocalRobolectricTestRunner.class) | 71 @RunWith(LocalRobolectricTestRunner.class) |
49 @Config(manifest = Config.NONE, application = BaseChromiumApplication.class, | 72 @Config(manifest = Config.NONE, application = BaseChromiumApplication.class, |
50 shadows = {HttpNegotiateAuthenticatorTest.ExtendedShadowAccountManager.c
lass, | 73 shadows = {HttpNegotiateAuthenticatorTest.ExtendedShadowAccountManager.c
lass, |
51 ShadowMultiDex.class}) | 74 ShadowMultiDex.class}) |
52 public class HttpNegotiateAuthenticatorTest { | 75 public class HttpNegotiateAuthenticatorTest { |
53 private static class GetAuthTokenByFeaturesInvocation { | |
54 // Since the account manager is an SDK singleton (it is fetched using Ac
countManager.get()), | |
55 // we can't validate its method calls with Mockito, so do so using our s
hadow method. | |
56 int mCallCount; | |
57 String mAccountTypeReceived; | |
58 String mAuthTokenTypeReceived; | |
59 String mFeaturesReceived[]; | |
60 Bundle mAddAccountOptionsReceived; | |
61 Bundle mAuthTokenOptionsReceived; | |
62 AccountManagerCallback<Bundle> mCallbackReceived; | |
63 Handler mHandlerReceived; | |
64 | |
65 public AccountManagerFuture<Bundle> getAuthTokenByFeatures(String accoun
tType, | |
66 String authTokenType, String[] features, Activity activity, | |
67 Bundle addAccountOptions, Bundle getAuthTokenOptions, | |
68 AccountManagerCallback<Bundle> callback, Handler handler) { | |
69 mCallCount++; | |
70 mAccountTypeReceived = accountType; | |
71 mAuthTokenTypeReceived = authTokenType; | |
72 mFeaturesReceived = features; | |
73 mAddAccountOptionsReceived = addAccountOptions; | |
74 mAuthTokenOptionsReceived = getAuthTokenOptions; | |
75 mCallbackReceived = callback; | |
76 mHandlerReceived = handler; | |
77 | |
78 return null; | |
79 } | |
80 } | |
81 | |
82 private static GetAuthTokenByFeaturesInvocation sInvocation; | |
83 | |
84 /** | 76 /** |
85 * Robolectic's ShadowAccountManager doesn't implement getAccountsByTypeAndF
eature so extend it. | 77 * User the AccountManager to inject a mock instance. |
86 * We simply check the call is correct, and don't try to emulate it. This al
so allows us to do | |
87 * more checking than we could using a vanilla shadow. | |
88 * | |
89 * Note: Shadow classes need to be public and static. | 78 * Note: Shadow classes need to be public and static. |
90 */ | 79 */ |
91 @Implements(AccountManager.class) | 80 @Implements(AccountManager.class) |
92 public static class ExtendedShadowAccountManager extends ShadowAccountManage
r { | 81 public static class ExtendedShadowAccountManager extends ShadowAccountManage
r { |
93 @Implementation | 82 @Implementation |
94 public AccountManagerFuture<Bundle> getAuthTokenByFeatures(String accoun
tType, | 83 public static AccountManager get(Context context) { |
95 String authTokenType, String[] features, Activity activity, | 84 return sMockAccountManager; |
96 Bundle addAccountOptions, Bundle getAuthTokenOptions, | |
97 AccountManagerCallback<Bundle> callback, Handler handler) { | |
98 return sInvocation.getAuthTokenByFeatures(accountType, authTokenType
, features, | |
99 activity, addAccountOptions, getAuthTokenOptions, callback,
handler); | |
100 } | 85 } |
101 } | 86 } |
102 | 87 |
| 88 @Mock |
| 89 private static AccountManager sMockAccountManager; |
| 90 @Captor |
| 91 private ArgumentCaptor<AccountManagerCallback<Bundle>> mBundleCallbackCaptor
; |
| 92 @Captor |
| 93 private ArgumentCaptor<AccountManagerCallback<Account[]>> mAccountCallbackCa
ptor; |
| 94 @Captor |
| 95 private ArgumentCaptor<Bundle> mBundleCaptor; |
| 96 |
103 @Before | 97 @Before |
104 public void setUp() { | 98 public void setUp() { |
105 sInvocation = new GetAuthTokenByFeaturesInvocation(); | 99 MockitoAnnotations.initMocks(this); |
| 100 ContextUtils.initApplicationContextForJUnitTests(Robolectric.application
); |
| 101 } |
| 102 |
| 103 @After |
| 104 public void tearDown() { |
| 105 ApplicationStatus.destroyForJUnitTests(); |
106 } | 106 } |
107 | 107 |
108 /** | 108 /** |
109 * Test of {@link HttpNegotiateAuthenticator#getNextAuthToken} | 109 * Test of {@link HttpNegotiateAuthenticator#getNextAuthToken} |
110 */ | 110 */ |
111 @Test | 111 @Test |
112 public void testGetNextAuthToken() { | 112 public void testGetNextAuthToken() { |
113 HttpNegotiateAuthenticator authenticator = | 113 final String accountType = "Dummy_Account"; |
114 HttpNegotiateAuthenticator.create("Dummy_Account"); | 114 HttpNegotiateAuthenticator authenticator = createWithoutNative(accountTy
pe); |
115 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); | 115 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 116 |
116 authenticator.getNextAuthToken(0, "test_principal", "", true); | 117 authenticator.getNextAuthToken(0, "test_principal", "", true); |
117 assertThat( | 118 |
118 "getAuthTokenByFeatures called precisely once", sInvocation.mCal
lCount, equalTo(1)); | 119 verify(sMockAccountManager).getAuthTokenByFeatures( |
119 assertThat("Received account type matches input", sInvocation.mAccountTy
peReceived, | 120 eq(accountType), |
120 equalTo("Dummy_Account")); | 121 eq("SPNEGO:HOSTBASED:test_principal"), |
121 assertThat("AuthTokenType is \"SPNEGO:HOSTBASED:test_principal\"", | 122 eq(new String[] {"SPNEGO"}), |
122 sInvocation.mAuthTokenTypeReceived, equalTo("SPNEGO:HOSTBASED:te
st_principal")); | 123 any(Activity.class), |
123 assertThat("Features are precisely {\"SPNEGO\"}", sInvocation.mFeaturesR
eceived, | 124 isNull(Bundle.class), |
124 equalTo(new String[] {"SPNEGO"})); | 125 mBundleCaptor.capture(), |
125 assertThat("No account options requested", sInvocation.mAddAccountOption
sReceived, | 126 mBundleCallbackCaptor.capture(), |
126 nullValue()); | 127 any(Handler.class)); |
| 128 |
127 assertThat("There is no existing context", | 129 assertThat("There is no existing context", |
128 sInvocation.mAuthTokenOptionsReceived.get( | 130 mBundleCaptor.getValue().get(HttpNegotiateConstants.KEY_SPNEGO_C
ONTEXT), |
129 HttpNegotiateConstants.KEY_SPNEGO_CONTEXT), | |
130 nullValue()); | 131 nullValue()); |
131 assertThat("The existing token is empty", | 132 assertThat("The existing token is empty", |
132 sInvocation.mAuthTokenOptionsReceived.getString( | 133 mBundleCaptor.getValue().getString(HttpNegotiateConstants.KEY_IN
COMING_AUTH_TOKEN), |
133 HttpNegotiateConstants.KEY_INCOMING_AUTH_TOKEN), | |
134 equalTo("")); | 134 equalTo("")); |
135 assertThat("Delegation is allowed", sInvocation.mAuthTokenOptionsReceive
d.getBoolean( | 135 assertThat("Delegation is allowed", |
136 HttpNegotiateConstants.KEY_C
AN_DELEGATE), | 136 mBundleCaptor.getValue().getBoolean(HttpNegotiateConstants.KEY_C
AN_DELEGATE), |
137 equalTo(true)); | 137 equalTo(true)); |
138 assertThat("getAuthTokenByFeatures was called with a callback", | 138 assertThat("getAuthTokenByFeatures was called with a callback", |
139 sInvocation.mCallbackReceived, notNullValue()); | 139 mBundleCallbackCaptor.getValue(), notNullValue()); |
140 assertThat("getAuthTokenByFeatures was called with a handler", sInvocati
on.mHandlerReceived, | |
141 notNullValue()); | |
142 } | 140 } |
143 | 141 |
144 /** | 142 /** |
| 143 * Test of {@link HttpNegotiateAuthenticator#getNextAuthToken} without a vis
ible activity. |
| 144 * This emulates the behavior with WebView, where the application is a gener
ic one and doesn't |
| 145 * set up the ApplicationStatus the same way. |
| 146 */ |
| 147 @Test |
| 148 @Config(application = Application.class) |
| 149 public void testGetNextAuthTokenWithoutActivity() { |
| 150 final String accountType = "Dummy_Account"; |
| 151 final Account[] returnedAccount = {new Account("name", accountType)}; |
| 152 HttpNegotiateAuthenticator authenticator = createWithoutNative(accountTy
pe); |
| 153 |
| 154 authenticator.getNextAuthToken(1234, "test_principal", "", true); |
| 155 |
| 156 Assert.assertNull(ApplicationStatus.getLastTrackedFocusedActivity()); |
| 157 verify(sMockAccountManager).getAccountsByTypeAndFeatures( |
| 158 eq(accountType), |
| 159 eq(new String[]{"SPNEGO"}), |
| 160 mAccountCallbackCaptor.capture(), |
| 161 any(Handler.class)); |
| 162 |
| 163 mAccountCallbackCaptor.getValue().run(makeFuture(returnedAccount)); |
| 164 |
| 165 verify(sMockAccountManager).getAuthToken( |
| 166 any(Account.class), |
| 167 eq("SPNEGO:HOSTBASED:test_principal"), |
| 168 mBundleCaptor.capture(), |
| 169 eq(true), |
| 170 any(HttpNegotiateAuthenticator.GetTokenCallback.class), |
| 171 any(Handler.class)); |
| 172 |
| 173 assertThat("There is no existing context", |
| 174 mBundleCaptor.getValue().get(HttpNegotiateConstants.KEY_SPNEGO_C
ONTEXT), |
| 175 nullValue()); |
| 176 assertThat("The existing token is empty", |
| 177 mBundleCaptor.getValue().getString(HttpNegotiateConstants.KEY_IN
COMING_AUTH_TOKEN), |
| 178 equalTo("")); |
| 179 assertThat("Delegation is allowed", |
| 180 mBundleCaptor.getValue().getBoolean(HttpNegotiateConstants.KEY_C
AN_DELEGATE), |
| 181 equalTo(true)); |
| 182 } |
| 183 |
| 184 /** Tests the behavior of {@link HttpNegotiateAuthenticator.GetAccountsCallb
ack} */ |
| 185 @Test |
| 186 public void testGetAccountCallback() { |
| 187 String type = "Dummy_Account"; |
| 188 HttpNegotiateAuthenticator authenticator = createWithoutNative(type); |
| 189 RequestData requestData = new RequestData(); |
| 190 requestData.nativeResultObject = 42; |
| 191 requestData.accountManager = sMockAccountManager; |
| 192 GetAccountsCallback callback = authenticator.new GetAccountsCallback(req
uestData); |
| 193 |
| 194 // Should fail because there are no accounts |
| 195 callback.run(makeFuture(new Account[]{})); |
| 196 verify(authenticator).nativeSetResult( |
| 197 eq(42L), |
| 198 eq(NetError.ERR_INVALID_AUTH_CREDENTIALS), |
| 199 isNull(String.class)); |
| 200 |
| 201 // Should succeed, for a single account we use it for the AccountManager
#getAuthToken call. |
| 202 Account testAccount = new Account("a", type); |
| 203 callback.run(makeFuture(new Account[]{testAccount})); |
| 204 verify(sMockAccountManager).getAuthToken( |
| 205 eq(testAccount), |
| 206 anyString(), |
| 207 any(Bundle.class), |
| 208 eq(true), |
| 209 any(HttpNegotiateAuthenticator.GetTokenCallback.class), |
| 210 any(Handler.class)); |
| 211 |
| 212 // Should fail because there is more than one account |
| 213 callback.run(makeFuture(new Account[]{new Account("a", type), new Accoun
t("b", type)})); |
| 214 verify(authenticator, times(2)).nativeSetResult( |
| 215 eq(42L), |
| 216 eq(NetError.ERR_INVALID_AUTH_CREDENTIALS), |
| 217 isNull(String.class)); |
| 218 } |
| 219 |
| 220 /** |
| 221 * Tests the behavior of {@link HttpNegotiateAuthenticator.GetTokenCallback}
when the result it |
| 222 * receives contains an intent rather than a token directly. |
| 223 */ |
| 224 @Test |
| 225 public void testGetTokenCallbackWithIntent() { |
| 226 String type = "Dummy_Account"; |
| 227 HttpNegotiateAuthenticator authenticator = createWithoutNative(type); |
| 228 RequestData requestData = new RequestData(); |
| 229 requestData.nativeResultObject = 42; |
| 230 requestData.authTokenType = "foo"; |
| 231 requestData.account = new Account("a", type); |
| 232 requestData.accountManager = sMockAccountManager; |
| 233 Bundle b = new Bundle(); |
| 234 b.putParcelable(AccountManager.KEY_INTENT, new Intent()); |
| 235 |
| 236 authenticator.new GetTokenCallback(requestData).run(makeFuture(b)); |
| 237 verifyZeroInteractions(sMockAccountManager); |
| 238 |
| 239 // Verify that the broadcast receiver is registered |
| 240 Intent intent = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION)
; |
| 241 ShadowApplication shadowApplication = Robolectric.getShadowApplication()
; |
| 242 List<BroadcastReceiver> receivers = shadowApplication.getReceiversForInt
ent(intent); |
| 243 assertThat("There is one registered broadcast receiver", receivers.size(
), equalTo(1)); |
| 244 |
| 245 // Send the intent to the receiver. |
| 246 BroadcastReceiver receiver = receivers.get(0); |
| 247 receiver.onReceive(Robolectric.getShadowApplication().getApplicationCont
ext(), intent); |
| 248 |
| 249 // Verify that the auth token is properly requested from the account man
ager. |
| 250 verify(sMockAccountManager).getAuthToken( |
| 251 eq(new Account("a", type)), |
| 252 eq("foo"), |
| 253 isNull(Bundle.class), |
| 254 eq(true), |
| 255 any(HttpNegotiateAuthenticator.GetTokenCallback.class), |
| 256 any(Handler.class)); |
| 257 } |
| 258 |
| 259 /** |
145 * Test of callback called when getting the auth token completes. | 260 * Test of callback called when getting the auth token completes. |
146 */ | 261 */ |
147 @Test | 262 @Test |
148 public void testAccountManagerCallbackRun() { | 263 public void testAccountManagerCallbackRun() { |
149 // Spy on the authenticator so that we can override and intercept the na
tive method call. | 264 HttpNegotiateAuthenticator authenticator = createWithoutNative("Dummy_Ac
count"); |
150 HttpNegotiateAuthenticator authenticator = | |
151 spy(HttpNegotiateAuthenticator.create("Dummy_Account")); | |
152 doNothing().when(authenticator).nativeSetResult(anyLong(), anyInt(), any
String()); | |
153 | 265 |
154 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); | 266 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
155 | 267 |
156 // Call getNextAuthToken to get the callback | 268 // Call getNextAuthToken to get the callback |
157 authenticator.getNextAuthToken(1234, "test_principal", "", true); | 269 authenticator.getNextAuthToken(1234, "test_principal", "", true); |
| 270 verify(sMockAccountManager) |
| 271 .getAuthTokenByFeatures(anyString(), anyString(), any(String[].c
lass), |
| 272 any(Activity.class), any(Bundle.class), any(Bundle.class
), |
| 273 mBundleCallbackCaptor.capture(), any(Handler.class)); |
158 | 274 |
159 // Avoid warning when creating mock accountManagerFuture, can't take .cl
ass of an | |
160 // instantiated generic type, yet compiler complains if I leave it unins
tantiated. | |
161 @SuppressWarnings("unchecked") | |
162 AccountManagerFuture<Bundle> accountManagerFuture = mock(AccountManagerF
uture.class); | |
163 Bundle resultBundle = new Bundle(); | 275 Bundle resultBundle = new Bundle(); |
164 Bundle context = new Bundle(); | 276 Bundle context = new Bundle(); |
165 context.putString("String", "test_context"); | 277 context.putString("String", "test_context"); |
166 resultBundle.putInt(HttpNegotiateConstants.KEY_SPNEGO_RESULT, HttpNegoti
ateConstants.OK); | 278 resultBundle.putInt(HttpNegotiateConstants.KEY_SPNEGO_RESULT, HttpNegoti
ateConstants.OK); |
167 resultBundle.putBundle(HttpNegotiateConstants.KEY_SPNEGO_CONTEXT, contex
t); | 279 resultBundle.putBundle(HttpNegotiateConstants.KEY_SPNEGO_CONTEXT, contex
t); |
168 resultBundle.putString(AccountManager.KEY_AUTHTOKEN, "output_token"); | 280 resultBundle.putString(AccountManager.KEY_AUTHTOKEN, "output_token"); |
| 281 mBundleCallbackCaptor.getValue().run(makeFuture(resultBundle)); |
| 282 verify(authenticator).nativeSetResult(1234, 0, "output_token"); |
| 283 |
| 284 // Check that the next call to getNextAuthToken uses the correct context |
| 285 authenticator.getNextAuthToken(5678, "test_principal", "", true); |
| 286 verify(sMockAccountManager, times(2)) |
| 287 .getAuthTokenByFeatures(anyString(), anyString(), any(String[].c
lass), |
| 288 any(Activity.class), any(Bundle.class), mBundleCaptor.ca
pture(), |
| 289 mBundleCallbackCaptor.capture(), any(Handler.class)); |
| 290 |
| 291 assertThat("The spnego context is preserved between calls", |
| 292 mBundleCaptor.getValue().getBundle(HttpNegotiateConstants.KEY_SP
NEGO_CONTEXT), |
| 293 equalTo(context)); |
| 294 |
| 295 // Test exception path |
| 296 mBundleCallbackCaptor.getValue().run( |
| 297 this.<Bundle>makeFuture(new OperationCanceledException())); |
| 298 verify(authenticator).nativeSetResult(5678, NetError.ERR_UNEXPECTED, nul
l); |
| 299 } |
| 300 |
| 301 @Test |
| 302 public void testPermissionDenied() { |
| 303 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 304 HttpNegotiateAuthenticator authenticator = createWithoutNative("Dummy_Ac
count"); |
| 305 |
| 306 doReturn(false).when(authenticator).hasPermission(any(Context.class), an
yString()); |
| 307 |
| 308 authenticator.getNextAuthToken(1234, "test_principal", "", true); |
| 309 verify(authenticator) |
| 310 .nativeSetResult(anyLong(), eq(NetError.ERR_MISCONFIGURED_AUTH_E
NVIRONMENT), |
| 311 isNull(String.class)); |
| 312 } |
| 313 |
| 314 @Test |
| 315 public void testAccountManagerCallbackNullErrorReturns() { |
| 316 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 317 checkErrorReturn(null, NetError.ERR_UNEXPECTED); |
| 318 } |
| 319 |
| 320 @Test |
| 321 public void testAccountManagerCallbackUnexpectedErrorReturns() { |
| 322 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 323 checkErrorReturn(HttpNegotiateConstants.ERR_UNEXPECTED, NetError.ERR_UNE
XPECTED); |
| 324 } |
| 325 |
| 326 @Test |
| 327 public void testAccountManagerCallbackAbortedErrorReturns() { |
| 328 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 329 checkErrorReturn(HttpNegotiateConstants.ERR_ABORTED, NetError.ERR_ABORTE
D); |
| 330 } |
| 331 |
| 332 @Test |
| 333 public void testAccountManagerCallbackSecLibErrorReturns() { |
| 334 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 335 checkErrorReturn(HttpNegotiateConstants.ERR_UNEXPECTED_SECURITY_LIBRARY_
STATUS, |
| 336 NetError.ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS); |
| 337 } |
| 338 |
| 339 @Test |
| 340 public void testAccountManagerCallbackInvalidResponseErrorReturns() { |
| 341 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 342 checkErrorReturn( |
| 343 HttpNegotiateConstants.ERR_INVALID_RESPONSE, NetError.ERR_INVALI
D_RESPONSE); |
| 344 } |
| 345 |
| 346 @Test |
| 347 public void testAccountManagerCallbackInvalidAuthCredsErrorReturns() { |
| 348 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 349 checkErrorReturn(HttpNegotiateConstants.ERR_INVALID_AUTH_CREDENTIALS, |
| 350 NetError.ERR_INVALID_AUTH_CREDENTIALS); |
| 351 } |
| 352 |
| 353 @Test |
| 354 public void testAccountManagerCallbackUnsuppAutchSchemeErrorReturns() { |
| 355 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 356 checkErrorReturn(HttpNegotiateConstants.ERR_UNSUPPORTED_AUTH_SCHEME, |
| 357 NetError.ERR_UNSUPPORTED_AUTH_SCHEME); |
| 358 } |
| 359 |
| 360 @Test |
| 361 public void testAccountManagerCallbackMissingAuthCredsErrorReturns() { |
| 362 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 363 checkErrorReturn(HttpNegotiateConstants.ERR_MISSING_AUTH_CREDENTIALS, |
| 364 NetError.ERR_MISSING_AUTH_CREDENTIALS); |
| 365 } |
| 366 |
| 367 @Test |
| 368 public void testAccountManagerCallbackUndocSecLibErrorReturns() { |
| 369 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 370 checkErrorReturn(HttpNegotiateConstants.ERR_UNDOCUMENTED_SECURITY_LIBRAR
Y_STATUS, |
| 371 NetError.ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS); |
| 372 } |
| 373 |
| 374 @Test |
| 375 public void testAccountManagerCallbackMalformedIdentityErrorReturns() { |
| 376 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 377 checkErrorReturn( |
| 378 HttpNegotiateConstants.ERR_MALFORMED_IDENTITY, NetError.ERR_MALF
ORMED_IDENTITY); |
| 379 } |
| 380 |
| 381 @Test |
| 382 public void testAccountManagerCallbackInvalidErrorReturns() { |
| 383 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 384 // 9999 is not a valid return value |
| 385 checkErrorReturn(9999, NetError.ERR_UNEXPECTED); |
| 386 } |
| 387 |
| 388 private void checkErrorReturn(Integer spnegoError, int expectedError) { |
| 389 HttpNegotiateAuthenticator authenticator = createWithoutNative("Dummy_Ac
count"); |
| 390 |
| 391 // Call getNextAuthToken to get the callback |
| 392 authenticator.getNextAuthToken(1234, "test_principal", "", true); |
| 393 verify(sMockAccountManager).getAuthTokenByFeatures( |
| 394 anyString(), |
| 395 anyString(), |
| 396 any(String[].class), |
| 397 any(Activity.class), |
| 398 any(Bundle.class), |
| 399 any(Bundle.class), |
| 400 mBundleCallbackCaptor.capture(), |
| 401 any(Handler.class)); |
| 402 |
| 403 Bundle resultBundle = new Bundle(); |
| 404 if (spnegoError != null) { |
| 405 resultBundle.putInt(HttpNegotiateConstants.KEY_SPNEGO_RESULT, spnego
Error); |
| 406 } |
| 407 mBundleCallbackCaptor.getValue().run(makeFuture(resultBundle)); |
| 408 verify(authenticator).nativeSetResult(anyLong(), eq(expectedError), anyS
tring()); |
| 409 } |
| 410 |
| 411 /** |
| 412 * Returns a future that successfully returns the provided result. |
| 413 * Hides mocking related annoyances: compiler warnings and irrelevant catch
clauses. |
| 414 */ |
| 415 private <T> AccountManagerFuture<T> makeFuture(T result) { |
| 416 // Avoid warning when creating mock accountManagerFuture, can't take .cl
ass of an |
| 417 // instantiated generic type, yet compiler complains if I leave it unins
tantiated. |
| 418 @SuppressWarnings("unchecked") |
| 419 AccountManagerFuture<T> accountManagerFuture = mock(AccountManagerFuture
.class); |
169 try { | 420 try { |
170 when(accountManagerFuture.getResult()).thenReturn(resultBundle); | 421 when(accountManagerFuture.getResult()).thenReturn(result); |
171 } catch (OperationCanceledException | AuthenticatorException | IOExcepti
on e) { | 422 } catch (OperationCanceledException | AuthenticatorException | IOExcepti
on e) { |
172 // Can never happen - artifact of Mockito. | 423 // Can never happen - artifact of Mockito. |
173 fail(); | 424 fail(); |
174 } | 425 } |
175 sInvocation.mCallbackReceived.run(accountManagerFuture); | 426 return accountManagerFuture; |
176 verify(authenticator).nativeSetResult(1234, 0, "output_token"); | 427 } |
177 | 428 |
178 // Check that the next call to getNextAuthToken uses the correct context | 429 /** |
179 authenticator.getNextAuthToken(5678, "test_principal", "", true); | 430 * Returns a future that fails with the provided exception when trying to ge
t its result. |
180 assertThat("The spnego context is preserved between calls", | 431 * Hides mocking related annoyances: compiler warnings and irrelevant catch
clauses. |
181 sInvocation.mAuthTokenOptionsReceived.getBundle( | 432 */ |
182 HttpNegotiateConstants.KEY_SPNEGO_CONTEXT), | 433 private <T> AccountManagerFuture<T> makeFuture(Exception ex) { |
183 equalTo(context)); | 434 // Avoid warning when creating mock accountManagerFuture, can't take .cl
ass of an |
184 | 435 // instantiated generic type, yet compiler complains if I leave it unins
tantiated. |
185 // Test exception path | 436 @SuppressWarnings("unchecked") |
| 437 AccountManagerFuture<T> accountManagerFuture = mock(AccountManagerFuture
.class); |
186 try { | 438 try { |
187 when(accountManagerFuture.getResult()).thenThrow(new OperationCancel
edException()); | 439 when(accountManagerFuture.getResult()).thenThrow(ex); |
188 } catch (OperationCanceledException | AuthenticatorException | IOExcepti
on e) { | 440 } catch (OperationCanceledException | AuthenticatorException | IOExcepti
on e) { |
189 // Can never happen - artifact of Mockito. | 441 // Can never happen - artifact of Mockito. |
190 fail(); | 442 fail(); |
191 } | 443 } |
192 sInvocation.mCallbackReceived.run(accountManagerFuture); | 444 return accountManagerFuture; |
193 verify(authenticator).nativeSetResult(5678, NetError.ERR_ABORTED, null); | |
194 } | |
195 | |
196 private void checkErrorReturn(Integer spnegoError, int expectedError) { | |
197 // Spy on the authenticator so that we can override and intercept the na
tive method call. | |
198 HttpNegotiateAuthenticator authenticator = | |
199 spy(HttpNegotiateAuthenticator.create("Dummy_Account")); | |
200 doNothing().when(authenticator).nativeSetResult(anyLong(), anyInt(), any
String()); | |
201 | |
202 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); | |
203 | |
204 // Call getNextAuthToken to get the callback | |
205 authenticator.getNextAuthToken(1234, "test_principal", "", true); | |
206 | |
207 // Avoid warning when creating mock accountManagerFuture, can't take .cl
ass of an | |
208 // instantiated generic type, yet compiler complains if I leave it unins
tantiated. | |
209 @SuppressWarnings("unchecked") | |
210 AccountManagerFuture<Bundle> accountManagerFuture = mock(AccountManagerF
uture.class); | |
211 Bundle resultBundle = new Bundle(); | |
212 if (spnegoError != null) { | |
213 resultBundle.putInt(HttpNegotiateConstants.KEY_SPNEGO_RESULT, spnego
Error); | |
214 } | |
215 try { | |
216 when(accountManagerFuture.getResult()).thenReturn(resultBundle); | |
217 } catch (OperationCanceledException | AuthenticatorException | IOExcepti
on e) { | |
218 // Can never happen - artifact of Mockito. | |
219 fail(); | |
220 } | |
221 sInvocation.mCallbackReceived.run(accountManagerFuture); | |
222 verify(authenticator).nativeSetResult(anyLong(), eq(expectedError), anyS
tring()); | |
223 } | 445 } |
224 | 446 |
225 /** | 447 /** |
226 * Test of callback error returns when getting the auth token completes. | 448 * Returns a new authenticator as a spy so that we can override and intercep
t the native method |
| 449 * calls. |
227 */ | 450 */ |
228 @Test | 451 private HttpNegotiateAuthenticator createWithoutNative(String accountType) { |
229 public void testAccountManagerCallbackErrorReturns() { | 452 HttpNegotiateAuthenticator authenticator = |
230 checkErrorReturn(null, NetError.ERR_UNEXPECTED); | 453 spy(HttpNegotiateAuthenticator.create(accountType)); |
231 checkErrorReturn(HttpNegotiateConstants.ERR_UNEXPECTED, NetError.ERR_UNE
XPECTED); | 454 doNothing().when(authenticator).nativeSetResult(anyLong(), anyInt(), any
String()); |
232 checkErrorReturn(HttpNegotiateConstants.ERR_ABORTED, NetError.ERR_ABORTE
D); | 455 doReturn(true).when(authenticator).hasPermission(any(Context.class), any
String()); |
233 checkErrorReturn(HttpNegotiateConstants.ERR_UNEXPECTED_SECURITY_LIBRARY_
STATUS, | 456 return authenticator; |
234 NetError.ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS); | |
235 checkErrorReturn( | |
236 HttpNegotiateConstants.ERR_INVALID_RESPONSE, NetError.ERR_INVALI
D_RESPONSE); | |
237 checkErrorReturn(HttpNegotiateConstants.ERR_INVALID_AUTH_CREDENTIALS, | |
238 NetError.ERR_INVALID_AUTH_CREDENTIALS); | |
239 checkErrorReturn(HttpNegotiateConstants.ERR_UNSUPPORTED_AUTH_SCHEME, | |
240 NetError.ERR_UNSUPPORTED_AUTH_SCHEME); | |
241 checkErrorReturn(HttpNegotiateConstants.ERR_MISSING_AUTH_CREDENTIALS, | |
242 NetError.ERR_MISSING_AUTH_CREDENTIALS); | |
243 checkErrorReturn(HttpNegotiateConstants.ERR_UNDOCUMENTED_SECURITY_LIBRAR
Y_STATUS, | |
244 NetError.ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS); | |
245 checkErrorReturn( | |
246 HttpNegotiateConstants.ERR_MALFORMED_IDENTITY, NetError.ERR_MALF
ORMED_IDENTITY); | |
247 // 9999 is not a valid return value | |
248 checkErrorReturn(9999, NetError.ERR_UNEXPECTED); | |
249 } | 457 } |
250 } | 458 } |
OLD | NEW |