1// Copyright 2014 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#include <string> 6 7#include "base/bind.h" 8#include "base/location.h" 9#include "base/memory/ref_counted.h" 10#include "base/run_loop.h" 11#include "base/test/null_task_runner.h" 12#include "chrome/test/base/testing_profile.h" 13#include "chromeos/login/auth/auth_attempt_state.h" 14#include "chromeos/login/auth/mock_auth_attempt_state_resolver.h" 15#include "chromeos/login/auth/mock_url_fetchers.h" 16#include "chromeos/login/auth/online_attempt.h" 17#include "chromeos/login/auth/test_attempt_state.h" 18#include "chromeos/login/auth/user_context.h" 19#include "google_apis/gaia/gaia_auth_consumer.h" 20#include "google_apis/gaia/mock_url_fetcher_factory.h" 21#include "net/url_request/url_request_context.h" 22#include "net/url_request/url_request_context_getter.h" 23#include "net/url_request/url_request_test_util.h" 24#include "testing/gmock/include/gmock/gmock.h" 25#include "testing/gtest/include/gtest/gtest.h" 26#include "url/gurl.h" 27 28using ::testing::AnyNumber; 29using ::testing::Invoke; 30using ::testing::Return; 31using ::testing::_; 32 33namespace { 34 35class TestContextURLRequestContextGetter : public net::URLRequestContextGetter { 36 public: 37 TestContextURLRequestContextGetter() 38 : null_task_runner_(new base::NullTaskRunner) {} 39 40 virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE { 41 return &context_; 42 } 43 44 virtual scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() 45 const OVERRIDE { 46 return null_task_runner_; 47 } 48 49 private: 50 virtual ~TestContextURLRequestContextGetter() {} 51 52 net::TestURLRequestContext context_; 53 scoped_refptr<base::SingleThreadTaskRunner> null_task_runner_; 54}; 55 56} // namespace 57 58namespace chromeos { 59 60class OnlineAttemptTest : public testing::Test { 61 public: 62 OnlineAttemptTest() 63 : state_(UserContext(), false), 64 attempt_(new OnlineAttempt(&state_, &resolver_)) {} 65 66 virtual void SetUp() OVERRIDE { 67 message_loop_ = base::MessageLoopProxy::current(); 68 request_context_ = new TestContextURLRequestContextGetter(); 69 } 70 71 void RunFailureTest(const GoogleServiceAuthError& error) { 72 EXPECT_CALL(resolver_, Resolve()).Times(1).RetiresOnSaturation(); 73 74 message_loop_->PostTask(FROM_HERE, 75 base::Bind(&OnlineAttempt::OnClientLoginFailure, 76 attempt_->weak_factory_.GetWeakPtr(), 77 error)); 78 // Force UI thread to finish tasks so I can verify |state_|. 79 base::RunLoop().RunUntilIdle(); 80 EXPECT_TRUE(error == state_.online_outcome().error()); 81 } 82 83 void CancelLogin(OnlineAttempt* auth) { 84 message_loop_->PostTask(FROM_HERE, 85 base::Bind(&OnlineAttempt::CancelClientLogin, 86 auth->weak_factory_.GetWeakPtr())); 87 } 88 89 scoped_refptr<base::MessageLoopProxy> message_loop_; 90 scoped_refptr<net::URLRequestContextGetter> request_context_; 91 base::MessageLoop loop_; 92 TestAttemptState state_; 93 MockAuthAttemptStateResolver resolver_; 94 scoped_ptr<OnlineAttempt> attempt_; 95}; 96 97TEST_F(OnlineAttemptTest, LoginSuccess) { 98 EXPECT_CALL(resolver_, Resolve()).Times(1).RetiresOnSaturation(); 99 100 message_loop_->PostTask(FROM_HERE, 101 base::Bind(&OnlineAttempt::OnClientLoginSuccess, 102 attempt_->weak_factory_.GetWeakPtr(), 103 GaiaAuthConsumer::ClientLoginResult())); 104 // Force UI thread to finish tasks so I can verify |state_|. 105 base::RunLoop().RunUntilIdle(); 106} 107 108TEST_F(OnlineAttemptTest, LoginCancelRetry) { 109 GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED); 110 TestingProfile profile; 111 112 base::RunLoop run_loop; 113 EXPECT_CALL(resolver_, Resolve()) 114 .WillOnce(Invoke(&run_loop, &base::RunLoop::Quit)) 115 .RetiresOnSaturation(); 116 117 // This is how we inject fake URLFetcher objects, with a factory. 118 // This factory creates fake URLFetchers that Start() a fake fetch attempt 119 // and then come back on the UI thread saying they've been canceled. 120 MockURLFetcherFactory<GotCanceledFetcher> factory; 121 122 attempt_->Initiate(request_context_.get()); 123 124 run_loop.Run(); 125 126 EXPECT_TRUE(error == state_.online_outcome().error()); 127 EXPECT_EQ(AuthFailure::NETWORK_AUTH_FAILED, state_.online_outcome().reason()); 128} 129 130TEST_F(OnlineAttemptTest, LoginTimeout) { 131 AuthFailure error(AuthFailure::LOGIN_TIMED_OUT); 132 TestingProfile profile; 133 134 base::RunLoop run_loop; 135 EXPECT_CALL(resolver_, Resolve()) 136 .WillOnce(Invoke(&run_loop, &base::RunLoop::Quit)) 137 .RetiresOnSaturation(); 138 139 // This is how we inject fake URLFetcher objects, with a factory. 140 // This factory creates fake URLFetchers that Start() a fake fetch attempt 141 // and then come back on the UI thread saying they've been canceled. 142 MockURLFetcherFactory<ExpectCanceledFetcher> factory; 143 144 attempt_->Initiate(request_context_.get()); 145 146 // Post a task to cancel the login attempt. 147 CancelLogin(attempt_.get()); 148 149 run_loop.Run(); 150 151 EXPECT_EQ(AuthFailure::LOGIN_TIMED_OUT, state_.online_outcome().reason()); 152} 153 154TEST_F(OnlineAttemptTest, HostedLoginRejected) { 155 AuthFailure error(AuthFailure::FromNetworkAuthFailure( 156 GoogleServiceAuthError(GoogleServiceAuthError::HOSTED_NOT_ALLOWED))); 157 TestingProfile profile; 158 159 base::RunLoop run_loop; 160 EXPECT_CALL(resolver_, Resolve()) 161 .WillOnce(Invoke(&run_loop, &base::RunLoop::Quit)) 162 .RetiresOnSaturation(); 163 164 // This is how we inject fake URLFetcher objects, with a factory. 165 MockURLFetcherFactory<HostedFetcher> factory; 166 167 TestAttemptState local_state(UserContext(), true); 168 attempt_.reset(new OnlineAttempt(&local_state, &resolver_)); 169 attempt_->Initiate(request_context_.get()); 170 171 run_loop.Run(); 172 173 EXPECT_EQ(error, local_state.online_outcome()); 174 EXPECT_EQ(AuthFailure::NETWORK_AUTH_FAILED, 175 local_state.online_outcome().reason()); 176} 177 178TEST_F(OnlineAttemptTest, FullLogin) { 179 TestingProfile profile; 180 181 base::RunLoop run_loop; 182 EXPECT_CALL(resolver_, Resolve()) 183 .WillOnce(Invoke(&run_loop, &base::RunLoop::Quit)) 184 .RetiresOnSaturation(); 185 186 // This is how we inject fake URLFetcher objects, with a factory. 187 MockURLFetcherFactory<SuccessFetcher> factory; 188 189 TestAttemptState local_state(UserContext(), true); 190 attempt_.reset(new OnlineAttempt(&local_state, &resolver_)); 191 attempt_->Initiate(request_context_.get()); 192 193 run_loop.Run(); 194 195 EXPECT_EQ(AuthFailure::AuthFailureNone(), local_state.online_outcome()); 196} 197 198TEST_F(OnlineAttemptTest, LoginNetFailure) { 199 RunFailureTest( 200 GoogleServiceAuthError::FromConnectionError(net::ERR_CONNECTION_RESET)); 201} 202 203TEST_F(OnlineAttemptTest, LoginDenied) { 204 RunFailureTest( 205 GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); 206} 207 208TEST_F(OnlineAttemptTest, LoginAccountDisabled) { 209 RunFailureTest( 210 GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED)); 211} 212 213TEST_F(OnlineAttemptTest, LoginAccountDeleted) { 214 RunFailureTest( 215 GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DELETED)); 216} 217 218TEST_F(OnlineAttemptTest, LoginServiceUnavailable) { 219 RunFailureTest( 220 GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE)); 221} 222 223TEST_F(OnlineAttemptTest, CaptchaErrorOutputted) { 224 GoogleServiceAuthError auth_error = 225 GoogleServiceAuthError::FromClientLoginCaptchaChallenge( 226 "CCTOKEN", 227 GURL("http://accounts.google.com/Captcha?ctoken=CCTOKEN"), 228 GURL("http://www.google.com/login/captcha")); 229 RunFailureTest(auth_error); 230} 231 232TEST_F(OnlineAttemptTest, TwoFactorSuccess) { 233 EXPECT_CALL(resolver_, Resolve()).Times(1).RetiresOnSaturation(); 234 GoogleServiceAuthError error(GoogleServiceAuthError::TWO_FACTOR); 235 message_loop_->PostTask(FROM_HERE, 236 base::Bind(&OnlineAttempt::OnClientLoginFailure, 237 attempt_->weak_factory_.GetWeakPtr(), 238 error)); 239 240 // Force UI thread to finish tasks so I can verify |state_|. 241 base::RunLoop().RunUntilIdle(); 242 EXPECT_TRUE(GoogleServiceAuthError::AuthErrorNone() == 243 state_.online_outcome().error()); 244} 245 246} // namespace chromeos 247