1 // Copyright (c) 2012 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.
6 #include "base/basictypes.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/profiles/profile.h"
9 #include "chrome/browser/signin/fake_signin_manager.h"
10 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
11 #include "chrome/browser/sync/profile_sync_service_mock.h"
12 #include "chrome/browser/sync/sync_ui_util.h"
13 #include "chrome/grit/generated_resources.h"
14 #include "components/signin/core/browser/fake_auth_status_provider.h"
15 #include "components/signin/core/browser/profile_oauth2_token_service.h"
16 #include "components/signin/core/browser/signin_manager.h"
17 #include "content/public/test/test_browser_thread.h"
18 #include "content/public/test/test_browser_thread_bundle.h"
19 #include "testing/gmock/include/gmock/gmock-actions.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #include "ui/base/l10n/l10n_util.h"
24 using ::testing::AtMost;
25 using ::testing::NiceMock;
26 using ::testing::Return;
27 using ::testing::ReturnRef;
28 using ::testing::SetArgPointee;
30 using content::BrowserThread;
32 // A number of distinct states of the ProfileSyncService can be generated for
35 STATUS_CASE_SETUP_IN_PROGRESS,
36 STATUS_CASE_SETUP_ERROR,
37 STATUS_CASE_AUTHENTICATING,
38 STATUS_CASE_AUTH_ERROR,
39 STATUS_CASE_PROTOCOL_ERROR,
40 STATUS_CASE_PASSPHRASE_ERROR,
42 STATUS_CASE_SYNC_DISABLED_BY_POLICY,
43 NUMBER_OF_STATUS_CASES
48 const char kTestUser[] = "test_user@test.com";
50 #if !defined(OS_CHROMEOS)
51 // Utility function to test that GetStatusLabelsForSyncGlobalError returns
52 // the correct results for the given states.
53 void VerifySyncGlobalErrorResult(NiceMock<ProfileSyncServiceMock>* service,
54 GoogleServiceAuthError::State error_state,
57 EXPECT_CALL(*service, HasSyncSetupCompleted())
58 .WillRepeatedly(Return(is_signed_in));
60 GoogleServiceAuthError auth_error(error_state);
61 EXPECT_CALL(*service, GetAuthError()).WillRepeatedly(ReturnRef(auth_error));
63 base::string16 label1, label2, label3;
64 sync_ui_util::GetStatusLabelsForSyncGlobalError(
65 service, &label1, &label2, &label3);
66 EXPECT_EQ(label1.empty(), !is_error);
67 EXPECT_EQ(label2.empty(), !is_error);
68 EXPECT_EQ(label3.empty(), !is_error);
75 class SyncUIUtilTest : public testing::Test {
77 content::TestBrowserThreadBundle thread_bundle_;
80 #if !defined(OS_CHROMEOS)
81 // Test that GetStatusLabelsForSyncGlobalError returns an error if a
82 // passphrase is required.
83 TEST_F(SyncUIUtilTest, PassphraseGlobalError) {
84 scoped_ptr<Profile> profile(
85 ProfileSyncServiceMock::MakeSignedInTestingProfile());
86 NiceMock<ProfileSyncServiceMock> service(profile.get());
87 browser_sync::SyncBackendHost::Status status;
88 EXPECT_CALL(service, QueryDetailedSyncStatus(_))
89 .WillRepeatedly(Return(false));
90 EXPECT_CALL(service, IsPassphraseRequired())
91 .WillRepeatedly(Return(true));
92 EXPECT_CALL(service, IsPassphraseRequiredForDecryption())
93 .WillRepeatedly(Return(true));
95 VerifySyncGlobalErrorResult(&service,
96 GoogleServiceAuthError::NONE,
101 // Test that GetStatusLabelsForSyncGlobalError returns an error if a
102 // passphrase is required and not for auth errors.
103 TEST_F(SyncUIUtilTest, AuthAndPassphraseGlobalError) {
104 scoped_ptr<Profile> profile(
105 ProfileSyncServiceMock::MakeSignedInTestingProfile());
106 NiceMock<ProfileSyncServiceMock> service(profile.get());
107 browser_sync::SyncBackendHost::Status status;
108 EXPECT_CALL(service, QueryDetailedSyncStatus(_))
109 .WillRepeatedly(Return(false));
111 EXPECT_CALL(service, IsPassphraseRequired())
112 .WillRepeatedly(Return(true));
113 EXPECT_CALL(service, IsPassphraseRequiredForDecryption())
114 .WillRepeatedly(Return(true));
115 EXPECT_CALL(service, HasSyncSetupCompleted())
116 .WillRepeatedly(Return(true));
118 GoogleServiceAuthError auth_error(
119 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
120 EXPECT_CALL(service, GetAuthError()).WillRepeatedly(ReturnRef(auth_error));
121 base::string16 menu_label, label2, label3;
122 sync_ui_util::GetStatusLabelsForSyncGlobalError(
123 &service, &menu_label, &label2, &label3);
124 // Make sure we are still displaying the passphrase error badge (don't show
125 // auth errors through SyncUIUtil).
126 EXPECT_EQ(menu_label, l10n_util::GetStringUTF16(
127 IDS_SYNC_PASSPHRASE_ERROR_WRENCH_MENU_ITEM));
130 // Test that GetStatusLabelsForSyncGlobalError does not indicate errors for
131 // auth errors (these are reported through SigninGlobalError).
132 TEST_F(SyncUIUtilTest, AuthStateGlobalError) {
133 scoped_ptr<Profile> profile(
134 ProfileSyncServiceMock::MakeSignedInTestingProfile());
135 NiceMock<ProfileSyncServiceMock> service(profile.get());
137 browser_sync::SyncBackendHost::Status status;
138 EXPECT_CALL(service, QueryDetailedSyncStatus(_))
139 .WillRepeatedly(Return(false));
141 GoogleServiceAuthError::State table[] = {
142 GoogleServiceAuthError::NONE,
143 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS,
144 GoogleServiceAuthError::USER_NOT_SIGNED_UP,
145 GoogleServiceAuthError::CONNECTION_FAILED,
146 GoogleServiceAuthError::CAPTCHA_REQUIRED,
147 GoogleServiceAuthError::ACCOUNT_DELETED,
148 GoogleServiceAuthError::ACCOUNT_DISABLED,
149 GoogleServiceAuthError::SERVICE_UNAVAILABLE,
150 GoogleServiceAuthError::TWO_FACTOR,
151 GoogleServiceAuthError::REQUEST_CANCELED,
152 GoogleServiceAuthError::HOSTED_NOT_ALLOWED
155 FakeSigninManagerBase signin(profile.get());
156 for (size_t i = 0; i < arraysize(table); ++i) {
157 VerifySyncGlobalErrorResult(&service,
159 true /* signed in */,
160 false /* no error */);
161 VerifySyncGlobalErrorResult(&service,
163 false /* not signed in */,
164 false /* no error */);
169 // TODO(tim): This shouldn't be required. r194857 removed the
170 // AuthInProgress override from FakeSigninManager, which meant this test started
171 // using the "real" SigninManager AuthInProgress logic. Without that override,
172 // it's no longer possible to test both chrome os + desktop flows as part of the
173 // same test, because AuthInProgress is always false on chrome os. Most of the
174 // tests are unaffected, but STATUS_CASE_AUTHENTICATING can't exist in both
175 // versions, so it we will require two separate tests, one using SigninManager
176 // and one using SigninManagerBase (which require different setup procedures.
177 class FakeSigninManagerForSyncUIUtilTest : public FakeSigninManagerBase {
179 explicit FakeSigninManagerForSyncUIUtilTest(Profile* profile)
180 : FakeSigninManagerBase(profile), auth_in_progress_(false) {
184 ~FakeSigninManagerForSyncUIUtilTest() override {}
186 bool AuthInProgress() const override { return auth_in_progress_; }
188 void set_auth_in_progress() {
189 auth_in_progress_ = true;
193 bool auth_in_progress_;
196 // Loads a ProfileSyncServiceMock to emulate one of a number of distinct cases
197 // in order to perform tests on the generated messages.
198 void GetDistinctCase(ProfileSyncServiceMock& service,
199 FakeSigninManagerForSyncUIUtilTest* signin,
200 FakeAuthStatusProvider* provider,
202 // Auth Error object is returned by reference in mock and needs to stay in
203 // scope throughout test, so it is owned by calling method. However it is
204 // immutable so can only be allocated in this method.
205 switch (caseNumber) {
206 case STATUS_CASE_SETUP_IN_PROGRESS: {
207 EXPECT_CALL(service, HasSyncSetupCompleted())
208 .WillRepeatedly(Return(false));
209 EXPECT_CALL(service, FirstSetupInProgress())
210 .WillRepeatedly(Return(true));
211 browser_sync::SyncBackendHost::Status status;
212 EXPECT_CALL(service, QueryDetailedSyncStatus(_))
213 .WillRepeatedly(DoAll(SetArgPointee<0>(status),
217 case STATUS_CASE_SETUP_ERROR: {
218 EXPECT_CALL(service, HasSyncSetupCompleted())
219 .WillRepeatedly(Return(false));
220 EXPECT_CALL(service, FirstSetupInProgress())
221 .WillRepeatedly(Return(false));
222 EXPECT_CALL(service, HasUnrecoverableError())
223 .WillRepeatedly(Return(true));
224 browser_sync::SyncBackendHost::Status status;
225 EXPECT_CALL(service, QueryDetailedSyncStatus(_))
226 .WillRepeatedly(DoAll(SetArgPointee<0>(status),
230 case STATUS_CASE_AUTHENTICATING: {
231 EXPECT_CALL(service, HasSyncSetupCompleted())
232 .WillRepeatedly(Return(true));
233 EXPECT_CALL(service, SyncActive()).WillRepeatedly(Return(true));
234 EXPECT_CALL(service, IsPassphraseRequired())
235 .WillRepeatedly(Return(false));
236 browser_sync::SyncBackendHost::Status status;
237 EXPECT_CALL(service, QueryDetailedSyncStatus(_))
238 .WillRepeatedly(DoAll(SetArgPointee<0>(status),
240 EXPECT_CALL(service, HasUnrecoverableError())
241 .WillRepeatedly(Return(false));
242 signin->set_auth_in_progress();
245 case STATUS_CASE_AUTH_ERROR: {
246 EXPECT_CALL(service, HasSyncSetupCompleted())
247 .WillRepeatedly(Return(true));
248 EXPECT_CALL(service, SyncActive()).WillRepeatedly(Return(true));
249 EXPECT_CALL(service, IsPassphraseRequired())
250 .WillRepeatedly(Return(false));
251 browser_sync::SyncBackendHost::Status status;
252 EXPECT_CALL(service, QueryDetailedSyncStatus(_))
253 .WillRepeatedly(DoAll(SetArgPointee<0>(status),
255 provider->SetAuthError(
258 GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
259 EXPECT_CALL(service, HasUnrecoverableError())
260 .WillRepeatedly(Return(false));
263 case STATUS_CASE_PROTOCOL_ERROR: {
264 EXPECT_CALL(service, HasSyncSetupCompleted())
265 .WillRepeatedly(Return(true));
266 EXPECT_CALL(service, SyncActive()).WillRepeatedly(Return(true));
267 EXPECT_CALL(service, IsPassphraseRequired())
268 .WillRepeatedly(Return(false));
269 syncer::SyncProtocolError protocolError;
270 protocolError.action = syncer::STOP_AND_RESTART_SYNC;
271 browser_sync::SyncBackendHost::Status status;
272 status.sync_protocol_error = protocolError;
273 EXPECT_CALL(service, QueryDetailedSyncStatus(_))
274 .WillRepeatedly(DoAll(SetArgPointee<0>(status),
276 EXPECT_CALL(service, HasUnrecoverableError())
277 .WillRepeatedly(Return(false));
280 case STATUS_CASE_PASSPHRASE_ERROR: {
281 EXPECT_CALL(service, HasSyncSetupCompleted())
282 .WillRepeatedly(Return(true));
283 EXPECT_CALL(service, SyncActive()).WillRepeatedly(Return(true));
284 browser_sync::SyncBackendHost::Status status;
285 EXPECT_CALL(service, QueryDetailedSyncStatus(_))
286 .WillRepeatedly(DoAll(SetArgPointee<0>(status),
288 EXPECT_CALL(service, HasUnrecoverableError())
289 .WillRepeatedly(Return(false));
290 EXPECT_CALL(service, IsPassphraseRequired())
291 .WillRepeatedly(Return(true));
292 EXPECT_CALL(service, IsPassphraseRequiredForDecryption())
293 .WillRepeatedly(Return(true));
296 case STATUS_CASE_SYNCED: {
297 EXPECT_CALL(service, HasSyncSetupCompleted())
298 .WillRepeatedly(Return(true));
299 EXPECT_CALL(service, SyncActive()).WillRepeatedly(Return(true));
300 EXPECT_CALL(service, IsPassphraseRequired())
301 .WillRepeatedly(Return(false));
302 browser_sync::SyncBackendHost::Status status;
303 EXPECT_CALL(service, QueryDetailedSyncStatus(_))
304 .WillRepeatedly(DoAll(SetArgPointee<0>(status),
306 EXPECT_CALL(service, HasUnrecoverableError())
307 .WillRepeatedly(Return(false));
308 EXPECT_CALL(service, IsPassphraseRequired())
309 .WillRepeatedly(Return(false));
312 case STATUS_CASE_SYNC_DISABLED_BY_POLICY: {
313 EXPECT_CALL(service, IsManaged()).WillRepeatedly(Return(true));
314 EXPECT_CALL(service, HasSyncSetupCompleted())
315 .WillRepeatedly(Return(false));
316 EXPECT_CALL(service, SyncActive()).WillRepeatedly(Return(false));
317 EXPECT_CALL(service, IsPassphraseRequired())
318 .WillRepeatedly(Return(false));
319 browser_sync::SyncBackendHost::Status status;
320 EXPECT_CALL(service, QueryDetailedSyncStatus(_))
321 .WillRepeatedly(DoAll(SetArgPointee<0>(status),
323 EXPECT_CALL(service, HasUnrecoverableError())
324 .WillRepeatedly(Return(false));
332 // This test ensures that a each distinctive ProfileSyncService statuses
333 // will return a unique combination of status and link messages from
334 // GetStatusLabels().
335 TEST_F(SyncUIUtilTest, DistinctCasesReportUniqueMessageSets) {
336 std::set<base::string16> messages;
337 for (int idx = 0; idx != NUMBER_OF_STATUS_CASES; idx++) {
338 scoped_ptr<Profile> profile(new TestingProfile());
339 ProfileSyncServiceMock service(profile.get());
340 GoogleServiceAuthError error = GoogleServiceAuthError::AuthErrorNone();
341 EXPECT_CALL(service, GetAuthError()).WillRepeatedly(ReturnRef(error));
342 FakeSigninManagerForSyncUIUtilTest signin(profile.get());
343 signin.SetAuthenticatedUsername(kTestUser);
344 scoped_ptr<FakeAuthStatusProvider> provider(new FakeAuthStatusProvider(
345 ProfileOAuth2TokenServiceFactory::GetForProfile(profile.get())->
346 signin_error_controller()));
347 GetDistinctCase(service, &signin, provider.get(), idx);
348 base::string16 status_label;
349 base::string16 link_label;
350 sync_ui_util::GetStatusLabels(&service,
352 sync_ui_util::WITH_HTML,
355 // If the status and link message combination is already present in the set
356 // of messages already seen, this is a duplicate rather than a unique
357 // message, and the test has failed.
358 EXPECT_FALSE(status_label.empty()) <<
359 "Empty status label returned for case #" << idx;
360 base::string16 combined_label =
361 status_label + base::ASCIIToUTF16("#") + link_label;
362 EXPECT_TRUE(messages.find(combined_label) == messages.end()) <<
363 "Duplicate message for case #" << idx << ": " << combined_label;
364 messages.insert(combined_label);
365 testing::Mock::VerifyAndClearExpectations(&service);
366 testing::Mock::VerifyAndClearExpectations(&signin);
367 EXPECT_CALL(service, GetAuthError()).WillRepeatedly(ReturnRef(error));
373 // This test ensures that the html_links parameter on GetStatusLabels() is
375 TEST_F(SyncUIUtilTest, HtmlNotIncludedInStatusIfNotRequested) {
376 for (int idx = 0; idx != NUMBER_OF_STATUS_CASES; idx++) {
377 scoped_ptr<Profile> profile(
378 ProfileSyncServiceMock::MakeSignedInTestingProfile());
379 ProfileSyncServiceMock service(profile.get());
380 GoogleServiceAuthError error = GoogleServiceAuthError::AuthErrorNone();
381 EXPECT_CALL(service, GetAuthError()).WillRepeatedly(ReturnRef(error));
382 FakeSigninManagerForSyncUIUtilTest signin(profile.get());
383 signin.SetAuthenticatedUsername(kTestUser);
384 scoped_ptr<FakeAuthStatusProvider> provider(new FakeAuthStatusProvider(
385 ProfileOAuth2TokenServiceFactory::GetForProfile(profile.get())->
386 signin_error_controller()));
387 GetDistinctCase(service, &signin, provider.get(), idx);
388 base::string16 status_label;
389 base::string16 link_label;
390 sync_ui_util::GetStatusLabels(&service,
392 sync_ui_util::PLAIN_TEXT,
396 // Ensures a search for string 'href' (found in links, not a string to be
397 // found in an English language message) fails when links are excluded from
399 EXPECT_FALSE(status_label.empty());
400 EXPECT_EQ(status_label.find(base::ASCIIToUTF16("href")),
401 base::string16::npos);
402 testing::Mock::VerifyAndClearExpectations(&service);
403 testing::Mock::VerifyAndClearExpectations(&signin);
404 EXPECT_CALL(service, GetAuthError()).WillRepeatedly(ReturnRef(error));