OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.net; |
| 6 |
| 7 import static org.hamcrest.CoreMatchers.equalTo; |
| 8 import static org.hamcrest.CoreMatchers.notNullValue; |
| 9 import static org.hamcrest.CoreMatchers.nullValue; |
| 10 import static org.junit.Assert.assertThat; |
| 11 import static org.junit.Assert.fail; |
| 12 import static org.mockito.Matchers.anyInt; |
| 13 import static org.mockito.Matchers.anyLong; |
| 14 import static org.mockito.Matchers.anyString; |
| 15 import static org.mockito.Mockito.doNothing; |
| 16 import static org.mockito.Mockito.eq; |
| 17 import static org.mockito.Mockito.mock; |
| 18 import static org.mockito.Mockito.spy; |
| 19 import static org.mockito.Mockito.verify; |
| 20 import static org.mockito.Mockito.when; |
| 21 |
| 22 import android.accounts.AccountManager; |
| 23 import android.accounts.AccountManagerCallback; |
| 24 import android.accounts.AccountManagerFuture; |
| 25 import android.accounts.AuthenticatorException; |
| 26 import android.accounts.OperationCanceledException; |
| 27 import android.app.Activity; |
| 28 import android.os.Bundle; |
| 29 import android.os.Handler; |
| 30 |
| 31 import org.chromium.base.BaseChromiumApplication; |
| 32 import org.chromium.testing.local.LocalRobolectricTestRunner; |
| 33 import org.junit.Test; |
| 34 import org.junit.runner.RunWith; |
| 35 import org.robolectric.Robolectric; |
| 36 import org.robolectric.annotation.Config; |
| 37 import org.robolectric.annotation.Implementation; |
| 38 import org.robolectric.annotation.Implements; |
| 39 import org.robolectric.shadows.ShadowAccountManager; |
| 40 |
| 41 import java.io.IOException; |
| 42 |
| 43 /** |
| 44 * Robolectric tests for HttpNegotiateAuthenticator |
| 45 */ |
| 46 @RunWith(LocalRobolectricTestRunner.class) |
| 47 @Config(manifest = Config.NONE, |
| 48 shadows = HttpNegotiateAuthenticatorTest.ExtendedShadowAccountManager.cl
ass, |
| 49 application = BaseChromiumApplication.class) |
| 50 public class HttpNegotiateAuthenticatorTest { |
| 51 static int sCallCount = 0; |
| 52 static String sAccountTypeReceived; |
| 53 static String sAuthTokenTypeReceived; |
| 54 static String sFeaturesReceived[]; |
| 55 static Bundle sAddAccountOptionsReceived; |
| 56 static Bundle sAuthTokenOptionsReceived; |
| 57 static AccountManagerCallback<Bundle> sCallbackReceived; |
| 58 static Handler sHandlerReceived; |
| 59 |
| 60 /** |
| 61 * Robolectic's ShadowAccountManager doesn't implement getAccountsByTypeAndF
eature so extend it. |
| 62 * We simply check the call is correct, and don't try to emulate it Note: Sh
adow classes need to |
| 63 * be public and static. |
| 64 */ |
| 65 @Implements(AccountManager.class) |
| 66 public static class ExtendedShadowAccountManager extends ShadowAccountManage
r { |
| 67 @Implementation |
| 68 public AccountManagerFuture<Bundle> getAuthTokenByFeatures(String accoun
tType, |
| 69 String authTokenType, String[] features, Activity activity, |
| 70 Bundle addAccountOptions, Bundle getAuthTokenOptions, |
| 71 AccountManagerCallback<Bundle> callback, Handler handler) { |
| 72 sCallCount++; |
| 73 sAccountTypeReceived = accountType; |
| 74 sAuthTokenTypeReceived = authTokenType; |
| 75 sFeaturesReceived = features; |
| 76 sAddAccountOptionsReceived = addAccountOptions; |
| 77 sAuthTokenOptionsReceived = getAuthTokenOptions; |
| 78 sCallbackReceived = callback; |
| 79 sHandlerReceived = handler; |
| 80 |
| 81 return null; |
| 82 } |
| 83 } |
| 84 |
| 85 /** |
| 86 * Test of {@link HttpNegotiateAuthenticator#getNextAuthToken} |
| 87 */ |
| 88 @Test |
| 89 public void testGetNextAuthToken() { |
| 90 HttpNegotiateAuthenticator authenticator = |
| 91 HttpNegotiateAuthenticator.create("Dummy_Account"); |
| 92 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 93 authenticator.getNextAuthToken(0, "test_principal", "", true); |
| 94 assertThat("getAuthTokenByFeatures called precisely once", sCallCount, e
qualTo(1)); |
| 95 assertThat("Received account type matches input", sAccountTypeReceived, |
| 96 equalTo("Dummy_Account")); |
| 97 assertThat("AuthTokenType is \"SPNEGO:HOSTBASED:test_principal\"", sAuth
TokenTypeReceived, |
| 98 equalTo("SPNEGO:HOSTBASED:test_principal")); |
| 99 assertThat("Features are precisely {\"SPNEGO\"}", sFeaturesReceived, |
| 100 equalTo(new String[] {"SPNEGO"})); |
| 101 assertThat("No account options requested", sAddAccountOptionsReceived, n
ullValue()); |
| 102 assertThat("There is no existing context", |
| 103 sAuthTokenOptionsReceived.get(HttpNegotiateConstants.KEY_SPNEGO_
CONTEXT), |
| 104 nullValue()); |
| 105 assertThat("The existing token is empty", |
| 106 sAuthTokenOptionsReceived.getString(HttpNegotiateConstants.KEY_I
NCOMING_AUTH_TOKEN), |
| 107 equalTo("")); |
| 108 assertThat("Delegation is allowed", |
| 109 sAuthTokenOptionsReceived.getBoolean(HttpNegotiateConstants.KEY_
CAN_DELEGATE), |
| 110 equalTo(true)); |
| 111 assertThat("getAuthTokenByFeatures was called with a callback", sCallbac
kReceived, |
| 112 notNullValue()); |
| 113 assertThat("getAuthTokenByFeatures was called with a handler", sHandlerR
eceived, |
| 114 notNullValue()); |
| 115 } |
| 116 |
| 117 /** |
| 118 * Test of callback called when getting the auth token completes. |
| 119 */ |
| 120 @Test |
| 121 public void testAccountManagerCallbackRun() { |
| 122 // Spy on the authenticator so that we can override and intercept the na
tive method call. |
| 123 HttpNegotiateAuthenticator authenticator = |
| 124 spy(HttpNegotiateAuthenticator.create("Dummy_Account")); |
| 125 doNothing().when(authenticator).nativeSetResult(anyLong(), anyInt(), any
String()); |
| 126 |
| 127 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 128 |
| 129 // Call getNextAuthToken to get the callback |
| 130 authenticator.getNextAuthToken(1234, "test_principal", "", true); |
| 131 |
| 132 // Avoid warning when creating mock accountManagerFuture, can't take .cl
ass of an |
| 133 // instantiated generic type, yet compiler complains if I leave it unins
tantiated. |
| 134 @SuppressWarnings("unchecked") |
| 135 AccountManagerFuture<Bundle> accountManagerFuture = mock(AccountManagerF
uture.class); |
| 136 Bundle resultBundle = new Bundle(); |
| 137 Bundle context = new Bundle(); |
| 138 context.putString("String", "test_context"); |
| 139 resultBundle.putInt(HttpNegotiateConstants.KEY_SPNEGO_RESULT, HttpNegoti
ateConstants.OK); |
| 140 resultBundle.putBundle(HttpNegotiateConstants.KEY_SPNEGO_CONTEXT, contex
t); |
| 141 resultBundle.putString(AccountManager.KEY_AUTHTOKEN, "output_token"); |
| 142 try { |
| 143 when(accountManagerFuture.getResult()).thenReturn(resultBundle); |
| 144 } catch (OperationCanceledException | AuthenticatorException | IOExcepti
on e) { |
| 145 // Can never happen - artifact of Mockito. |
| 146 fail(); |
| 147 } |
| 148 sCallbackReceived.run(accountManagerFuture); |
| 149 verify(authenticator).nativeSetResult(1234, 0, "output_token"); |
| 150 |
| 151 // Check that the next call to getNextAuthToken uses the correct context |
| 152 authenticator.getNextAuthToken(5678, "test_principal", "", true); |
| 153 assertThat("The spnego context is preserved between calls", |
| 154 sAuthTokenOptionsReceived.getBundle(HttpNegotiateConstants.KEY_S
PNEGO_CONTEXT), |
| 155 equalTo(context)); |
| 156 |
| 157 // Test exception path |
| 158 try { |
| 159 when(accountManagerFuture.getResult()).thenThrow(new OperationCancel
edException()); |
| 160 } catch (OperationCanceledException | AuthenticatorException | IOExcepti
on e) { |
| 161 // Can never happen - artifact of Mockito. |
| 162 fail(); |
| 163 } |
| 164 sCallbackReceived.run(accountManagerFuture); |
| 165 verify(authenticator).nativeSetResult(5678, NetError.ERR_ABORTED, null); |
| 166 } |
| 167 |
| 168 private void checkErrorReturn(Integer spnegoError, int expectedError) { |
| 169 // Spy on the authenticator so that we can override and intercept the na
tive method call. |
| 170 HttpNegotiateAuthenticator authenticator = |
| 171 spy(HttpNegotiateAuthenticator.create("Dummy_Account")); |
| 172 doNothing().when(authenticator).nativeSetResult(anyLong(), anyInt(), any
String()); |
| 173 |
| 174 Robolectric.buildActivity(Activity.class).create().start().resume().visi
ble(); |
| 175 |
| 176 // Call getNextAuthToken to get the callback |
| 177 authenticator.getNextAuthToken(1234, "test_principal", "", true); |
| 178 |
| 179 // Avoid warning when creating mock accountManagerFuture, can't take .cl
ass of an |
| 180 // instantiated generic type, yet compiler complains if I leave it unins
tantiated. |
| 181 @SuppressWarnings("unchecked") |
| 182 AccountManagerFuture<Bundle> accountManagerFuture = mock(AccountManagerF
uture.class); |
| 183 Bundle resultBundle = new Bundle(); |
| 184 if (spnegoError != null) { |
| 185 resultBundle.putInt(HttpNegotiateConstants.KEY_SPNEGO_RESULT, spnego
Error); |
| 186 } |
| 187 try { |
| 188 when(accountManagerFuture.getResult()).thenReturn(resultBundle); |
| 189 } catch (OperationCanceledException | AuthenticatorException | IOExcepti
on e) { |
| 190 // Can never happen - artifact of Mockito. |
| 191 fail(); |
| 192 } |
| 193 sCallbackReceived.run(accountManagerFuture); |
| 194 verify(authenticator).nativeSetResult(anyLong(), eq(expectedError), anyS
tring()); |
| 195 } |
| 196 |
| 197 /** |
| 198 * Test of callback error returns when getting the auth token completes. |
| 199 */ |
| 200 @Test |
| 201 public void testAccountManagerCallbackErrorReturns() { |
| 202 checkErrorReturn(null, NetError.ERR_UNEXPECTED); |
| 203 checkErrorReturn(HttpNegotiateConstants.ERR_UNEXPECTED, NetError.ERR_UNE
XPECTED); |
| 204 checkErrorReturn(HttpNegotiateConstants.ERR_ABORTED, NetError.ERR_ABORTE
D); |
| 205 checkErrorReturn(HttpNegotiateConstants.ERR_UNEXPECTED_SECURITY_LIBRARY_
STATUS, |
| 206 NetError.ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS); |
| 207 checkErrorReturn( |
| 208 HttpNegotiateConstants.ERR_INVALID_RESPONSE, NetError.ERR_INVALI
D_RESPONSE); |
| 209 checkErrorReturn(HttpNegotiateConstants.ERR_INVALID_AUTH_CREDENTIALS, |
| 210 NetError.ERR_INVALID_AUTH_CREDENTIALS); |
| 211 checkErrorReturn(HttpNegotiateConstants.ERR_UNSUPPORTED_AUTH_SCHEME, |
| 212 NetError.ERR_UNSUPPORTED_AUTH_SCHEME); |
| 213 checkErrorReturn(HttpNegotiateConstants.ERR_MISSING_AUTH_CREDENTIALS, |
| 214 NetError.ERR_MISSING_AUTH_CREDENTIALS); |
| 215 checkErrorReturn(HttpNegotiateConstants.ERR_UNDOCUMENTED_SECURITY_LIBRAR
Y_STATUS, |
| 216 NetError.ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS); |
| 217 checkErrorReturn( |
| 218 HttpNegotiateConstants.ERR_MALFORMED_IDENTITY, NetError.ERR_MALF
ORMED_IDENTITY); |
| 219 // 9999 is not a valid return value |
| 220 checkErrorReturn(9999, NetError.ERR_UNEXPECTED); |
| 221 } |
| 222 } |
OLD | NEW |