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 "chrome/browser/sync/startup_controller.h"
6
7#include "base/command_line.h"
8#include "base/message_loop/message_loop.h"
9#include "base/run_loop.h"
10#include "base/time/time.h"
11#include "chrome/browser/defaults.h"
12#include "chrome/browser/signin/fake_profile_oauth2_token_service.h"
13#include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h"
14#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
15#include "chrome/browser/sync/supervised_user_signin_manager_wrapper.h"
16#include "chrome/common/chrome_switches.h"
17#include "chrome/test/base/testing_profile.h"
18#include "components/sync_driver/sync_prefs.h"
19#include "testing/gtest/include/gtest/gtest.h"
20
21namespace browser_sync {
22
23static const char kTestUser[] = "test@gmail.com";
24static const char kTestToken[] = "testToken";
25
26// These are coupled to the implementation of StartupController's
27// GetBackendInitializationStateString which is used by about:sync. We use it
28// as a convenient way to verify internal state and that the class is
29// outputting the correct values for the debug string.
30static const char kStateStringStarted[] = "Started";
31static const char kStateStringDeferred[] = "Deferred";
32static const char kStateStringNotStarted[] = "Not started";
33
34class FakeSupervisedUserSigninManagerWrapper
35    : public SupervisedUserSigninManagerWrapper {
36 public:
37  FakeSupervisedUserSigninManagerWrapper()
38      : SupervisedUserSigninManagerWrapper(NULL, NULL) {}
39  virtual std::string GetEffectiveUsername() const OVERRIDE {
40    return account_;
41  }
42
43  virtual std::string GetAccountIdToUse() const OVERRIDE {
44    return account_;
45  }
46
47  void set_account(const std::string& account) { account_ = account; }
48
49 private:
50  std::string account_;
51};
52
53class StartupControllerTest : public testing::Test {
54 public:
55  StartupControllerTest() : started_(false) {}
56
57  virtual void SetUp() OVERRIDE {
58    profile_.reset(new TestingProfile());
59    sync_prefs_.reset(new sync_driver::SyncPrefs(profile_->GetPrefs()));
60    token_service_.reset(static_cast<FakeProfileOAuth2TokenService*>(
61        BuildFakeProfileOAuth2TokenService(profile_.get())));
62    signin_.reset(new FakeSupervisedUserSigninManagerWrapper());
63
64    ProfileSyncServiceStartBehavior behavior =
65        browser_defaults::kSyncAutoStarts ? AUTO_START : MANUAL_START;
66    base::Closure fake_start_backend = base::Bind(
67        &StartupControllerTest::FakeStartBackend, base::Unretained(this));
68    controller_.reset(new StartupController(behavior, token_service(),
69                                            sync_prefs_.get(), signin_.get(),
70                                            fake_start_backend));
71    controller_->Reset(syncer::UserTypes());
72    controller_->OverrideFallbackTimeoutForTest(
73        base::TimeDelta::FromSeconds(0));
74  }
75
76  virtual void TearDown() OVERRIDE {
77    controller_.reset();
78    signin_.reset();
79    token_service_->Shutdown();
80    token_service_.reset();
81    sync_prefs_.reset();
82    started_ = false;
83  }
84
85  void FakeStartBackend() {
86    started_ = true;
87  }
88
89  bool started() const { return started_; }
90  void clear_started() { started_ = false; }
91  StartupController* controller() { return controller_.get(); }
92  FakeSupervisedUserSigninManagerWrapper* signin() { return signin_.get(); }
93  FakeProfileOAuth2TokenService* token_service() {
94    return token_service_.get();
95  }
96  sync_driver::SyncPrefs* sync_prefs() { return sync_prefs_.get(); }
97  Profile* profile() { return profile_.get(); }
98
99 private:
100  bool started_;
101  base::MessageLoop message_loop_;
102  scoped_ptr<StartupController> controller_;
103  scoped_ptr<FakeSupervisedUserSigninManagerWrapper> signin_;
104  scoped_ptr<FakeProfileOAuth2TokenService> token_service_;
105  scoped_ptr<sync_driver::SyncPrefs> sync_prefs_;
106  scoped_ptr<TestingProfile> profile_;
107};
108
109// Test that sync doesn't start until all conditions are met.
110TEST_F(StartupControllerTest, Basic) {
111  controller()->TryStart();
112  EXPECT_FALSE(started());
113  sync_prefs()->SetSyncSetupCompleted();
114  controller()->TryStart();
115  EXPECT_FALSE(started());
116  signin()->set_account(kTestUser);
117  controller()->TryStart();
118  EXPECT_FALSE(started());
119  token_service()->IssueRefreshTokenForUser(kTestUser, kTestToken);
120  const bool deferred_start = !CommandLine::ForCurrentProcess()->
121      HasSwitch(switches::kSyncDisableDeferredStartup);
122  controller()->TryStart();
123  EXPECT_EQ(!deferred_start, started());
124  std::string state(controller()->GetBackendInitializationStateString());
125  EXPECT_TRUE(deferred_start ? state == kStateStringDeferred :
126                               state == kStateStringStarted);
127}
128
129// Test that sync doesn't when suppressed even if all other conditons are met.
130TEST_F(StartupControllerTest, Suppressed) {
131  sync_prefs()->SetSyncSetupCompleted();
132  sync_prefs()->SetStartSuppressed(true);
133  signin()->set_account(kTestUser);
134  token_service()->IssueRefreshTokenForUser(kTestUser, kTestToken);
135  controller()->TryStart();
136  EXPECT_FALSE(started());
137  EXPECT_EQ(kStateStringNotStarted,
138            controller()->GetBackendInitializationStateString());
139}
140
141// Test that sync doesn't when managed even if all other conditons are met.
142TEST_F(StartupControllerTest, Managed) {
143  sync_prefs()->SetSyncSetupCompleted();
144  sync_prefs()->SetManagedForTest(true);
145  signin()->set_account(kTestUser);
146  token_service()->IssueRefreshTokenForUser(kTestUser, kTestToken);
147  controller()->TryStart();
148  EXPECT_FALSE(started());
149  EXPECT_EQ(kStateStringNotStarted,
150            controller()->GetBackendInitializationStateString());
151}
152
153// Test that sync doesn't start until all conditions are met and a
154// data type triggers sync startup.
155TEST_F(StartupControllerTest, DataTypeTriggered) {
156  sync_prefs()->SetSyncSetupCompleted();
157  signin()->set_account(kTestUser);
158  token_service()->IssueRefreshTokenForUser(kTestUser, kTestToken);
159  controller()->TryStart();
160  EXPECT_FALSE(started());
161  EXPECT_EQ(kStateStringDeferred,
162            controller()->GetBackendInitializationStateString());
163  controller()->OnDataTypeRequestsSyncStartup(syncer::SESSIONS);
164  EXPECT_TRUE(started());
165  EXPECT_EQ(kStateStringStarted,
166            controller()->GetBackendInitializationStateString());
167
168  // The fallback timer shouldn't result in another invocation of the closure
169  // we passed to the StartupController.
170  clear_started();
171  base::RunLoop().RunUntilIdle();
172  EXPECT_FALSE(started());
173}
174
175// Test that the fallback timer starts sync in the event all
176// conditions are met and no data type requests sync.
177TEST_F(StartupControllerTest, FallbackTimer) {
178  sync_prefs()->SetSyncSetupCompleted();
179  signin()->set_account(kTestUser);
180  token_service()->IssueRefreshTokenForUser(kTestUser, kTestToken);
181  controller()->TryStart();
182  EXPECT_FALSE(started());
183  base::RunLoop().RunUntilIdle();
184  EXPECT_TRUE(started());
185}
186
187// Test that we start immediately if sessions is disabled.
188TEST_F(StartupControllerTest, NoDeferralWithoutSessionsSync) {
189  syncer::ModelTypeSet types(syncer::UserTypes());
190  // Disabling sessions means disabling 4 types due to groupings.
191  types.Remove(syncer::SESSIONS);
192  types.Remove(syncer::PROXY_TABS);
193  types.Remove(syncer::TYPED_URLS);
194  types.Remove(syncer::SUPERVISED_USER_SETTINGS);
195  sync_prefs()->SetKeepEverythingSynced(false);
196  sync_prefs()->SetPreferredDataTypes(syncer::UserTypes(), types);
197  controller()->Reset(syncer::UserTypes());
198  sync_prefs()->SetSyncSetupCompleted();
199  signin()->set_account(kTestUser);
200  token_service()->IssueRefreshTokenForUser(kTestUser, kTestToken);
201  controller()->TryStart();
202  EXPECT_TRUE(started());
203}
204
205// Sanity check that the fallback timer doesn't fire before startup
206// conditions are met.
207TEST_F(StartupControllerTest, FallbackTimerWaits) {
208  controller()->TryStart();
209  EXPECT_FALSE(started());
210  base::RunLoop().RunUntilIdle();
211  EXPECT_FALSE(started());
212}
213
214// Test that sync starts when the user first asks to setup sync (which
215// may be implicit due to the platform).
216TEST_F(StartupControllerTest, FirstSetup) {
217  signin()->set_account(kTestUser);
218  token_service()->IssueRefreshTokenForUser(kTestUser, kTestToken);
219  controller()->TryStart();
220
221  if (browser_defaults::kSyncAutoStarts) {
222    EXPECT_TRUE(started());
223  } else {
224    controller()->set_setup_in_progress(true);
225    controller()->TryStart();
226    EXPECT_TRUE(started());
227  }
228}
229
230TEST_F(StartupControllerTest, Reset) {
231  sync_prefs()->SetSyncSetupCompleted();
232  signin()->set_account(kTestUser);
233  token_service()->IssueRefreshTokenForUser(kTestUser, kTestToken);
234  controller()->TryStart();
235  const bool deferred_start = !CommandLine::ForCurrentProcess()->
236      HasSwitch(switches::kSyncDisableDeferredStartup);
237  EXPECT_EQ(!deferred_start, started());
238  controller()->OnDataTypeRequestsSyncStartup(syncer::SESSIONS);
239  EXPECT_TRUE(started());
240  clear_started();
241  controller()->Reset(syncer::UserTypes());
242  EXPECT_FALSE(started());
243  controller()->TryStart();
244  // Restart is not deferred.
245  EXPECT_TRUE(started());
246}
247
248// Test that setup-in-progress tracking is persistent across a Reset.
249TEST_F(StartupControllerTest, ResetDuringSetup) {
250  signin()->set_account(kTestUser);
251  token_service()->IssueRefreshTokenForUser(kTestUser, kTestToken);
252
253  // Simulate UI telling us setup is in progress.
254  controller()->set_setup_in_progress(true);
255
256  // This could happen if the UI triggers a stop-syncing permanently call.
257  controller()->Reset(syncer::UserTypes());
258
259  // From the UI's point of view, setup is still in progress.
260  EXPECT_TRUE(controller()->setup_in_progress());
261}
262
263}  // namespace browser_sync
264