Home | History | Annotate | Download | only in sync
      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/ui/sync/one_click_signin_sync_observer.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback.h"
      9 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
     10 #include "chrome/browser/signin/signin_manager_factory.h"
     11 #include "chrome/browser/signin/signin_promo.h"
     12 #include "chrome/browser/sync/profile_sync_service_factory.h"
     13 #include "chrome/browser/sync/startup_controller.h"
     14 #include "chrome/browser/sync/test_profile_sync_service.h"
     15 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
     16 #include "chrome/test/base/testing_profile.h"
     17 #include "components/signin/core/browser/signin_manager.h"
     18 #include "content/public/browser/render_view_host.h"
     19 #include "content/public/browser/web_contents.h"
     20 #include "content/public/test/test_utils.h"
     21 #include "testing/gmock/include/gmock/gmock.h"
     22 #include "testing/gtest/include/gtest/gtest.h"
     23 
     24 using testing::_;
     25 
     26 namespace {
     27 
     28 const char kContinueUrl[] = "https://www.example.com/";
     29 
     30 class MockWebContentsObserver : public content::WebContentsObserver {
     31  public:
     32   explicit MockWebContentsObserver(content::WebContents* web_contents)
     33       : content::WebContentsObserver(web_contents) {}
     34   virtual ~MockWebContentsObserver() {}
     35 
     36   // A hook to verify that the OneClickSigninSyncObserver initiated a redirect
     37   // to the continue URL. Navigations in unit_tests never complete, but a
     38   // navigation start is a sufficient signal for the purposes of this test.
     39   // Listening for this call also has the advantage of being synchronous.
     40   MOCK_METHOD1(AboutToNavigateRenderView, void(content::RenderViewHost*));
     41 };
     42 
     43 class OneClickTestProfileSyncService : public TestProfileSyncService {
     44  public:
     45   virtual ~OneClickTestProfileSyncService() {}
     46 
     47   // Helper routine to be used in conjunction with
     48   // BrowserContextKeyedServiceFactory::SetTestingFactory().
     49   static KeyedService* Build(content::BrowserContext* profile) {
     50     return new OneClickTestProfileSyncService(static_cast<Profile*>(profile));
     51   }
     52 
     53   virtual bool FirstSetupInProgress() const OVERRIDE {
     54     return first_setup_in_progress_;
     55   }
     56 
     57   virtual bool sync_initialized() const OVERRIDE { return sync_initialized_; }
     58 
     59   void set_first_setup_in_progress(bool in_progress) {
     60     first_setup_in_progress_ = in_progress;
     61   }
     62 
     63   void set_sync_initialized(bool initialized) {
     64     sync_initialized_ = initialized;
     65   }
     66 
     67  private:
     68   explicit OneClickTestProfileSyncService(Profile* profile)
     69       : TestProfileSyncService(
     70           NULL,
     71           profile,
     72           SigninManagerFactory::GetForProfile(profile),
     73           ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
     74           browser_sync::MANUAL_START),
     75         first_setup_in_progress_(false),
     76         sync_initialized_(false) {}
     77 
     78   bool first_setup_in_progress_;
     79   bool sync_initialized_;
     80 };
     81 
     82 class TestOneClickSigninSyncObserver : public OneClickSigninSyncObserver {
     83  public:
     84   typedef base::Callback<void(TestOneClickSigninSyncObserver*)>
     85       DestructionCallback;
     86 
     87   TestOneClickSigninSyncObserver(content::WebContents* web_contents,
     88                                  const GURL& continue_url,
     89                                  const DestructionCallback& callback)
     90       : OneClickSigninSyncObserver(web_contents, continue_url),
     91         destruction_callback_(callback) {}
     92   virtual ~TestOneClickSigninSyncObserver() { destruction_callback_.Run(this); }
     93 
     94  private:
     95   DestructionCallback destruction_callback_;
     96 
     97   DISALLOW_COPY_AND_ASSIGN(TestOneClickSigninSyncObserver);
     98 };
     99 
    100 // A trivial factory to build a null service.
    101 KeyedService* BuildNullService(content::BrowserContext* context) {
    102   return NULL;
    103 }
    104 
    105 }  // namespace
    106 
    107 class OneClickSigninSyncObserverTest : public ChromeRenderViewHostTestHarness {
    108  public:
    109   OneClickSigninSyncObserverTest()
    110       : sync_service_(NULL),
    111         sync_observer_(NULL),
    112         sync_observer_destroyed_(true) {}
    113 
    114   virtual void SetUp() OVERRIDE {
    115     ChromeRenderViewHostTestHarness::SetUp();
    116     web_contents_observer_.reset(new MockWebContentsObserver(web_contents()));
    117     sync_service_ =
    118         static_cast<OneClickTestProfileSyncService*>(
    119             ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
    120                 profile(), OneClickTestProfileSyncService::Build));
    121   }
    122 
    123   virtual void TearDown() OVERRIDE {
    124     // Verify that the |sync_observer_| unregistered as an observer from the
    125     // sync service and freed its memory.
    126     EXPECT_TRUE(sync_observer_destroyed_);
    127     if (sync_service_)
    128       EXPECT_FALSE(sync_service_->HasObserver(sync_observer_));
    129     ChromeRenderViewHostTestHarness::TearDown();
    130   }
    131 
    132  protected:
    133   void CreateSyncObserver(const std::string& url) {
    134     sync_observer_ = new TestOneClickSigninSyncObserver(
    135       web_contents(), GURL(url),
    136       base::Bind(&OneClickSigninSyncObserverTest::OnSyncObserverDestroyed,
    137                  base::Unretained(this)));
    138     if (sync_service_)
    139       EXPECT_TRUE(sync_service_->HasObserver(sync_observer_));
    140     EXPECT_TRUE(sync_observer_destroyed_);
    141     sync_observer_destroyed_ = false;
    142   }
    143 
    144   OneClickTestProfileSyncService* sync_service_;
    145   scoped_ptr<MockWebContentsObserver> web_contents_observer_;
    146 
    147  private:
    148   void OnSyncObserverDestroyed(TestOneClickSigninSyncObserver* observer) {
    149     EXPECT_EQ(sync_observer_, observer);
    150     EXPECT_FALSE(sync_observer_destroyed_);
    151     sync_observer_destroyed_ = true;
    152   }
    153 
    154   TestOneClickSigninSyncObserver* sync_observer_;
    155   bool sync_observer_destroyed_;
    156 };
    157 
    158 // Verify that if no Sync service is present, e.g. because Sync is disabled, the
    159 // observer immediately loads the continue URL.
    160 TEST_F(OneClickSigninSyncObserverTest, NoSyncService_RedirectsImmediately) {
    161   // Simulate disabling Sync.
    162   sync_service_ =
    163       static_cast<OneClickTestProfileSyncService*>(
    164           ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
    165               profile(), BuildNullService));
    166 
    167   // The observer should immediately redirect to the continue URL.
    168   EXPECT_CALL(*web_contents_observer_, AboutToNavigateRenderView(_));
    169   CreateSyncObserver(kContinueUrl);
    170   EXPECT_EQ(GURL(kContinueUrl), web_contents()->GetVisibleURL());
    171 
    172   // The |sync_observer_| will be destroyed asynchronously, so manually pump
    173   // the message loop to wait for the destruction.
    174   content::RunAllPendingInMessageLoop();
    175 }
    176 
    177 // Verify that when the WebContents is destroyed without any Sync notifications
    178 // firing, the observer cleans up its memory without loading the continue URL.
    179 TEST_F(OneClickSigninSyncObserverTest, WebContentsDestroyed) {
    180   EXPECT_CALL(*web_contents_observer_, AboutToNavigateRenderView(_)).Times(0);
    181   CreateSyncObserver(kContinueUrl);
    182   SetContents(NULL);
    183 }
    184 
    185 // Verify that when Sync is configured successfully, the observer loads the
    186 // continue URL and cleans up after itself.
    187 TEST_F(OneClickSigninSyncObserverTest,
    188        OnSyncStateChanged_SyncConfiguredSuccessfully) {
    189   CreateSyncObserver(kContinueUrl);
    190   sync_service_->set_first_setup_in_progress(false);
    191   sync_service_->set_sync_initialized(true);
    192 
    193   EXPECT_CALL(*web_contents_observer_, AboutToNavigateRenderView(_));
    194   sync_service_->NotifyObservers();
    195   EXPECT_EQ(GURL(kContinueUrl), web_contents()->GetVisibleURL());
    196 }
    197 
    198 // Verify that when Sync configuration fails, the observer does not load the
    199 // continue URL, but still cleans up after itself.
    200 TEST_F(OneClickSigninSyncObserverTest,
    201        OnSyncStateChanged_SyncConfigurationFailed) {
    202   CreateSyncObserver(kContinueUrl);
    203   sync_service_->set_first_setup_in_progress(false);
    204   sync_service_->set_sync_initialized(false);
    205 
    206   EXPECT_CALL(*web_contents_observer_, AboutToNavigateRenderView(_)).Times(0);
    207   sync_service_->NotifyObservers();
    208   EXPECT_NE(GURL(kContinueUrl), web_contents()->GetVisibleURL());
    209 }
    210 
    211 // Verify that when Sync sends a notification while setup is not yet complete,
    212 // the observer does not load the continue URL, and continues to wait.
    213 TEST_F(OneClickSigninSyncObserverTest,
    214        OnSyncStateChanged_SyncConfigurationInProgress) {
    215   CreateSyncObserver(kContinueUrl);
    216   sync_service_->set_first_setup_in_progress(true);
    217   sync_service_->set_sync_initialized(false);
    218 
    219   EXPECT_CALL(*web_contents_observer_, AboutToNavigateRenderView(_)).Times(0);
    220   sync_service_->NotifyObservers();
    221   EXPECT_NE(GURL(kContinueUrl), web_contents()->GetVisibleURL());
    222 
    223   // Trigger an event to force state to be cleaned up.
    224   SetContents(NULL);
    225 }
    226 
    227 // Verify that if the continue_url is to the settings page, no navigation is
    228 // triggered, since it would be redundant.
    229 TEST_F(OneClickSigninSyncObserverTest,
    230        OnSyncStateChanged_SyncConfiguredSuccessfully_SourceIsSettings) {
    231   GURL continue_url = signin::GetPromoURL(signin::SOURCE_SETTINGS, false);
    232   CreateSyncObserver(continue_url.spec());
    233   sync_service_->set_first_setup_in_progress(false);
    234   sync_service_->set_sync_initialized(true);
    235 
    236   EXPECT_CALL(*web_contents_observer_, AboutToNavigateRenderView(_)).Times(0);
    237   sync_service_->NotifyObservers();
    238   EXPECT_NE(GURL(kContinueUrl), web_contents()->GetVisibleURL());
    239 }
    240