Index: net/android/junit/src/org/chromium/net/HttpNegotiateAuthenticatorTest.java |
diff --git a/net/android/junit/src/org/chromium/net/HttpNegotiateAuthenticatorTest.java b/net/android/junit/src/org/chromium/net/HttpNegotiateAuthenticatorTest.java |
index 0044420ca1fb9e5108251a6b5313048dce179eda..b0d76c47ec9e18cc3246616579709b67055ea66f 100644 |
--- a/net/android/junit/src/org/chromium/net/HttpNegotiateAuthenticatorTest.java |
+++ b/net/android/junit/src/org/chromium/net/HttpNegotiateAuthenticatorTest.java |
@@ -9,38 +9,61 @@ |
import static org.hamcrest.CoreMatchers.nullValue; |
import static org.junit.Assert.assertThat; |
import static org.junit.Assert.fail; |
+import static org.mockito.Matchers.any; |
import static org.mockito.Matchers.anyInt; |
import static org.mockito.Matchers.anyLong; |
import static org.mockito.Matchers.anyString; |
import static org.mockito.Matchers.eq; |
+import static org.mockito.Matchers.isNull; |
import static org.mockito.Mockito.doNothing; |
+import static org.mockito.Mockito.doReturn; |
import static org.mockito.Mockito.mock; |
import static org.mockito.Mockito.spy; |
+import static org.mockito.Mockito.times; |
import static org.mockito.Mockito.verify; |
+import static org.mockito.Mockito.verifyZeroInteractions; |
import static org.mockito.Mockito.when; |
+import android.accounts.Account; |
import android.accounts.AccountManager; |
import android.accounts.AccountManagerCallback; |
import android.accounts.AccountManagerFuture; |
import android.accounts.AuthenticatorException; |
import android.accounts.OperationCanceledException; |
import android.app.Activity; |
+import android.app.Application; |
+import android.content.BroadcastReceiver; |
+import android.content.Context; |
+import android.content.Intent; |
import android.os.Bundle; |
import android.os.Handler; |
+import junit.framework.Assert; |
+ |
+import org.chromium.base.ApplicationStatus; |
import org.chromium.base.BaseChromiumApplication; |
+import org.chromium.base.ContextUtils; |
import org.chromium.base.test.shadows.ShadowMultiDex; |
+import org.chromium.net.HttpNegotiateAuthenticator.GetAccountsCallback; |
+import org.chromium.net.HttpNegotiateAuthenticator.RequestData; |
import org.chromium.testing.local.LocalRobolectricTestRunner; |
+import org.junit.After; |
import org.junit.Before; |
import org.junit.Test; |
import org.junit.runner.RunWith; |
+import org.mockito.ArgumentCaptor; |
+import org.mockito.Captor; |
+import org.mockito.Mock; |
+import org.mockito.MockitoAnnotations; |
import org.robolectric.Robolectric; |
import org.robolectric.annotation.Config; |
import org.robolectric.annotation.Implementation; |
import org.robolectric.annotation.Implements; |
import org.robolectric.shadows.ShadowAccountManager; |
+import org.robolectric.shadows.ShadowApplication; |
import java.io.IOException; |
+import java.util.List; |
/** |
* Robolectric tests for HttpNegotiateAuthenticator |
@@ -50,59 +73,36 @@ |
shadows = {HttpNegotiateAuthenticatorTest.ExtendedShadowAccountManager.class, |
ShadowMultiDex.class}) |
public class HttpNegotiateAuthenticatorTest { |
- private static class GetAuthTokenByFeaturesInvocation { |
- // Since the account manager is an SDK singleton (it is fetched using AccountManager.get()), |
- // we can't validate its method calls with Mockito, so do so using our shadow method. |
- int mCallCount; |
- String mAccountTypeReceived; |
- String mAuthTokenTypeReceived; |
- String mFeaturesReceived[]; |
- Bundle mAddAccountOptionsReceived; |
- Bundle mAuthTokenOptionsReceived; |
- AccountManagerCallback<Bundle> mCallbackReceived; |
- Handler mHandlerReceived; |
- |
- public AccountManagerFuture<Bundle> getAuthTokenByFeatures(String accountType, |
- String authTokenType, String[] features, Activity activity, |
- Bundle addAccountOptions, Bundle getAuthTokenOptions, |
- AccountManagerCallback<Bundle> callback, Handler handler) { |
- mCallCount++; |
- mAccountTypeReceived = accountType; |
- mAuthTokenTypeReceived = authTokenType; |
- mFeaturesReceived = features; |
- mAddAccountOptionsReceived = addAccountOptions; |
- mAuthTokenOptionsReceived = getAuthTokenOptions; |
- mCallbackReceived = callback; |
- mHandlerReceived = handler; |
- |
- return null; |
- } |
- } |
- |
- private static GetAuthTokenByFeaturesInvocation sInvocation; |
- |
/** |
- * Robolectic's ShadowAccountManager doesn't implement getAccountsByTypeAndFeature so extend it. |
- * We simply check the call is correct, and don't try to emulate it. This also allows us to do |
- * more checking than we could using a vanilla shadow. |
- * |
+ * User the AccountManager to inject a mock instance. |
* Note: Shadow classes need to be public and static. |
*/ |
@Implements(AccountManager.class) |
public static class ExtendedShadowAccountManager extends ShadowAccountManager { |
@Implementation |
- public AccountManagerFuture<Bundle> getAuthTokenByFeatures(String accountType, |
- String authTokenType, String[] features, Activity activity, |
- Bundle addAccountOptions, Bundle getAuthTokenOptions, |
- AccountManagerCallback<Bundle> callback, Handler handler) { |
- return sInvocation.getAuthTokenByFeatures(accountType, authTokenType, features, |
- activity, addAccountOptions, getAuthTokenOptions, callback, handler); |
+ public static AccountManager get(Context context) { |
+ return sMockAccountManager; |
} |
} |
+ @Mock |
+ private static AccountManager sMockAccountManager; |
+ @Captor |
+ private ArgumentCaptor<AccountManagerCallback<Bundle>> mBundleCallbackCaptor; |
+ @Captor |
+ private ArgumentCaptor<AccountManagerCallback<Account[]>> mAccountCallbackCaptor; |
+ @Captor |
+ private ArgumentCaptor<Bundle> mBundleCaptor; |
+ |
@Before |
public void setUp() { |
- sInvocation = new GetAuthTokenByFeaturesInvocation(); |
+ MockitoAnnotations.initMocks(this); |
+ ContextUtils.initApplicationContextForJUnitTests(Robolectric.application); |
+ } |
+ |
+ @After |
+ public void tearDown() { |
+ ApplicationStatus.destroyForJUnitTests(); |
} |
/** |
@@ -110,35 +110,150 @@ public void setUp() { |
*/ |
@Test |
public void testGetNextAuthToken() { |
- HttpNegotiateAuthenticator authenticator = |
- HttpNegotiateAuthenticator.create("Dummy_Account"); |
+ final String accountType = "Dummy_Account"; |
+ HttpNegotiateAuthenticator authenticator = createWithoutNative(accountType); |
Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
+ |
authenticator.getNextAuthToken(0, "test_principal", "", true); |
- assertThat( |
- "getAuthTokenByFeatures called precisely once", sInvocation.mCallCount, equalTo(1)); |
- assertThat("Received account type matches input", sInvocation.mAccountTypeReceived, |
- equalTo("Dummy_Account")); |
- assertThat("AuthTokenType is \"SPNEGO:HOSTBASED:test_principal\"", |
- sInvocation.mAuthTokenTypeReceived, equalTo("SPNEGO:HOSTBASED:test_principal")); |
- assertThat("Features are precisely {\"SPNEGO\"}", sInvocation.mFeaturesReceived, |
- equalTo(new String[] {"SPNEGO"})); |
- assertThat("No account options requested", sInvocation.mAddAccountOptionsReceived, |
- nullValue()); |
+ |
+ verify(sMockAccountManager).getAuthTokenByFeatures( |
+ eq(accountType), |
+ eq("SPNEGO:HOSTBASED:test_principal"), |
+ eq(new String[] {"SPNEGO"}), |
+ any(Activity.class), |
+ isNull(Bundle.class), |
+ mBundleCaptor.capture(), |
+ mBundleCallbackCaptor.capture(), |
+ any(Handler.class)); |
+ |
assertThat("There is no existing context", |
- sInvocation.mAuthTokenOptionsReceived.get( |
- HttpNegotiateConstants.KEY_SPNEGO_CONTEXT), |
+ mBundleCaptor.getValue().get(HttpNegotiateConstants.KEY_SPNEGO_CONTEXT), |
nullValue()); |
assertThat("The existing token is empty", |
- sInvocation.mAuthTokenOptionsReceived.getString( |
- HttpNegotiateConstants.KEY_INCOMING_AUTH_TOKEN), |
+ mBundleCaptor.getValue().getString(HttpNegotiateConstants.KEY_INCOMING_AUTH_TOKEN), |
equalTo("")); |
- assertThat("Delegation is allowed", sInvocation.mAuthTokenOptionsReceived.getBoolean( |
- HttpNegotiateConstants.KEY_CAN_DELEGATE), |
+ assertThat("Delegation is allowed", |
+ mBundleCaptor.getValue().getBoolean(HttpNegotiateConstants.KEY_CAN_DELEGATE), |
equalTo(true)); |
assertThat("getAuthTokenByFeatures was called with a callback", |
- sInvocation.mCallbackReceived, notNullValue()); |
- assertThat("getAuthTokenByFeatures was called with a handler", sInvocation.mHandlerReceived, |
- notNullValue()); |
+ mBundleCallbackCaptor.getValue(), notNullValue()); |
+ } |
+ |
+ /** |
+ * Test of {@link HttpNegotiateAuthenticator#getNextAuthToken} without a visible activity. |
+ * This emulates the behavior with WebView, where the application is a generic one and doesn't |
+ * set up the ApplicationStatus the same way. |
+ */ |
+ @Test |
+ @Config(application = Application.class) |
+ public void testGetNextAuthTokenWithoutActivity() { |
+ final String accountType = "Dummy_Account"; |
+ final Account[] returnedAccount = {new Account("name", accountType)}; |
+ HttpNegotiateAuthenticator authenticator = createWithoutNative(accountType); |
+ |
+ authenticator.getNextAuthToken(1234, "test_principal", "", true); |
+ |
+ Assert.assertNull(ApplicationStatus.getLastTrackedFocusedActivity()); |
+ verify(sMockAccountManager).getAccountsByTypeAndFeatures( |
+ eq(accountType), |
+ eq(new String[]{"SPNEGO"}), |
+ mAccountCallbackCaptor.capture(), |
+ any(Handler.class)); |
+ |
+ mAccountCallbackCaptor.getValue().run(makeFuture(returnedAccount)); |
+ |
+ verify(sMockAccountManager).getAuthToken( |
+ any(Account.class), |
+ eq("SPNEGO:HOSTBASED:test_principal"), |
+ mBundleCaptor.capture(), |
+ eq(true), |
+ any(HttpNegotiateAuthenticator.GetTokenCallback.class), |
+ any(Handler.class)); |
+ |
+ assertThat("There is no existing context", |
+ mBundleCaptor.getValue().get(HttpNegotiateConstants.KEY_SPNEGO_CONTEXT), |
+ nullValue()); |
+ assertThat("The existing token is empty", |
+ mBundleCaptor.getValue().getString(HttpNegotiateConstants.KEY_INCOMING_AUTH_TOKEN), |
+ equalTo("")); |
+ assertThat("Delegation is allowed", |
+ mBundleCaptor.getValue().getBoolean(HttpNegotiateConstants.KEY_CAN_DELEGATE), |
+ equalTo(true)); |
+ } |
+ |
+ /** Tests the behavior of {@link HttpNegotiateAuthenticator.GetAccountsCallback} */ |
+ @Test |
+ public void testGetAccountCallback() { |
+ String type = "Dummy_Account"; |
+ HttpNegotiateAuthenticator authenticator = createWithoutNative(type); |
+ RequestData requestData = new RequestData(); |
+ requestData.nativeResultObject = 42; |
+ requestData.accountManager = sMockAccountManager; |
+ GetAccountsCallback callback = authenticator.new GetAccountsCallback(requestData); |
+ |
+ // Should fail because there are no accounts |
+ callback.run(makeFuture(new Account[]{})); |
+ verify(authenticator).nativeSetResult( |
+ eq(42L), |
+ eq(NetError.ERR_INVALID_AUTH_CREDENTIALS), |
+ isNull(String.class)); |
+ |
+ // Should succeed, for a single account we use it for the AccountManager#getAuthToken call. |
+ Account testAccount = new Account("a", type); |
+ callback.run(makeFuture(new Account[]{testAccount})); |
+ verify(sMockAccountManager).getAuthToken( |
+ eq(testAccount), |
+ anyString(), |
+ any(Bundle.class), |
+ eq(true), |
+ any(HttpNegotiateAuthenticator.GetTokenCallback.class), |
+ any(Handler.class)); |
+ |
+ // Should fail because there is more than one account |
+ callback.run(makeFuture(new Account[]{new Account("a", type), new Account("b", type)})); |
+ verify(authenticator, times(2)).nativeSetResult( |
+ eq(42L), |
+ eq(NetError.ERR_INVALID_AUTH_CREDENTIALS), |
+ isNull(String.class)); |
+ } |
+ |
+ /** |
+ * Tests the behavior of {@link HttpNegotiateAuthenticator.GetTokenCallback} when the result it |
+ * receives contains an intent rather than a token directly. |
+ */ |
+ @Test |
+ public void testGetTokenCallbackWithIntent() { |
+ String type = "Dummy_Account"; |
+ HttpNegotiateAuthenticator authenticator = createWithoutNative(type); |
+ RequestData requestData = new RequestData(); |
+ requestData.nativeResultObject = 42; |
+ requestData.authTokenType = "foo"; |
+ requestData.account = new Account("a", type); |
+ requestData.accountManager = sMockAccountManager; |
+ Bundle b = new Bundle(); |
+ b.putParcelable(AccountManager.KEY_INTENT, new Intent()); |
+ |
+ authenticator.new GetTokenCallback(requestData).run(makeFuture(b)); |
+ verifyZeroInteractions(sMockAccountManager); |
+ |
+ // Verify that the broadcast receiver is registered |
+ Intent intent = new Intent(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION); |
+ ShadowApplication shadowApplication = Robolectric.getShadowApplication(); |
+ List<BroadcastReceiver> receivers = shadowApplication.getReceiversForIntent(intent); |
+ assertThat("There is one registered broadcast receiver", receivers.size(), equalTo(1)); |
+ |
+ // Send the intent to the receiver. |
+ BroadcastReceiver receiver = receivers.get(0); |
+ receiver.onReceive(Robolectric.getShadowApplication().getApplicationContext(), intent); |
+ |
+ // Verify that the auth token is properly requested from the account manager. |
+ verify(sMockAccountManager).getAuthToken( |
+ eq(new Account("a", type)), |
+ eq("foo"), |
+ isNull(Bundle.class), |
+ eq(true), |
+ any(HttpNegotiateAuthenticator.GetTokenCallback.class), |
+ any(Handler.class)); |
} |
/** |
@@ -146,105 +261,198 @@ public void testGetNextAuthToken() { |
*/ |
@Test |
public void testAccountManagerCallbackRun() { |
- // Spy on the authenticator so that we can override and intercept the native method call. |
- HttpNegotiateAuthenticator authenticator = |
- spy(HttpNegotiateAuthenticator.create("Dummy_Account")); |
- doNothing().when(authenticator).nativeSetResult(anyLong(), anyInt(), anyString()); |
+ HttpNegotiateAuthenticator authenticator = createWithoutNative("Dummy_Account"); |
Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
// Call getNextAuthToken to get the callback |
authenticator.getNextAuthToken(1234, "test_principal", "", true); |
+ verify(sMockAccountManager) |
+ .getAuthTokenByFeatures(anyString(), anyString(), any(String[].class), |
+ any(Activity.class), any(Bundle.class), any(Bundle.class), |
+ mBundleCallbackCaptor.capture(), any(Handler.class)); |
- // Avoid warning when creating mock accountManagerFuture, can't take .class of an |
- // instantiated generic type, yet compiler complains if I leave it uninstantiated. |
- @SuppressWarnings("unchecked") |
- AccountManagerFuture<Bundle> accountManagerFuture = mock(AccountManagerFuture.class); |
Bundle resultBundle = new Bundle(); |
Bundle context = new Bundle(); |
context.putString("String", "test_context"); |
resultBundle.putInt(HttpNegotiateConstants.KEY_SPNEGO_RESULT, HttpNegotiateConstants.OK); |
resultBundle.putBundle(HttpNegotiateConstants.KEY_SPNEGO_CONTEXT, context); |
resultBundle.putString(AccountManager.KEY_AUTHTOKEN, "output_token"); |
- try { |
- when(accountManagerFuture.getResult()).thenReturn(resultBundle); |
- } catch (OperationCanceledException | AuthenticatorException | IOException e) { |
- // Can never happen - artifact of Mockito. |
- fail(); |
- } |
- sInvocation.mCallbackReceived.run(accountManagerFuture); |
+ mBundleCallbackCaptor.getValue().run(makeFuture(resultBundle)); |
verify(authenticator).nativeSetResult(1234, 0, "output_token"); |
// Check that the next call to getNextAuthToken uses the correct context |
authenticator.getNextAuthToken(5678, "test_principal", "", true); |
+ verify(sMockAccountManager, times(2)) |
+ .getAuthTokenByFeatures(anyString(), anyString(), any(String[].class), |
+ any(Activity.class), any(Bundle.class), mBundleCaptor.capture(), |
+ mBundleCallbackCaptor.capture(), any(Handler.class)); |
+ |
assertThat("The spnego context is preserved between calls", |
- sInvocation.mAuthTokenOptionsReceived.getBundle( |
- HttpNegotiateConstants.KEY_SPNEGO_CONTEXT), |
+ mBundleCaptor.getValue().getBundle(HttpNegotiateConstants.KEY_SPNEGO_CONTEXT), |
equalTo(context)); |
// Test exception path |
- try { |
- when(accountManagerFuture.getResult()).thenThrow(new OperationCanceledException()); |
- } catch (OperationCanceledException | AuthenticatorException | IOException e) { |
- // Can never happen - artifact of Mockito. |
- fail(); |
- } |
- sInvocation.mCallbackReceived.run(accountManagerFuture); |
- verify(authenticator).nativeSetResult(5678, NetError.ERR_ABORTED, null); |
+ mBundleCallbackCaptor.getValue().run( |
+ this.<Bundle>makeFuture(new OperationCanceledException())); |
+ verify(authenticator).nativeSetResult(5678, NetError.ERR_UNEXPECTED, null); |
} |
- private void checkErrorReturn(Integer spnegoError, int expectedError) { |
- // Spy on the authenticator so that we can override and intercept the native method call. |
- HttpNegotiateAuthenticator authenticator = |
- spy(HttpNegotiateAuthenticator.create("Dummy_Account")); |
- doNothing().when(authenticator).nativeSetResult(anyLong(), anyInt(), anyString()); |
- |
+ @Test |
+ public void testPermissionDenied() { |
Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
+ HttpNegotiateAuthenticator authenticator = createWithoutNative("Dummy_Account"); |
- // Call getNextAuthToken to get the callback |
- authenticator.getNextAuthToken(1234, "test_principal", "", true); |
+ doReturn(false).when(authenticator).hasPermission(any(Context.class), anyString()); |
- // Avoid warning when creating mock accountManagerFuture, can't take .class of an |
- // instantiated generic type, yet compiler complains if I leave it uninstantiated. |
- @SuppressWarnings("unchecked") |
- AccountManagerFuture<Bundle> accountManagerFuture = mock(AccountManagerFuture.class); |
- Bundle resultBundle = new Bundle(); |
- if (spnegoError != null) { |
- resultBundle.putInt(HttpNegotiateConstants.KEY_SPNEGO_RESULT, spnegoError); |
- } |
- try { |
- when(accountManagerFuture.getResult()).thenReturn(resultBundle); |
- } catch (OperationCanceledException | AuthenticatorException | IOException e) { |
- // Can never happen - artifact of Mockito. |
- fail(); |
- } |
- sInvocation.mCallbackReceived.run(accountManagerFuture); |
- verify(authenticator).nativeSetResult(anyLong(), eq(expectedError), anyString()); |
+ authenticator.getNextAuthToken(1234, "test_principal", "", true); |
+ verify(authenticator) |
+ .nativeSetResult(anyLong(), eq(NetError.ERR_MISCONFIGURED_AUTH_ENVIRONMENT), |
+ isNull(String.class)); |
} |
- /** |
- * Test of callback error returns when getting the auth token completes. |
- */ |
@Test |
- public void testAccountManagerCallbackErrorReturns() { |
+ public void testAccountManagerCallbackNullErrorReturns() { |
+ Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
checkErrorReturn(null, NetError.ERR_UNEXPECTED); |
+ } |
+ |
+ @Test |
+ public void testAccountManagerCallbackUnexpectedErrorReturns() { |
+ Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
checkErrorReturn(HttpNegotiateConstants.ERR_UNEXPECTED, NetError.ERR_UNEXPECTED); |
+ } |
+ |
+ @Test |
+ public void testAccountManagerCallbackAbortedErrorReturns() { |
+ Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
checkErrorReturn(HttpNegotiateConstants.ERR_ABORTED, NetError.ERR_ABORTED); |
+ } |
+ |
+ @Test |
+ public void testAccountManagerCallbackSecLibErrorReturns() { |
+ Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
checkErrorReturn(HttpNegotiateConstants.ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS, |
NetError.ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS); |
+ } |
+ |
+ @Test |
+ public void testAccountManagerCallbackInvalidResponseErrorReturns() { |
+ Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
checkErrorReturn( |
HttpNegotiateConstants.ERR_INVALID_RESPONSE, NetError.ERR_INVALID_RESPONSE); |
+ } |
+ |
+ @Test |
+ public void testAccountManagerCallbackInvalidAuthCredsErrorReturns() { |
+ Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
checkErrorReturn(HttpNegotiateConstants.ERR_INVALID_AUTH_CREDENTIALS, |
NetError.ERR_INVALID_AUTH_CREDENTIALS); |
+ } |
+ |
+ @Test |
+ public void testAccountManagerCallbackUnsuppAutchSchemeErrorReturns() { |
+ Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
checkErrorReturn(HttpNegotiateConstants.ERR_UNSUPPORTED_AUTH_SCHEME, |
NetError.ERR_UNSUPPORTED_AUTH_SCHEME); |
+ } |
+ |
+ @Test |
+ public void testAccountManagerCallbackMissingAuthCredsErrorReturns() { |
+ Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
checkErrorReturn(HttpNegotiateConstants.ERR_MISSING_AUTH_CREDENTIALS, |
NetError.ERR_MISSING_AUTH_CREDENTIALS); |
+ } |
+ |
+ @Test |
+ public void testAccountManagerCallbackUndocSecLibErrorReturns() { |
+ Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
checkErrorReturn(HttpNegotiateConstants.ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS, |
NetError.ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS); |
+ } |
+ |
+ @Test |
+ public void testAccountManagerCallbackMalformedIdentityErrorReturns() { |
+ Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
checkErrorReturn( |
HttpNegotiateConstants.ERR_MALFORMED_IDENTITY, NetError.ERR_MALFORMED_IDENTITY); |
+ } |
+ |
+ @Test |
+ public void testAccountManagerCallbackInvalidErrorReturns() { |
+ Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
// 9999 is not a valid return value |
checkErrorReturn(9999, NetError.ERR_UNEXPECTED); |
} |
+ |
+ private void checkErrorReturn(Integer spnegoError, int expectedError) { |
+ HttpNegotiateAuthenticator authenticator = createWithoutNative("Dummy_Account"); |
+ |
+ // Call getNextAuthToken to get the callback |
+ authenticator.getNextAuthToken(1234, "test_principal", "", true); |
+ verify(sMockAccountManager).getAuthTokenByFeatures( |
+ anyString(), |
+ anyString(), |
+ any(String[].class), |
+ any(Activity.class), |
+ any(Bundle.class), |
+ any(Bundle.class), |
+ mBundleCallbackCaptor.capture(), |
+ any(Handler.class)); |
+ |
+ Bundle resultBundle = new Bundle(); |
+ if (spnegoError != null) { |
+ resultBundle.putInt(HttpNegotiateConstants.KEY_SPNEGO_RESULT, spnegoError); |
+ } |
+ mBundleCallbackCaptor.getValue().run(makeFuture(resultBundle)); |
+ verify(authenticator).nativeSetResult(anyLong(), eq(expectedError), anyString()); |
+ } |
+ |
+ /** |
+ * Returns a future that successfully returns the provided result. |
+ * Hides mocking related annoyances: compiler warnings and irrelevant catch clauses. |
+ */ |
+ private <T> AccountManagerFuture<T> makeFuture(T result) { |
+ // Avoid warning when creating mock accountManagerFuture, can't take .class of an |
+ // instantiated generic type, yet compiler complains if I leave it uninstantiated. |
+ @SuppressWarnings("unchecked") |
+ AccountManagerFuture<T> accountManagerFuture = mock(AccountManagerFuture.class); |
+ try { |
+ when(accountManagerFuture.getResult()).thenReturn(result); |
+ } catch (OperationCanceledException | AuthenticatorException | IOException e) { |
+ // Can never happen - artifact of Mockito. |
+ fail(); |
+ } |
+ return accountManagerFuture; |
+ } |
+ |
+ /** |
+ * Returns a future that fails with the provided exception when trying to get its result. |
+ * Hides mocking related annoyances: compiler warnings and irrelevant catch clauses. |
+ */ |
+ private <T> AccountManagerFuture<T> makeFuture(Exception ex) { |
+ // Avoid warning when creating mock accountManagerFuture, can't take .class of an |
+ // instantiated generic type, yet compiler complains if I leave it uninstantiated. |
+ @SuppressWarnings("unchecked") |
+ AccountManagerFuture<T> accountManagerFuture = mock(AccountManagerFuture.class); |
+ try { |
+ when(accountManagerFuture.getResult()).thenThrow(ex); |
+ } catch (OperationCanceledException | AuthenticatorException | IOException e) { |
+ // Can never happen - artifact of Mockito. |
+ fail(); |
+ } |
+ return accountManagerFuture; |
+ } |
+ |
+ /** |
+ * Returns a new authenticator as a spy so that we can override and intercept the native method |
+ * calls. |
+ */ |
+ private HttpNegotiateAuthenticator createWithoutNative(String accountType) { |
+ HttpNegotiateAuthenticator authenticator = |
+ spy(HttpNegotiateAuthenticator.create(accountType)); |
+ doNothing().when(authenticator).nativeSetResult(anyLong(), anyInt(), anyString()); |
+ doReturn(true).when(authenticator).hasPermission(any(Context.class), anyString()); |
+ return authenticator; |
+ } |
} |