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_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"
     23 
     24 using ::testing::AtMost;
     25 using ::testing::NiceMock;
     26 using ::testing::Return;
     27 using ::testing::ReturnRef;
     28 using ::testing::SetArgPointee;
     29 using ::testing::_;
     30 using content::BrowserThread;
     31 
     32 // A number of distinct states of the ProfileSyncService can be generated for
     33 // tests.
     34 enum DistinctState {
     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,
     41   STATUS_CASE_SYNCED,
     42   STATUS_CASE_SYNC_DISABLED_BY_POLICY,
     43   NUMBER_OF_STATUS_CASES
     44 };
     45 
     46 namespace {
     47 
     48 const char kTestUser[] = "test_user (at) test.com";
     49 
     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,
     55                                  bool is_signed_in,
     56                                  bool is_error) {
     57   EXPECT_CALL(*service, HasSyncSetupCompleted())
     58               .WillRepeatedly(Return(is_signed_in));
     59 
     60   GoogleServiceAuthError auth_error(error_state);
     61   EXPECT_CALL(*service, GetAuthError()).WillRepeatedly(ReturnRef(auth_error));
     62 
     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);
     69 }
     70 #endif
     71 
     72 } // namespace
     73 
     74 
     75 class SyncUIUtilTest : public testing::Test {
     76  private:
     77   content::TestBrowserThreadBundle thread_bundle_;
     78 };
     79 
     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));
     94 
     95   VerifySyncGlobalErrorResult(&service,
     96                               GoogleServiceAuthError::NONE,
     97                               true /* signed in */,
     98                               true /* error */);
     99 }
    100 
    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));
    110 
    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));
    117 
    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));
    128 }
    129 
    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());
    136 
    137   browser_sync::SyncBackendHost::Status status;
    138   EXPECT_CALL(service, QueryDetailedSyncStatus(_))
    139               .WillRepeatedly(Return(false));
    140 
    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
    153   };
    154 
    155   FakeSigninManagerBase signin(profile.get());
    156   for (size_t i = 0; i < arraysize(table); ++i) {
    157     VerifySyncGlobalErrorResult(&service,
    158                                 table[i],
    159                                 true /* signed in */,
    160                                 false /* no error */);
    161     VerifySyncGlobalErrorResult(&service,
    162                                 table[i],
    163                                 false /* not signed in */,
    164                                 false /* no error */);
    165   }
    166 }
    167 #endif
    168 
    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 {
    178  public:
    179   explicit FakeSigninManagerForSyncUIUtilTest(Profile* profile)
    180       : FakeSigninManagerBase(profile), auth_in_progress_(false) {
    181     Initialize(NULL);
    182   }
    183 
    184   virtual ~FakeSigninManagerForSyncUIUtilTest() {
    185   }
    186 
    187   virtual bool AuthInProgress() const OVERRIDE {
    188     return auth_in_progress_;
    189   }
    190 
    191   void set_auth_in_progress() {
    192     auth_in_progress_ = true;
    193   }
    194 
    195  private:
    196   bool auth_in_progress_;
    197 };
    198 
    199 // Loads a ProfileSyncServiceMock to emulate one of a number of distinct cases
    200 // in order to perform tests on the generated messages.
    201 void GetDistinctCase(ProfileSyncServiceMock& service,
    202                      FakeSigninManagerForSyncUIUtilTest* signin,
    203                      FakeAuthStatusProvider* provider,
    204                      int caseNumber) {
    205   // Auth Error object is returned by reference in mock and needs to stay in
    206   // scope throughout test, so it is owned by calling method. However it is
    207   // immutable so can only be allocated in this method.
    208   switch (caseNumber) {
    209     case STATUS_CASE_SETUP_IN_PROGRESS: {
    210       EXPECT_CALL(service, HasSyncSetupCompleted())
    211                   .WillRepeatedly(Return(false));
    212       EXPECT_CALL(service, FirstSetupInProgress())
    213                   .WillRepeatedly(Return(true));
    214       browser_sync::SyncBackendHost::Status status;
    215       EXPECT_CALL(service, QueryDetailedSyncStatus(_))
    216                   .WillRepeatedly(DoAll(SetArgPointee<0>(status),
    217                                   Return(false)));
    218       return;
    219     }
    220    case STATUS_CASE_SETUP_ERROR: {
    221       EXPECT_CALL(service, HasSyncSetupCompleted())
    222                   .WillRepeatedly(Return(false));
    223       EXPECT_CALL(service, FirstSetupInProgress())
    224                   .WillRepeatedly(Return(false));
    225       EXPECT_CALL(service, HasUnrecoverableError())
    226                   .WillRepeatedly(Return(true));
    227       browser_sync::SyncBackendHost::Status status;
    228       EXPECT_CALL(service, QueryDetailedSyncStatus(_))
    229                   .WillRepeatedly(DoAll(SetArgPointee<0>(status),
    230                                   Return(false)));
    231       return;
    232     }
    233     case STATUS_CASE_AUTHENTICATING: {
    234       EXPECT_CALL(service, HasSyncSetupCompleted())
    235                   .WillRepeatedly(Return(true));
    236       EXPECT_CALL(service, sync_initialized()).WillRepeatedly(Return(true));
    237       EXPECT_CALL(service, IsPassphraseRequired())
    238                   .WillRepeatedly(Return(false));
    239       browser_sync::SyncBackendHost::Status status;
    240       EXPECT_CALL(service, QueryDetailedSyncStatus(_))
    241                   .WillRepeatedly(DoAll(SetArgPointee<0>(status),
    242                                   Return(false)));
    243       EXPECT_CALL(service, HasUnrecoverableError())
    244                   .WillRepeatedly(Return(false));
    245       signin->set_auth_in_progress();
    246       return;
    247     }
    248     case STATUS_CASE_AUTH_ERROR: {
    249       EXPECT_CALL(service, HasSyncSetupCompleted())
    250                   .WillRepeatedly(Return(true));
    251       EXPECT_CALL(service, sync_initialized()).WillRepeatedly(Return(true));
    252       EXPECT_CALL(service, IsPassphraseRequired())
    253                   .WillRepeatedly(Return(false));
    254       browser_sync::SyncBackendHost::Status status;
    255       EXPECT_CALL(service, QueryDetailedSyncStatus(_))
    256                   .WillRepeatedly(DoAll(SetArgPointee<0>(status),
    257                                   Return(false)));
    258       provider->SetAuthError(
    259           kTestUser,
    260           kTestUser,
    261           GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE));
    262       EXPECT_CALL(service, HasUnrecoverableError())
    263                   .WillRepeatedly(Return(false));
    264       return;
    265     }
    266     case STATUS_CASE_PROTOCOL_ERROR: {
    267       EXPECT_CALL(service, HasSyncSetupCompleted())
    268                   .WillRepeatedly(Return(true));
    269       EXPECT_CALL(service, sync_initialized()).WillRepeatedly(Return(true));
    270       EXPECT_CALL(service, IsPassphraseRequired())
    271                   .WillRepeatedly(Return(false));
    272       syncer::SyncProtocolError protocolError;
    273       protocolError.action = syncer::STOP_AND_RESTART_SYNC;
    274       browser_sync::SyncBackendHost::Status status;
    275       status.sync_protocol_error = protocolError;
    276       EXPECT_CALL(service, QueryDetailedSyncStatus(_))
    277                   .WillRepeatedly(DoAll(SetArgPointee<0>(status),
    278                                   Return(false)));
    279       EXPECT_CALL(service, HasUnrecoverableError())
    280                   .WillRepeatedly(Return(false));
    281       return;
    282     }
    283     case STATUS_CASE_PASSPHRASE_ERROR: {
    284       EXPECT_CALL(service, HasSyncSetupCompleted())
    285                   .WillRepeatedly(Return(true));
    286       EXPECT_CALL(service, sync_initialized()).WillRepeatedly(Return(true));
    287       browser_sync::SyncBackendHost::Status status;
    288       EXPECT_CALL(service, QueryDetailedSyncStatus(_))
    289                   .WillRepeatedly(DoAll(SetArgPointee<0>(status),
    290                                   Return(false)));
    291       EXPECT_CALL(service, HasUnrecoverableError())
    292                   .WillRepeatedly(Return(false));
    293       EXPECT_CALL(service, IsPassphraseRequired())
    294                   .WillRepeatedly(Return(true));
    295       EXPECT_CALL(service, IsPassphraseRequiredForDecryption())
    296                   .WillRepeatedly(Return(true));
    297       return;
    298     }
    299     case STATUS_CASE_SYNCED: {
    300       EXPECT_CALL(service, HasSyncSetupCompleted())
    301               .WillRepeatedly(Return(true));
    302       EXPECT_CALL(service, sync_initialized()).WillRepeatedly(Return(true));
    303       EXPECT_CALL(service, IsPassphraseRequired())
    304                   .WillRepeatedly(Return(false));
    305       browser_sync::SyncBackendHost::Status status;
    306       EXPECT_CALL(service, QueryDetailedSyncStatus(_))
    307                   .WillRepeatedly(DoAll(SetArgPointee<0>(status),
    308                                   Return(false)));
    309       EXPECT_CALL(service, HasUnrecoverableError())
    310                   .WillRepeatedly(Return(false));
    311       EXPECT_CALL(service, IsPassphraseRequired())
    312                   .WillRepeatedly(Return(false));
    313       return;
    314     }
    315     case STATUS_CASE_SYNC_DISABLED_BY_POLICY: {
    316       EXPECT_CALL(service, IsManaged()).WillRepeatedly(Return(true));
    317       EXPECT_CALL(service, HasSyncSetupCompleted())
    318           .WillRepeatedly(Return(false));
    319       EXPECT_CALL(service, sync_initialized()).WillRepeatedly(Return(false));
    320       EXPECT_CALL(service, IsPassphraseRequired())
    321                   .WillRepeatedly(Return(false));
    322       browser_sync::SyncBackendHost::Status status;
    323       EXPECT_CALL(service, QueryDetailedSyncStatus(_))
    324                   .WillRepeatedly(DoAll(SetArgPointee<0>(status),
    325                                   Return(false)));
    326       EXPECT_CALL(service, HasUnrecoverableError())
    327                   .WillRepeatedly(Return(false));
    328       return;
    329     }
    330     default:
    331       NOTREACHED();
    332   }
    333 }
    334 
    335 // This test ensures that a each distinctive ProfileSyncService statuses
    336 // will return a unique combination of status and link messages from
    337 // GetStatusLabels().
    338 TEST_F(SyncUIUtilTest, DistinctCasesReportUniqueMessageSets) {
    339   std::set<base::string16> messages;
    340   for (int idx = 0; idx != NUMBER_OF_STATUS_CASES; idx++) {
    341     scoped_ptr<Profile> profile(new TestingProfile());
    342     ProfileSyncServiceMock service(profile.get());
    343     GoogleServiceAuthError error = GoogleServiceAuthError::AuthErrorNone();
    344     EXPECT_CALL(service, GetAuthError()).WillRepeatedly(ReturnRef(error));
    345     FakeSigninManagerForSyncUIUtilTest signin(profile.get());
    346     signin.SetAuthenticatedUsername(kTestUser);
    347     scoped_ptr<FakeAuthStatusProvider> provider(new FakeAuthStatusProvider(
    348         ProfileOAuth2TokenServiceFactory::GetForProfile(profile.get())->
    349             signin_error_controller()));
    350     GetDistinctCase(service, &signin, provider.get(), idx);
    351     base::string16 status_label;
    352     base::string16 link_label;
    353     sync_ui_util::GetStatusLabels(&service,
    354                                   signin,
    355                                   sync_ui_util::WITH_HTML,
    356                                   &status_label,
    357                                   &link_label);
    358     // If the status and link message combination is already present in the set
    359     // of messages already seen, this is a duplicate rather than a unique
    360     // message, and the test has failed.
    361     EXPECT_FALSE(status_label.empty()) <<
    362         "Empty status label returned for case #" << idx;
    363     base::string16 combined_label =
    364         status_label + base::ASCIIToUTF16("#") + link_label;
    365     EXPECT_TRUE(messages.find(combined_label) == messages.end()) <<
    366         "Duplicate message for case #" << idx << ": " << combined_label;
    367     messages.insert(combined_label);
    368     testing::Mock::VerifyAndClearExpectations(&service);
    369     testing::Mock::VerifyAndClearExpectations(&signin);
    370     EXPECT_CALL(service, GetAuthError()).WillRepeatedly(ReturnRef(error));
    371     provider.reset();
    372     signin.Shutdown();
    373   }
    374 }
    375 
    376 // This test ensures that the html_links parameter on GetStatusLabels() is
    377 // honored.
    378 TEST_F(SyncUIUtilTest, HtmlNotIncludedInStatusIfNotRequested) {
    379   for (int idx = 0; idx != NUMBER_OF_STATUS_CASES; idx++) {
    380     scoped_ptr<Profile> profile(
    381         ProfileSyncServiceMock::MakeSignedInTestingProfile());
    382     ProfileSyncServiceMock service(profile.get());
    383     GoogleServiceAuthError error = GoogleServiceAuthError::AuthErrorNone();
    384     EXPECT_CALL(service, GetAuthError()).WillRepeatedly(ReturnRef(error));
    385     FakeSigninManagerForSyncUIUtilTest signin(profile.get());
    386     signin.SetAuthenticatedUsername(kTestUser);
    387     scoped_ptr<FakeAuthStatusProvider> provider(new FakeAuthStatusProvider(
    388         ProfileOAuth2TokenServiceFactory::GetForProfile(profile.get())->
    389             signin_error_controller()));
    390     GetDistinctCase(service, &signin, provider.get(), idx);
    391     base::string16 status_label;
    392     base::string16 link_label;
    393     sync_ui_util::GetStatusLabels(&service,
    394                                   signin,
    395                                   sync_ui_util::PLAIN_TEXT,
    396                                   &status_label,
    397                                   &link_label);
    398 
    399     // Ensures a search for string 'href' (found in links, not a string to be
    400     // found in an English language message) fails when links are excluded from
    401     // the status label.
    402     EXPECT_FALSE(status_label.empty());
    403     EXPECT_EQ(status_label.find(base::ASCIIToUTF16("href")),
    404               base::string16::npos);
    405     testing::Mock::VerifyAndClearExpectations(&service);
    406     testing::Mock::VerifyAndClearExpectations(&signin);
    407     EXPECT_CALL(service, GetAuthError()).WillRepeatedly(ReturnRef(error));
    408     provider.reset();
    409     signin.Shutdown();
    410   }
    411 }
    412