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