Chromium Code Reviews| 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 |