1// Copyright (c) 2011 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/memory/ref_counted.h"
8#include "base/message_loop.h"
9#include "chrome/browser/chromeos/cros/mock_library_loader.h"
10#include "chrome/browser/chromeos/login/auth_attempt_state.h"
11#include "chrome/browser/chromeos/login/online_attempt.h"
12#include "chrome/browser/chromeos/login/mock_auth_attempt_state_resolver.h"
13#include "chrome/browser/chromeos/login/mock_url_fetchers.h"
14#include "chrome/browser/chromeos/login/test_attempt_state.h"
15#include "chrome/common/net/gaia/gaia_auth_consumer.h"
16#include "chrome/common/net/gaia/gaia_auth_fetcher_unittest.h"
17#include "chrome/test/testing_profile.h"
18#include "content/browser/browser_thread.h"
19#include "googleurl/src/gurl.h"
20#include "testing/gmock/include/gmock/gmock.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23using ::testing::AnyNumber;
24using ::testing::Invoke;
25using ::testing::Return;
26using ::testing::_;
27
28namespace chromeos {
29
30class OnlineAttemptTest : public ::testing::Test {
31 public:
32  OnlineAttemptTest()
33      : message_loop_(MessageLoop::TYPE_UI),
34        ui_thread_(BrowserThread::UI, &message_loop_),
35        io_thread_(BrowserThread::IO),
36        state_("", "", "", "", "", false),
37        resolver_(new MockAuthAttemptStateResolver) {
38  }
39
40  virtual ~OnlineAttemptTest() {}
41
42  virtual void SetUp() {
43    CrosLibrary::TestApi* test_api = CrosLibrary::Get()->GetTestApi();
44
45    MockLibraryLoader* loader = new MockLibraryLoader();
46    ON_CALL(*loader, Load(_))
47        .WillByDefault(Return(true));
48    EXPECT_CALL(*loader, Load(_))
49        .Times(AnyNumber());
50
51    // Passes ownership of |loader| to CrosLibrary.
52    test_api->SetLibraryLoader(loader, true);
53
54    attempt_ = new OnlineAttempt(&state_, resolver_.get());
55
56    io_thread_.Start();
57  }
58
59  virtual void TearDown() {
60    // Prevent bogus gMock leak check from firing.
61    chromeos::CrosLibrary::TestApi* test_api =
62        chromeos::CrosLibrary::Get()->GetTestApi();
63    test_api->SetLibraryLoader(NULL, false);
64  }
65
66  void RunFailureTest(const GoogleServiceAuthError& error) {
67    EXPECT_CALL(*(resolver_.get()), Resolve())
68        .Times(1)
69        .RetiresOnSaturation();
70
71    BrowserThread::PostTask(
72        BrowserThread::IO, FROM_HERE,
73        NewRunnableMethod(attempt_.get(),
74                          &OnlineAttempt::OnClientLoginFailure,
75                          error));
76    // Force IO thread to finish tasks so I can verify |state_|.
77    io_thread_.Stop();
78    EXPECT_TRUE(error == state_.online_outcome().error());
79  }
80
81  void CancelLogin(OnlineAttempt* auth) {
82    BrowserThread::PostTask(
83        BrowserThread::IO,
84        FROM_HERE,
85        NewRunnableMethod(auth,
86                          &OnlineAttempt::CancelClientLogin));
87  }
88
89  static void Quit() {
90    BrowserThread::PostTask(
91        BrowserThread::UI, FROM_HERE, new MessageLoop::QuitTask());
92  }
93
94  static void RunThreadTest() {
95    MessageLoop::current()->RunAllPending();
96  }
97
98  MessageLoop message_loop_;
99  BrowserThread ui_thread_;
100  BrowserThread io_thread_;
101  TestAttemptState state_;
102  scoped_ptr<MockAuthAttemptStateResolver> resolver_;
103  scoped_refptr<OnlineAttempt> attempt_;
104};
105
106TEST_F(OnlineAttemptTest, LoginSuccess) {
107  GaiaAuthConsumer::ClientLoginResult result;
108  EXPECT_CALL(*(resolver_.get()), Resolve())
109      .Times(1)
110      .RetiresOnSaturation();
111
112  BrowserThread::PostTask(
113      BrowserThread::IO, FROM_HERE,
114      NewRunnableMethod(attempt_.get(),
115                        &OnlineAttempt::OnClientLoginSuccess,
116                        result));
117  // Force IO thread to finish tasks so I can verify |state_|.
118  io_thread_.Stop();
119  EXPECT_TRUE(result == state_.credentials());
120}
121
122TEST_F(OnlineAttemptTest, LoginCancelRetry) {
123  GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
124  TestingProfile profile;
125
126  EXPECT_CALL(*(resolver_.get()), Resolve())
127      .WillOnce(Invoke(OnlineAttemptTest::Quit))
128      .RetiresOnSaturation();
129
130  // This is how we inject fake URLFetcher objects, with a factory.
131  // This factory creates fake URLFetchers that Start() a fake fetch attempt
132  // and then come back on the IO thread saying they've been canceled.
133  MockFactory<GotCanceledFetcher> factory;
134  URLFetcher::set_factory(&factory);
135
136  attempt_->Initiate(&profile);
137  BrowserThread::PostTask(
138      BrowserThread::IO, FROM_HERE,
139      NewRunnableFunction(&OnlineAttemptTest::RunThreadTest));
140
141  MessageLoop::current()->Run();
142
143  EXPECT_TRUE(error == state_.online_outcome().error());
144  EXPECT_EQ(LoginFailure::NETWORK_AUTH_FAILED,
145            state_.online_outcome().reason());
146  URLFetcher::set_factory(NULL);
147}
148
149TEST_F(OnlineAttemptTest, LoginTimeout) {
150  LoginFailure error(LoginFailure::LOGIN_TIMED_OUT);
151  TestingProfile profile;
152
153  EXPECT_CALL(*(resolver_.get()), Resolve())
154      .WillOnce(Invoke(OnlineAttemptTest::Quit))
155      .RetiresOnSaturation();
156
157  // This is how we inject fake URLFetcher objects, with a factory.
158  // This factory creates fake URLFetchers that Start() a fake fetch attempt
159  // and then come back on the IO thread saying they've been canceled.
160  MockFactory<ExpectCanceledFetcher> factory;
161  URLFetcher::set_factory(&factory);
162
163  attempt_->Initiate(&profile);
164  BrowserThread::PostTask(
165      BrowserThread::IO, FROM_HERE,
166      NewRunnableFunction(&OnlineAttemptTest::RunThreadTest));
167
168  // Post a task to cancel the login attempt.
169  CancelLogin(attempt_.get());
170
171  MessageLoop::current()->Run();
172
173  EXPECT_EQ(LoginFailure::LOGIN_TIMED_OUT, state_.online_outcome().reason());
174  URLFetcher::set_factory(NULL);
175}
176
177TEST_F(OnlineAttemptTest, HostedLoginRejected) {
178  LoginFailure error(
179      LoginFailure::FromNetworkAuthFailure(
180          GoogleServiceAuthError(
181              GoogleServiceAuthError::HOSTED_NOT_ALLOWED)));
182  TestingProfile profile;
183
184  EXPECT_CALL(*(resolver_.get()), Resolve())
185      .WillOnce(Invoke(OnlineAttemptTest::Quit))
186      .RetiresOnSaturation();
187
188  // This is how we inject fake URLFetcher objects, with a factory.
189  MockFactory<HostedFetcher> factory;
190  URLFetcher::set_factory(&factory);
191
192  TestAttemptState local_state("", "", "", "", "", true);
193  attempt_ = new OnlineAttempt(&local_state, resolver_.get());
194  attempt_->Initiate(&profile);
195  BrowserThread::PostTask(
196      BrowserThread::IO, FROM_HERE,
197      NewRunnableFunction(&OnlineAttemptTest::RunThreadTest));
198
199  MessageLoop::current()->Run();
200
201  EXPECT_EQ(error, local_state.online_outcome());
202  EXPECT_EQ(LoginFailure::NETWORK_AUTH_FAILED,
203            local_state.online_outcome().reason());
204  URLFetcher::set_factory(NULL);
205}
206
207TEST_F(OnlineAttemptTest, FullLogin) {
208  TestingProfile profile;
209
210  EXPECT_CALL(*(resolver_.get()), Resolve())
211      .WillOnce(Invoke(OnlineAttemptTest::Quit))
212      .RetiresOnSaturation();
213
214  // This is how we inject fake URLFetcher objects, with a factory.
215  MockFactory<SuccessFetcher> factory;
216  URLFetcher::set_factory(&factory);
217
218  TestAttemptState local_state("", "", "", "", "", true);
219  attempt_ = new OnlineAttempt(&local_state, resolver_.get());
220  attempt_->Initiate(&profile);
221  BrowserThread::PostTask(
222      BrowserThread::IO, FROM_HERE,
223      NewRunnableFunction(&OnlineAttemptTest::RunThreadTest));
224
225  MessageLoop::current()->Run();
226
227  EXPECT_EQ(LoginFailure::None(), local_state.online_outcome());
228  URLFetcher::set_factory(NULL);
229}
230
231TEST_F(OnlineAttemptTest, LoginNetFailure) {
232  RunFailureTest(
233      GoogleServiceAuthError::FromConnectionError(net::ERR_CONNECTION_RESET));
234}
235
236TEST_F(OnlineAttemptTest, LoginDenied) {
237  RunFailureTest(
238      GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
239}
240
241TEST_F(OnlineAttemptTest, LoginAccountDisabled) {
242  RunFailureTest(
243      GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED));
244}
245
246TEST_F(OnlineAttemptTest, LoginAccountDeleted) {
247  RunFailureTest(
248      GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DELETED));
249}
250
251TEST_F(OnlineAttemptTest, LoginServiceUnavailable) {
252  RunFailureTest(
253      GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
254}
255
256TEST_F(OnlineAttemptTest, CaptchaErrorOutputted) {
257  GoogleServiceAuthError auth_error =
258      GoogleServiceAuthError::FromCaptchaChallenge(
259          "CCTOKEN",
260          GURL("http://www.google.com/accounts/Captcha?ctoken=CCTOKEN"),
261          GURL("http://www.google.com/login/captcha"));
262  RunFailureTest(auth_error);
263}
264
265TEST_F(OnlineAttemptTest, TwoFactorSuccess) {
266  EXPECT_CALL(*(resolver_.get()), Resolve())
267      .Times(1)
268      .RetiresOnSaturation();
269  GoogleServiceAuthError error(GoogleServiceAuthError::TWO_FACTOR);
270  BrowserThread::PostTask(
271      BrowserThread::IO, FROM_HERE,
272      NewRunnableMethod(attempt_.get(),
273                        &OnlineAttempt::OnClientLoginFailure,
274                        error));
275
276  // Force IO thread to finish tasks so I can verify |state_|.
277  io_thread_.Stop();
278  EXPECT_TRUE(GoogleServiceAuthError::None() ==
279              state_.online_outcome().error());
280  EXPECT_TRUE(GaiaAuthConsumer::ClientLoginResult() == state_.credentials());
281}
282
283}  // namespace chromeos
284