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