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 |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3e3f9d511e15b5ccffad5bdfde0b757f3fdacec0 |
--- /dev/null |
+++ b/net/android/junit/src/org/chromium/net/HttpNegotiateAuthenticatorTest.java |
@@ -0,0 +1,168 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.net; |
+ |
+import static org.hamcrest.CoreMatchers.equalTo; |
+import static org.hamcrest.CoreMatchers.notNullValue; |
+import static org.hamcrest.CoreMatchers.nullValue; |
+import static org.junit.Assert.assertThat; |
+import static org.junit.Assert.fail; |
+import static org.mockito.Matchers.anyInt; |
+import static org.mockito.Matchers.anyLong; |
+import static org.mockito.Matchers.anyString; |
+import static org.mockito.Mockito.doNothing; |
+import static org.mockito.Mockito.mock; |
+import static org.mockito.Mockito.spy; |
+import static org.mockito.Mockito.verify; |
+import static org.mockito.Mockito.when; |
+ |
+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.os.Bundle; |
+import android.os.Handler; |
+ |
+import org.chromium.base.BaseChromiumApplication; |
+import org.chromium.testing.local.LocalRobolectricTestRunner; |
+import org.junit.Test; |
+import org.junit.runner.RunWith; |
+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 java.io.IOException; |
+ |
+/** |
+ * Robolectic tests for HttpNegotiateAuthenticator |
cbentzel
2015/06/30 12:53:55
Nit: Robolectric
aberent
2015/07/02 21:13:35
Done.
|
+ */ |
+@RunWith(LocalRobolectricTestRunner.class) |
+@Config(manifest = Config.NONE, |
+ shadows = HttpNegotiateAuthenticatorTest.ExtendedShadowAccountManager.class, |
+ application = BaseChromiumApplication.class) |
+public class HttpNegotiateAuthenticatorTest { |
+ static final String KEY_INCOMING_AUTH_TOKEN = "incomingAuthToken"; |
+ static final String KEY_SPNEGO_CONTEXT = "spnegoContext"; |
+ static final String KEY_CAN_DELEGATE = "canDelegate"; |
+ |
+ static final String SPNEGO_FEATURE = "SPNEGO"; |
+ static final String SPNEGO_TOKEN_TYPE_BASE = "SPNEGO:HOSTBASED:"; |
+ |
+ static int sCallCount = 0; |
+ static String sAccountTypeReceived; |
+ static String sAuthTokenTypeReceived; |
+ static String sFeaturesReceived[]; |
+ static Bundle sAddAccountOptionsReceived; |
+ static Bundle sAuthTokenOptionsReceived; |
+ static AccountManagerCallback<Bundle> sCallbackReceived; |
+ static Handler sHandlerReceived; |
+ |
+ /** |
+ * Robolectic's ShadowAccountManager doesn't implement getAccountsByTypeAndFeature so extend it. |
+ * We simply check the call is correct, and don't try to emulate it |
+ * 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) { |
+ sCallCount++; |
+ sAccountTypeReceived = accountType; |
+ sAuthTokenTypeReceived = authTokenType; |
+ sFeaturesReceived = features; |
+ sAddAccountOptionsReceived = addAccountOptions; |
+ sAuthTokenOptionsReceived = getAuthTokenOptions; |
+ sCallbackReceived = callback; |
+ sHandlerReceived = handler; |
+ |
+ return null; |
+ } |
+ } |
+ |
+ /** |
+ * Test of {@link HttpNegotiateAuthenticator#getNextAuthToken} |
+ */ |
+ @Test |
+ public void testGetNextAuthToken() { |
+ HttpNegotiateAuthenticator authenticator = |
+ HttpNegotiateAuthenticator.create("Dummy_Account"); |
+ Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
+ authenticator.getNextAuthToken(0, "test_principal", "", true); |
+ assertThat("getAuthTokenByFeatures called precisely once", sCallCount, equalTo(1)); |
+ assertThat("Received account type matches input", sAccountTypeReceived, |
+ equalTo("Dummy_Account")); |
+ assertThat("AuthTokenType is \"SPNEGO:HOSTBASED:test_principal\"", sAuthTokenTypeReceived, |
+ equalTo("SPNEGO:HOSTBASED:test_principal")); |
+ assertThat("Features are precisely {\"SPNEGO\"}", sFeaturesReceived, |
+ equalTo(new String[] {"SPNEGO"})); |
+ assertThat("No account options requested", sAddAccountOptionsReceived, nullValue()); |
+ assertThat("There is no existing context", |
+ sAuthTokenOptionsReceived.get(KEY_SPNEGO_CONTEXT), nullValue()); |
+ assertThat("The existing token is empty", |
+ sAuthTokenOptionsReceived.getString(KEY_INCOMING_AUTH_TOKEN), equalTo("")); |
+ assertThat("Delegation is allowed", sAuthTokenOptionsReceived.getBoolean(KEY_CAN_DELEGATE), |
+ equalTo(true)); |
+ assertThat("getAuthTokenByFeatures was called with a callback", sCallbackReceived, |
+ notNullValue()); |
+ assertThat("getAuthTokenByFeatures was called with a handler", sHandlerReceived, |
+ notNullValue()); |
+ } |
+ |
+ /** |
+ * Test of callback called when getting the auth token completes. |
+ */ |
+ @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()); |
+ |
+ Robolectric.buildActivity(Activity.class).create().start().resume().visible(); |
+ |
+ // Call getNextAuthToken to get the callback |
+ authenticator.getNextAuthToken(1234, "test_principal", "", true); |
+ |
+ // 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.putBundle(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(); |
+ } |
+ sCallbackReceived.run(accountManagerFuture); |
+ verify(authenticator).nativeSetResult(1234, 0, "output_token"); |
+ |
+ // Check that the next call to getNextAuthToken uses the correct context |
+ authenticator.getNextAuthToken(5678, "test_principal", "", true); |
+ assertThat("The spnego context is preserved between calls", |
+ sAuthTokenOptionsReceived.getBundle(KEY_SPNEGO_CONTEXT), equalTo(context)); |
+ |
+ // Test exception path |
cbentzel
2015/06/30 12:53:55
Should you also test the non-ABORTED error cases?
aberent
2015/07/02 21:13:35
Done.
|
+ try { |
+ when(accountManagerFuture.getResult()).thenThrow(new OperationCanceledException()); |
+ } catch (OperationCanceledException | AuthenticatorException | IOException e) { |
+ // Can never happen - artifact of Mockito. |
+ fail(); |
+ } |
+ sCallbackReceived.run(accountManagerFuture); |
+ verify(authenticator).nativeSetResult(5678, NetError.ERR_ABORTED, null); |
+ } |
+} |