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_components_factory_mock.h"
13 #include "chrome/browser/sync/profile_sync_service_factory.h"
14 #include "chrome/browser/sync/startup_controller.h"
15 #include "chrome/browser/sync/test_profile_sync_service.h"
16 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "components/signin/core/browser/signin_manager.h"
19 #include "content/public/browser/render_frame_host.h"
20 #include "content/public/browser/web_contents.h"
21 #include "content/public/test/test_utils.h"
22 #include "testing/gmock/include/gmock/gmock.h"
23 #include "testing/gtest/include/gtest/gtest.h"
29 const char kContinueUrl
[] = "https://www.example.com/";
31 class MockWebContentsObserver
: public content::WebContentsObserver
{
33 explicit MockWebContentsObserver(content::WebContents
* web_contents
)
34 : content::WebContentsObserver(web_contents
) {}
35 virtual ~MockWebContentsObserver() {}
37 // A hook to verify that the OneClickSigninSyncObserver initiated a redirect
38 // to the continue URL. Navigations in unit_tests never complete, but a
39 // navigation start is a sufficient signal for the purposes of this test.
40 // Listening for this call also has the advantage of being synchronous.
41 MOCK_METHOD2(AboutToNavigateRenderFrame
, void(content::RenderFrameHost
*,
42 content::RenderFrameHost
*));
45 class OneClickTestProfileSyncService
: public TestProfileSyncService
{
47 ~OneClickTestProfileSyncService() override
{}
49 // Helper routine to be used in conjunction with
50 // BrowserContextKeyedServiceFactory::SetTestingFactory().
51 static KeyedService
* Build(content::BrowserContext
* profile
) {
52 return new OneClickTestProfileSyncService(static_cast<Profile
*>(profile
));
55 bool FirstSetupInProgress() const override
{
56 return first_setup_in_progress_
;
59 bool SyncActive() const override
{ return sync_active_
; }
61 void set_first_setup_in_progress(bool in_progress
) {
62 first_setup_in_progress_
= in_progress
;
65 void set_sync_active(bool active
) {
66 sync_active_
= active
;
70 explicit OneClickTestProfileSyncService(Profile
* profile
)
71 : TestProfileSyncService(
72 scoped_ptr
<ProfileSyncComponentsFactory
>(
73 new ProfileSyncComponentsFactoryMock()),
75 SigninManagerFactory::GetForProfile(profile
),
76 ProfileOAuth2TokenServiceFactory::GetForProfile(profile
),
77 browser_sync::MANUAL_START
),
78 first_setup_in_progress_(false),
79 sync_active_(false) {}
81 bool first_setup_in_progress_
;
85 class TestOneClickSigninSyncObserver
: public OneClickSigninSyncObserver
{
87 typedef base::Callback
<void(TestOneClickSigninSyncObserver
*)>
90 TestOneClickSigninSyncObserver(content::WebContents
* web_contents
,
91 const GURL
& continue_url
,
92 const DestructionCallback
& callback
)
93 : OneClickSigninSyncObserver(web_contents
, continue_url
),
94 destruction_callback_(callback
) {}
95 ~TestOneClickSigninSyncObserver() override
{
96 destruction_callback_
.Run(this);
100 DestructionCallback destruction_callback_
;
102 DISALLOW_COPY_AND_ASSIGN(TestOneClickSigninSyncObserver
);
105 // A trivial factory to build a null service.
106 KeyedService
* BuildNullService(content::BrowserContext
* context
) {
112 class OneClickSigninSyncObserverTest
: public ChromeRenderViewHostTestHarness
{
114 OneClickSigninSyncObserverTest()
115 : sync_service_(NULL
),
116 sync_observer_(NULL
),
117 sync_observer_destroyed_(true) {}
119 void SetUp() override
{
120 ChromeRenderViewHostTestHarness::SetUp();
121 web_contents_observer_
.reset(new MockWebContentsObserver(web_contents()));
123 static_cast<OneClickTestProfileSyncService
*>(
124 ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
125 profile(), OneClickTestProfileSyncService::Build
));
128 void TearDown() override
{
129 // Verify that the |sync_observer_| unregistered as an observer from the
130 // sync service and freed its memory.
131 EXPECT_TRUE(sync_observer_destroyed_
);
133 EXPECT_FALSE(sync_service_
->HasObserver(sync_observer_
));
134 ChromeRenderViewHostTestHarness::TearDown();
138 void CreateSyncObserver(const std::string
& url
) {
139 sync_observer_
= new TestOneClickSigninSyncObserver(
140 web_contents(), GURL(url
),
141 base::Bind(&OneClickSigninSyncObserverTest::OnSyncObserverDestroyed
,
142 base::Unretained(this)));
144 EXPECT_TRUE(sync_service_
->HasObserver(sync_observer_
));
145 EXPECT_TRUE(sync_observer_destroyed_
);
146 sync_observer_destroyed_
= false;
149 OneClickTestProfileSyncService
* sync_service_
;
150 scoped_ptr
<MockWebContentsObserver
> web_contents_observer_
;
153 void OnSyncObserverDestroyed(TestOneClickSigninSyncObserver
* observer
) {
154 EXPECT_EQ(sync_observer_
, observer
);
155 EXPECT_FALSE(sync_observer_destroyed_
);
156 sync_observer_destroyed_
= true;
159 TestOneClickSigninSyncObserver
* sync_observer_
;
160 bool sync_observer_destroyed_
;
163 // Verify that if no Sync service is present, e.g. because Sync is disabled, the
164 // observer immediately loads the continue URL.
165 TEST_F(OneClickSigninSyncObserverTest
, NoSyncService_RedirectsImmediately
) {
166 // Simulate disabling Sync.
168 static_cast<OneClickTestProfileSyncService
*>(
169 ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
170 profile(), BuildNullService
));
172 // The observer should immediately redirect to the continue URL.
173 EXPECT_CALL(*web_contents_observer_
, AboutToNavigateRenderFrame(_
, _
));
174 CreateSyncObserver(kContinueUrl
);
175 EXPECT_EQ(GURL(kContinueUrl
), web_contents()->GetVisibleURL());
177 // The |sync_observer_| will be destroyed asynchronously, so manually pump
178 // the message loop to wait for the destruction.
179 content::RunAllPendingInMessageLoop();
182 // Verify that when the WebContents is destroyed without any Sync notifications
183 // firing, the observer cleans up its memory without loading the continue URL.
184 TEST_F(OneClickSigninSyncObserverTest
, WebContentsDestroyed
) {
185 EXPECT_CALL(*web_contents_observer_
,
186 AboutToNavigateRenderFrame(_
, _
)).Times(0);
187 CreateSyncObserver(kContinueUrl
);
191 // Verify that when Sync is configured successfully, the observer loads the
192 // continue URL and cleans up after itself.
193 TEST_F(OneClickSigninSyncObserverTest
,
194 OnSyncStateChanged_SyncConfiguredSuccessfully
) {
195 CreateSyncObserver(kContinueUrl
);
196 sync_service_
->set_first_setup_in_progress(false);
197 sync_service_
->set_sync_active(true);
199 EXPECT_CALL(*web_contents_observer_
, AboutToNavigateRenderFrame(_
, _
));
200 sync_service_
->NotifyObservers();
201 EXPECT_EQ(GURL(kContinueUrl
), web_contents()->GetVisibleURL());
204 // Verify that when Sync configuration fails, the observer does not load the
205 // continue URL, but still cleans up after itself.
206 TEST_F(OneClickSigninSyncObserverTest
,
207 OnSyncStateChanged_SyncConfigurationFailed
) {
208 CreateSyncObserver(kContinueUrl
);
209 sync_service_
->set_first_setup_in_progress(false);
210 sync_service_
->set_sync_active(false);
212 EXPECT_CALL(*web_contents_observer_
,
213 AboutToNavigateRenderFrame(_
, _
)).Times(0);
214 sync_service_
->NotifyObservers();
215 EXPECT_NE(GURL(kContinueUrl
), web_contents()->GetVisibleURL());
218 // Verify that when Sync sends a notification while setup is not yet complete,
219 // the observer does not load the continue URL, and continues to wait.
220 TEST_F(OneClickSigninSyncObserverTest
,
221 OnSyncStateChanged_SyncConfigurationInProgress
) {
222 CreateSyncObserver(kContinueUrl
);
223 sync_service_
->set_first_setup_in_progress(true);
224 sync_service_
->set_sync_active(false);
226 EXPECT_CALL(*web_contents_observer_
,
227 AboutToNavigateRenderFrame(_
, _
)).Times(0);
228 sync_service_
->NotifyObservers();
229 EXPECT_NE(GURL(kContinueUrl
), web_contents()->GetVisibleURL());
231 // Trigger an event to force state to be cleaned up.
235 // Verify that if the continue_url is to the settings page, no navigation is
236 // triggered, since it would be redundant.
237 TEST_F(OneClickSigninSyncObserverTest
,
238 OnSyncStateChanged_SyncConfiguredSuccessfully_SourceIsSettings
) {
239 GURL continue_url
= signin::GetPromoURL(
240 signin_metrics::SOURCE_SETTINGS
, false);
241 CreateSyncObserver(continue_url
.spec());
242 sync_service_
->set_first_setup_in_progress(false);
243 sync_service_
->set_sync_active(true);
245 EXPECT_CALL(*web_contents_observer_
,
246 AboutToNavigateRenderFrame(_
, _
)).Times(0);
247 sync_service_
->NotifyObservers();
248 EXPECT_NE(GURL(kContinueUrl
), web_contents()->GetVisibleURL());