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