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.
5 #include "chrome/browser/ui/sync/one_click_signin_sync_observer.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"
28 const char kContinueUrl
[] = "https://www.example.com/";
30 class MockWebContentsObserver
: public content::WebContentsObserver
{
32 explicit MockWebContentsObserver(content::WebContents
* web_contents
)
33 : content::WebContentsObserver(web_contents
) {}
34 virtual ~MockWebContentsObserver() {}
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
*));
43 class OneClickTestProfileSyncService
: public TestProfileSyncService
{
45 virtual ~OneClickTestProfileSyncService() {}
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
));
53 virtual bool FirstSetupInProgress() const OVERRIDE
{
54 return first_setup_in_progress_
;
57 virtual bool sync_initialized() const OVERRIDE
{ return sync_initialized_
; }
59 void set_first_setup_in_progress(bool in_progress
) {
60 first_setup_in_progress_
= in_progress
;
63 void set_sync_initialized(bool initialized
) {
64 sync_initialized_
= initialized
;
68 explicit OneClickTestProfileSyncService(Profile
* profile
)
69 : TestProfileSyncService(
72 SigninManagerFactory::GetForProfile(profile
),
73 ProfileOAuth2TokenServiceFactory::GetForProfile(profile
),
74 browser_sync::MANUAL_START
),
75 first_setup_in_progress_(false),
76 sync_initialized_(false) {}
78 bool first_setup_in_progress_
;
79 bool sync_initialized_
;
82 class TestOneClickSigninSyncObserver
: public OneClickSigninSyncObserver
{
84 typedef base::Callback
<void(TestOneClickSigninSyncObserver
*)>
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); }
95 DestructionCallback destruction_callback_
;
97 DISALLOW_COPY_AND_ASSIGN(TestOneClickSigninSyncObserver
);
100 // A trivial factory to build a null service.
101 KeyedService
* BuildNullService(content::BrowserContext
* context
) {
107 class OneClickSigninSyncObserverTest
: public ChromeRenderViewHostTestHarness
{
109 OneClickSigninSyncObserverTest()
110 : sync_service_(NULL
),
111 sync_observer_(NULL
),
112 sync_observer_destroyed_(true) {}
114 virtual void SetUp() OVERRIDE
{
115 ChromeRenderViewHostTestHarness::SetUp();
116 web_contents_observer_
.reset(new MockWebContentsObserver(web_contents()));
118 static_cast<OneClickTestProfileSyncService
*>(
119 ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
120 profile(), OneClickTestProfileSyncService::Build
));
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_
);
128 EXPECT_FALSE(sync_service_
->HasObserver(sync_observer_
));
129 ChromeRenderViewHostTestHarness::TearDown();
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)));
139 EXPECT_TRUE(sync_service_
->HasObserver(sync_observer_
));
140 EXPECT_TRUE(sync_observer_destroyed_
);
141 sync_observer_destroyed_
= false;
144 OneClickTestProfileSyncService
* sync_service_
;
145 scoped_ptr
<MockWebContentsObserver
> web_contents_observer_
;
148 void OnSyncObserverDestroyed(TestOneClickSigninSyncObserver
* observer
) {
149 EXPECT_EQ(sync_observer_
, observer
);
150 EXPECT_FALSE(sync_observer_destroyed_
);
151 sync_observer_destroyed_
= true;
154 TestOneClickSigninSyncObserver
* sync_observer_
;
155 bool sync_observer_destroyed_
;
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.
163 static_cast<OneClickTestProfileSyncService
*>(
164 ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
165 profile(), BuildNullService
));
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());
172 // The |sync_observer_| will be destroyed asynchronously, so manually pump
173 // the message loop to wait for the destruction.
174 content::RunAllPendingInMessageLoop();
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
);
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);
193 EXPECT_CALL(*web_contents_observer_
, AboutToNavigateRenderView(_
));
194 sync_service_
->NotifyObservers();
195 EXPECT_EQ(GURL(kContinueUrl
), web_contents()->GetVisibleURL());
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);
206 EXPECT_CALL(*web_contents_observer_
, AboutToNavigateRenderView(_
)).Times(0);
207 sync_service_
->NotifyObservers();
208 EXPECT_NE(GURL(kContinueUrl
), web_contents()->GetVisibleURL());
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);
219 EXPECT_CALL(*web_contents_observer_
, AboutToNavigateRenderView(_
)).Times(0);
220 sync_service_
->NotifyObservers();
221 EXPECT_NE(GURL(kContinueUrl
), web_contents()->GetVisibleURL());
223 // Trigger an event to force state to be cleaned up.
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);
236 EXPECT_CALL(*web_contents_observer_
, AboutToNavigateRenderView(_
)).Times(0);
237 sync_service_
->NotifyObservers();
238 EXPECT_NE(GURL(kContinueUrl
), web_contents()->GetVisibleURL());