sync_setup_wizard_unittest.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2010 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 "chrome/browser/sync/sync_setup_wizard.h"
6
7#include "base/json/json_writer.h"
8#include "base/scoped_ptr.h"
9#include "base/stl_util-inl.h"
10#include "chrome/browser/browser.h"
11#include "chrome/browser/browser_list.h"
12#include "chrome/browser/google_service_auth_error.h"
13#include "chrome/browser/pref_service.h"
14#include "chrome/browser/sync/profile_sync_factory_mock.h"
15#include "chrome/browser/sync/profile_sync_service.h"
16#include "chrome/browser/sync/sync_setup_flow.h"
17#include "chrome/common/pref_names.h"
18#include "chrome/test/browser_with_test_window_test.h"
19#include "chrome/test/testing_profile.h"
20#include "chrome/test/test_browser_window.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23static const char kTestUser[] = "chrome.p13n.test@gmail.com";
24static const char kTestPassword[] = "passwd";
25static const char kTestCaptcha[] = "pizzamyheart";
26static const char kTestCaptchaUrl[] = "http://pizzamyheart/";
27
28typedef GoogleServiceAuthError AuthError;
29
30// A PSS subtype to inject.
31class ProfileSyncServiceForWizardTest : public ProfileSyncService {
32 public:
33  ProfileSyncServiceForWizardTest(ProfileSyncFactory* factory, Profile* profile)
34      : ProfileSyncService(factory, profile, false),
35        user_cancelled_dialog_(false) {
36    RegisterPreferences();
37    ResetTestStats();
38  }
39
40  virtual ~ProfileSyncServiceForWizardTest() { }
41
42  virtual void OnUserSubmittedAuth(const std::string& username,
43                                   const std::string& password,
44                                   const std::string& captcha) {
45    username_ = username;
46    password_ = password;
47    captcha_ = captcha;
48  }
49
50  virtual void OnUserChoseDatatypes(bool sync_everything,
51      const syncable::ModelTypeSet& chosen_types) {
52    user_chose_data_types_ = true;
53    chosen_data_types_ = chosen_types;
54  }
55
56  virtual void OnUserCancelledDialog() {
57    user_cancelled_dialog_ = true;
58  }
59
60  virtual string16 GetAuthenticatedUsername() const {
61    return UTF8ToUTF16(username_);
62  }
63
64  void set_auth_state(const std::string& last_email,
65                      const AuthError& error) {
66    last_attempted_user_email_ = last_email;
67    last_auth_error_ = error;
68  }
69
70  void ResetTestStats() {
71    username_.clear();
72    password_.clear();
73    captcha_.clear();
74    user_cancelled_dialog_ = false;
75    user_chose_data_types_ = false;
76    keep_everything_synced_ = false;
77    chosen_data_types_.clear();
78  }
79
80  std::string username_;
81  std::string password_;
82  std::string captcha_;
83  bool user_cancelled_dialog_;
84  bool user_chose_data_types_;
85  bool keep_everything_synced_;
86  syncable::ModelTypeSet chosen_data_types_;
87
88 private:
89  DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceForWizardTest);
90};
91
92class TestingProfileWithSyncService : public TestingProfile {
93 public:
94  TestingProfileWithSyncService() {
95    sync_service_.reset(new ProfileSyncServiceForWizardTest(&factory_, this));
96  }
97
98  virtual ProfileSyncService* GetProfileSyncService() {
99    return sync_service_.get();
100  }
101 private:
102  ProfileSyncFactoryMock factory_;
103  scoped_ptr<ProfileSyncService> sync_service_;
104};
105
106class TestBrowserWindowForWizardTest : public TestBrowserWindow {
107 public:
108  explicit TestBrowserWindowForWizardTest(Browser* browser)
109      : TestBrowserWindow(browser), flow_(NULL),
110        was_show_html_dialog_called_(false) {
111  }
112
113  virtual ~TestBrowserWindowForWizardTest() {
114    if (flow_.get()) {
115      // In real life, the handlers are destroyed by the DOMUI infrastructure,
116      // which calls GetDOMMessageHandlers to take ownership.  This does not
117      // exist in our test, so we perform cleanup manually.
118      std::vector<DOMMessageHandler*> handlers;
119      flow_->GetDOMMessageHandlers(&handlers);
120      // The handler contract is that they are valid for the lifetime of the
121      // HTMLDialogUIDelegate, but are cleaned up after the dialog is closed
122      // and/or deleted.
123      flow_.reset();
124      STLDeleteElements(&handlers);
125    }
126  }
127
128  // We intercept this call to hijack the flow created and then use it to
129  // drive the wizard as if we were the HTML page.
130  virtual void ShowHTMLDialog(HtmlDialogUIDelegate* delegate,
131      gfx::NativeWindow parent_window) {
132    flow_.reset(static_cast<SyncSetupFlow*>(delegate));
133    was_show_html_dialog_called_ = true;
134  }
135
136  bool TestAndResetWasShowHTMLDialogCalled() {
137    bool ret = was_show_html_dialog_called_;
138    was_show_html_dialog_called_ = false;
139    return ret;
140  }
141
142  // Simulates the user (or browser view hierarchy) closing the html dialog.
143  // Handles cleaning up the delegate and associated handlers.
144  void CloseDialog() {
145    if (flow_.get()) {
146      std::vector<DOMMessageHandler*> handlers;
147      flow_->GetDOMMessageHandlers(&handlers);
148      // The flow deletes itself here.  Don't use reset().
149      flow_.release()->OnDialogClosed("");
150      STLDeleteElements(&handlers);
151    }
152  }
153
154  SyncSetupFlow* flow() { return flow_.get(); }
155
156 private:
157  // In real life, this is owned by the view that is opened by the browser.  We
158  // mock all that out, so we need to take ownership so the flow doesn't leak.
159  scoped_ptr<SyncSetupFlow> flow_;
160
161  bool was_show_html_dialog_called_;
162};
163
164class SyncSetupWizardTest : public BrowserWithTestWindowTest {
165 public:
166  SyncSetupWizardTest()
167      : ui_thread_(ChromeThread::UI, MessageLoop::current()),
168        file_thread_(ChromeThread::FILE, MessageLoop::current()),
169        test_window_(NULL),
170        wizard_(NULL) { }
171  virtual ~SyncSetupWizardTest() { }
172  virtual void SetUp() {
173    set_profile(new TestingProfileWithSyncService());
174    profile()->CreateBookmarkModel(false);
175    // Wait for the bookmarks model to load.
176    profile()->BlockUntilBookmarkModelLoaded();
177    set_browser(new Browser(Browser::TYPE_NORMAL, profile()));
178    test_window_ = new TestBrowserWindowForWizardTest(browser());
179    set_window(test_window_);
180    browser()->set_window(window());
181    BrowserList::SetLastActive(browser());
182    service_ = static_cast<ProfileSyncServiceForWizardTest*>(
183        profile()->GetProfileSyncService());
184    wizard_.reset(new SyncSetupWizard(service_));
185  }
186
187  virtual void TearDown() {
188    test_window_ = NULL;
189    service_ = NULL;
190    wizard_.reset();
191  }
192
193  ChromeThread ui_thread_;
194  ChromeThread file_thread_;
195  TestBrowserWindowForWizardTest* test_window_;
196  scoped_ptr<SyncSetupWizard> wizard_;
197  ProfileSyncServiceForWizardTest* service_;
198};
199
200// See http://code.google.com/p/chromium/issues/detail?id=40715 for
201// why we skip the below tests on OS X.  We don't use DISABLED_ as we
202// would have to change the corresponding FRIEND_TEST() declarations.
203
204#if defined(OS_MACOSX)
205#define SKIP_TEST_ON_MACOSX() \
206  do { LOG(WARNING) << "Test skipped on OS X"; return; } while (0)
207#else
208#define SKIP_TEST_ON_MACOSX() do {} while (0)
209#endif
210
211TEST_F(SyncSetupWizardTest, InitialStepLogin) {
212  SKIP_TEST_ON_MACOSX();
213  DictionaryValue dialog_args;
214  SyncSetupFlow::GetArgsForGaiaLogin(service_, &dialog_args);
215  std::string json_start_args;
216  base::JSONWriter::Write(&dialog_args, false, &json_start_args);
217  ListValue credentials;
218  std::string auth = "{\"user\":\"";
219  auth += std::string(kTestUser) + "\",\"pass\":\"";
220  auth += std::string(kTestPassword) + "\",\"captcha\":\"";
221  auth += std::string(kTestCaptcha) + "\"}";
222  credentials.Append(new StringValue(auth));
223
224  EXPECT_FALSE(wizard_->IsVisible());
225  EXPECT_FALSE(test_window_->flow());
226  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
227
228  EXPECT_TRUE(wizard_->IsVisible());
229  EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
230  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
231  EXPECT_EQ(SyncSetupWizard::DONE, test_window_->flow()->end_state_);
232  EXPECT_EQ(json_start_args, test_window_->flow()->dialog_start_args_);
233
234  // Simulate the user submitting credentials.
235  test_window_->flow()->flow_handler_->HandleSubmitAuth(&credentials);
236  EXPECT_TRUE(wizard_->IsVisible());
237  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
238  EXPECT_EQ(kTestUser, service_->username_);
239  EXPECT_EQ(kTestPassword, service_->password_);
240  EXPECT_EQ(kTestCaptcha, service_->captcha_);
241  EXPECT_FALSE(service_->user_cancelled_dialog_);
242  service_->ResetTestStats();
243
244  // Simulate failed credentials.
245  AuthError invalid_gaia(AuthError::INVALID_GAIA_CREDENTIALS);
246  service_->set_auth_state(kTestUser, invalid_gaia);
247  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
248  EXPECT_TRUE(wizard_->IsVisible());
249  EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
250  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
251  dialog_args.Clear();
252  SyncSetupFlow::GetArgsForGaiaLogin(service_, &dialog_args);
253  EXPECT_EQ(4U, dialog_args.size());
254  std::string iframe_to_show;
255  dialog_args.GetString(L"iframeToShow", &iframe_to_show);
256  EXPECT_EQ("login", iframe_to_show);
257  std::string actual_user;
258  dialog_args.GetString(L"user", &actual_user);
259  EXPECT_EQ(kTestUser, actual_user);
260  int error = -1;
261  dialog_args.GetInteger(L"error", &error);
262  EXPECT_EQ(static_cast<int>(AuthError::INVALID_GAIA_CREDENTIALS), error);
263  service_->set_auth_state(kTestUser, AuthError::None());
264
265  // Simulate captcha.
266  AuthError captcha_error(AuthError::FromCaptchaChallenge(
267      std::string(), GURL(kTestCaptchaUrl), GURL()));
268  service_->set_auth_state(kTestUser, captcha_error);
269  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
270  SyncSetupFlow::GetArgsForGaiaLogin(service_, &dialog_args);
271  EXPECT_EQ(4U, dialog_args.size());
272  dialog_args.GetString(L"iframeToShow", &iframe_to_show);
273  EXPECT_EQ("login", iframe_to_show);
274  std::string captcha_url;
275  dialog_args.GetString(L"captchaUrl", &captcha_url);
276  EXPECT_EQ(kTestCaptchaUrl, GURL(captcha_url).spec());
277  error = -1;
278  dialog_args.GetInteger(L"error", &error);
279  EXPECT_EQ(static_cast<int>(AuthError::CAPTCHA_REQUIRED), error);
280  service_->set_auth_state(kTestUser, AuthError::None());
281
282  // Simulate success.
283  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
284  EXPECT_TRUE(wizard_->IsVisible());
285  EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
286  // In a non-discrete run, GAIA_SUCCESS immediately transitions you to
287  // CHOOSE_DATA_TYPES.
288  EXPECT_EQ(SyncSetupWizard::CHOOSE_DATA_TYPES,
289            test_window_->flow()->current_state_);
290
291  // That's all we're testing here, just move on to DONE.  We'll test the
292  // "choose data types" scenarios elsewhere.
293  wizard_->Step(SyncSetupWizard::DONE);  // No merge and sync.
294  EXPECT_TRUE(wizard_->IsVisible());
295  EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
296  EXPECT_EQ(SyncSetupWizard::DONE, test_window_->flow()->current_state_);
297}
298
299TEST_F(SyncSetupWizardTest, ChooseDataTypesSetsPrefs) {
300  SKIP_TEST_ON_MACOSX();
301  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
302  wizard_->Step(SyncSetupWizard::CHOOSE_DATA_TYPES);
303
304  ListValue data_type_choices_value;
305  std::string data_type_choices = "{\"keepEverythingSynced\":false,";
306  data_type_choices += "\"syncBookmarks\":true,\"syncPreferences\":true,";
307  data_type_choices += "\"syncThemes\":false,\"syncPasswords\":false,";
308  data_type_choices += "\"syncAutofill\":false,\"syncExtensions\":false,";
309  data_type_choices += "\"syncTypedUrls\":true}";
310  data_type_choices_value.Append(new StringValue(data_type_choices));
311
312  // Simulate the user choosing data types; bookmarks and prefs are on, the rest
313  // are off.
314  test_window_->flow()->flow_handler_->HandleChooseDataTypes(
315      &data_type_choices_value);
316  EXPECT_TRUE(wizard_->IsVisible());
317  EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
318  EXPECT_FALSE(service_->keep_everything_synced_);
319  EXPECT_EQ(service_->chosen_data_types_.count(syncable::BOOKMARKS), 1U);
320  EXPECT_EQ(service_->chosen_data_types_.count(syncable::PREFERENCES), 1U);
321  EXPECT_EQ(service_->chosen_data_types_.count(syncable::THEMES), 0U);
322  EXPECT_EQ(service_->chosen_data_types_.count(syncable::PASSWORDS), 0U);
323  EXPECT_EQ(service_->chosen_data_types_.count(syncable::AUTOFILL), 0U);
324  EXPECT_EQ(service_->chosen_data_types_.count(syncable::EXTENSIONS), 0U);
325  EXPECT_EQ(service_->chosen_data_types_.count(syncable::TYPED_URLS), 1U);
326
327  test_window_->CloseDialog();
328}
329
330TEST_F(SyncSetupWizardTest, DialogCancelled) {
331  SKIP_TEST_ON_MACOSX();
332  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
333  // Simulate the user closing the dialog.
334  test_window_->CloseDialog();
335  EXPECT_FALSE(wizard_->IsVisible());
336  EXPECT_TRUE(service_->user_cancelled_dialog_);
337  EXPECT_EQ(std::string(), service_->username_);
338  EXPECT_EQ(std::string(), service_->password_);
339
340  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
341  EXPECT_TRUE(wizard_->IsVisible());
342  EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
343  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
344  EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
345
346  test_window_->CloseDialog();
347  EXPECT_FALSE(wizard_->IsVisible());
348  EXPECT_TRUE(service_->user_cancelled_dialog_);
349  EXPECT_EQ(std::string(), service_->username_);
350  EXPECT_EQ(std::string(), service_->password_);
351}
352
353TEST_F(SyncSetupWizardTest, InvalidTransitions) {
354  SKIP_TEST_ON_MACOSX();
355  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
356  EXPECT_FALSE(wizard_->IsVisible());
357  EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
358
359  wizard_->Step(SyncSetupWizard::DONE);
360  EXPECT_FALSE(wizard_->IsVisible());
361  EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
362
363  wizard_->Step(SyncSetupWizard::DONE_FIRST_TIME);
364  EXPECT_FALSE(wizard_->IsVisible());
365  EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
366
367  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
368
369  wizard_->Step(SyncSetupWizard::DONE);
370  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
371  wizard_->Step(SyncSetupWizard::DONE_FIRST_TIME);
372  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
373
374  wizard_->Step(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR);
375  EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN,
376            test_window_->flow()->current_state_);
377
378  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
379  EXPECT_EQ(SyncSetupWizard::CHOOSE_DATA_TYPES,
380            test_window_->flow()->current_state_);
381
382  wizard_->Step(SyncSetupWizard::FATAL_ERROR);
383  EXPECT_EQ(SyncSetupWizard::FATAL_ERROR, test_window_->flow()->current_state_);
384}
385
386TEST_F(SyncSetupWizardTest, FullSuccessfulRunSetsPref) {
387  SKIP_TEST_ON_MACOSX();
388  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
389  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
390  wizard_->Step(SyncSetupWizard::DONE);
391  test_window_->CloseDialog();
392  EXPECT_FALSE(wizard_->IsVisible());
393  EXPECT_TRUE(service_->profile()->GetPrefs()->GetBoolean(
394      prefs::kSyncHasSetupCompleted));
395}
396
397TEST_F(SyncSetupWizardTest, FirstFullSuccessfulRunSetsPref) {
398  SKIP_TEST_ON_MACOSX();
399  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
400  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
401  wizard_->Step(SyncSetupWizard::DONE_FIRST_TIME);
402  test_window_->CloseDialog();
403  EXPECT_FALSE(wizard_->IsVisible());
404  EXPECT_TRUE(service_->profile()->GetPrefs()->GetBoolean(
405      prefs::kSyncHasSetupCompleted));
406}
407
408TEST_F(SyncSetupWizardTest, AbortedByPendingClear) {
409  SKIP_TEST_ON_MACOSX();
410  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
411  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
412  wizard_->Step(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR);
413  EXPECT_EQ(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR,
414            test_window_->flow()->current_state_);
415  test_window_->CloseDialog();
416  EXPECT_FALSE(wizard_->IsVisible());
417}
418
419TEST_F(SyncSetupWizardTest, DiscreteRunChooseDataTypes) {
420  SKIP_TEST_ON_MACOSX();
421  // For a discrete run, we need to have ran through setup once.
422  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
423  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
424  wizard_->Step(SyncSetupWizard::DONE);
425  test_window_->CloseDialog();
426  EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
427
428  wizard_->Step(SyncSetupWizard::CHOOSE_DATA_TYPES);
429  EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
430  EXPECT_EQ(SyncSetupWizard::DONE, test_window_->flow()->end_state_);
431
432  wizard_->Step(SyncSetupWizard::DONE);
433  test_window_->CloseDialog();
434  EXPECT_FALSE(wizard_->IsVisible());
435}
436
437TEST_F(SyncSetupWizardTest, DiscreteRunChooseDataTypesAbortedByPendingClear) {
438  SKIP_TEST_ON_MACOSX();
439  // For a discrete run, we need to have ran through setup once.
440  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
441  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
442  wizard_->Step(SyncSetupWizard::DONE);
443  test_window_->CloseDialog();
444  EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
445
446  wizard_->Step(SyncSetupWizard::CHOOSE_DATA_TYPES);
447  EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
448  EXPECT_EQ(SyncSetupWizard::DONE, test_window_->flow()->end_state_);
449   wizard_->Step(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR);
450  EXPECT_EQ(SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR,
451            test_window_->flow()->current_state_);
452
453  test_window_->CloseDialog();
454  EXPECT_FALSE(wizard_->IsVisible());
455}
456
457TEST_F(SyncSetupWizardTest, DiscreteRunGaiaLogin) {
458  SKIP_TEST_ON_MACOSX();
459  DictionaryValue dialog_args;
460  // For a discrete run, we need to have ran through setup once.
461  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
462  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
463  wizard_->Step(SyncSetupWizard::DONE);
464  test_window_->CloseDialog();
465  EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
466
467  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
468  EXPECT_EQ(SyncSetupWizard::GAIA_SUCCESS, test_window_->flow()->end_state_);
469
470  AuthError invalid_gaia(AuthError::INVALID_GAIA_CREDENTIALS);
471  service_->set_auth_state(kTestUser, invalid_gaia);
472  wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
473  EXPECT_TRUE(wizard_->IsVisible());
474  SyncSetupFlow::GetArgsForGaiaLogin(service_, &dialog_args);
475  EXPECT_EQ(4U, dialog_args.size());
476  std::string iframe_to_show;
477  dialog_args.GetString(L"iframeToShow", &iframe_to_show);
478  EXPECT_EQ("login", iframe_to_show);
479  std::string actual_user;
480  dialog_args.GetString(L"user", &actual_user);
481  EXPECT_EQ(kTestUser, actual_user);
482  int error = -1;
483  dialog_args.GetInteger(L"error", &error);
484  EXPECT_EQ(static_cast<int>(AuthError::INVALID_GAIA_CREDENTIALS), error);
485  service_->set_auth_state(kTestUser, AuthError::None());
486
487  wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
488  EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
489}
490
491#undef SKIP_TEST_ON_MACOSX
492