| 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;
|
| + }
|
| }
|
|
|