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 "base/memory/scoped_ptr.h"
10 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
11 #include "chrome/browser/signin/signin_manager_factory.h"
12 #include "chrome/browser/signin/signin_promo.h"
13 #include "chrome/browser/sync/profile_sync_components_factory_mock.h"
14 #include "chrome/browser/sync/profile_sync_service_factory.h"
15 #include "chrome/browser/sync/startup_controller.h"
16 #include "chrome/browser/sync/test_profile_sync_service.h"
17 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
18 #include "chrome/test/base/testing_profile.h"
19 #include "components/signin/core/browser/signin_manager.h"
20 #include "content/public/browser/render_frame_host.h"
21 #include "content/public/browser/web_contents.h"
22 #include "content/public/test/test_utils.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
30 const char kContinueUrl
[] = "https://www.example.com/";
32 class MockWebContentsObserver
: public content::WebContentsObserver
{
34 explicit MockWebContentsObserver(content::WebContents
* web_contents
)
35 : content::WebContentsObserver(web_contents
) {}
36 virtual ~MockWebContentsObserver() {}
38 // A hook to verify that the OneClickSigninSyncObserver initiated a redirect
39 // to the continue URL. Navigations in unit_tests never complete, but a
40 // navigation start is a sufficient signal for the purposes of this test.
41 // Listening for this call also has the advantage of being synchronous.
42 MOCK_METHOD2(AboutToNavigateRenderFrame
, void(content::RenderFrameHost
*,
43 content::RenderFrameHost
*));
46 class OneClickTestProfileSyncService
: public TestProfileSyncService
{
48 ~OneClickTestProfileSyncService() override
{}
50 // Helper routine to be used in conjunction with
51 // BrowserContextKeyedServiceFactory::SetTestingFactory().
52 static scoped_ptr
<KeyedService
> Build(content::BrowserContext
* profile
) {
53 return make_scoped_ptr(
54 new OneClickTestProfileSyncService(static_cast<Profile
*>(profile
)));
57 bool FirstSetupInProgress() const override
{
58 return first_setup_in_progress_
;
61 bool IsSyncActive() const override
{ return sync_active_
; }
63 void set_first_setup_in_progress(bool in_progress
) {
64 first_setup_in_progress_
= in_progress
;
67 void set_sync_active(bool active
) {
68 sync_active_
= active
;
72 explicit OneClickTestProfileSyncService(Profile
* profile
)
73 : TestProfileSyncService(
74 scoped_ptr
<sync_driver::SyncApiComponentFactory
>(
75 new ProfileSyncComponentsFactoryMock()),
77 SigninManagerFactory::GetForProfile(profile
),
78 ProfileOAuth2TokenServiceFactory::GetForProfile(profile
),
79 browser_sync::MANUAL_START
),
80 first_setup_in_progress_(false),
81 sync_active_(false) {}
83 bool first_setup_in_progress_
;
87 class TestOneClickSigninSyncObserver
: public OneClickSigninSyncObserver
{
89 typedef base::Callback
<void(TestOneClickSigninSyncObserver
*)>
92 TestOneClickSigninSyncObserver(content::WebContents
* web_contents
,
93 const GURL
& continue_url
,
94 const DestructionCallback
& callback
)
95 : OneClickSigninSyncObserver(web_contents
, continue_url
),
96 destruction_callback_(callback
) {}
97 ~TestOneClickSigninSyncObserver() override
{
98 destruction_callback_
.Run(this);
102 DestructionCallback destruction_callback_
;
104 DISALLOW_COPY_AND_ASSIGN(TestOneClickSigninSyncObserver
);
107 // A trivial factory to build a null service.
108 scoped_ptr
<KeyedService
> BuildNullService(content::BrowserContext
* context
) {
114 class OneClickSigninSyncObserverTest
: public ChromeRenderViewHostTestHarness
{
116 OneClickSigninSyncObserverTest()
117 : sync_service_(NULL
),
118 sync_observer_(NULL
),
119 sync_observer_destroyed_(true) {}
121 void SetUp() override
{
122 ChromeRenderViewHostTestHarness::SetUp();
123 web_contents_observer_
.reset(new MockWebContentsObserver(web_contents()));
125 static_cast<OneClickTestProfileSyncService
*>(
126 ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
127 profile(), OneClickTestProfileSyncService::Build
));
130 void TearDown() override
{
131 // Verify that the |sync_observer_| unregistered as an observer from the
132 // sync service and freed its memory.
133 EXPECT_TRUE(sync_observer_destroyed_
);
135 EXPECT_FALSE(sync_service_
->HasObserver(sync_observer_
));
136 ChromeRenderViewHostTestHarness::TearDown();
140 void CreateSyncObserver(const std::string
& url
) {
141 sync_observer_
= new TestOneClickSigninSyncObserver(
142 web_contents(), GURL(url
),
143 base::Bind(&OneClickSigninSyncObserverTest::OnSyncObserverDestroyed
,
144 base::Unretained(this)));
146 EXPECT_TRUE(sync_service_
->HasObserver(sync_observer_
));
147 EXPECT_TRUE(sync_observer_destroyed_
);
148 sync_observer_destroyed_
= false;
151 OneClickTestProfileSyncService
* sync_service_
;
152 scoped_ptr
<MockWebContentsObserver
> web_contents_observer_
;
155 void OnSyncObserverDestroyed(TestOneClickSigninSyncObserver
* observer
) {
156 EXPECT_EQ(sync_observer_
, observer
);
157 EXPECT_FALSE(sync_observer_destroyed_
);
158 sync_observer_destroyed_
= true;
161 TestOneClickSigninSyncObserver
* sync_observer_
;
162 bool sync_observer_destroyed_
;
165 // Verify that if no Sync service is present, e.g. because Sync is disabled, the
166 // observer immediately loads the continue URL.
167 TEST_F(OneClickSigninSyncObserverTest
, NoSyncService_RedirectsImmediately
) {
168 // Simulate disabling Sync.
170 static_cast<OneClickTestProfileSyncService
*>(
171 ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
172 profile(), BuildNullService
));
174 // The observer should immediately redirect to the continue URL.
175 EXPECT_CALL(*web_contents_observer_
, AboutToNavigateRenderFrame(_
, _
));
176 CreateSyncObserver(kContinueUrl
);
177 EXPECT_EQ(GURL(kContinueUrl
), web_contents()->GetVisibleURL());
179 // The |sync_observer_| will be destroyed asynchronously, so manually pump
180 // the message loop to wait for the destruction.
181 content::RunAllPendingInMessageLoop();
184 // Verify that when the WebContents is destroyed without any Sync notifications
185 // firing, the observer cleans up its memory without loading the continue URL.
186 TEST_F(OneClickSigninSyncObserverTest
, WebContentsDestroyed
) {
187 EXPECT_CALL(*web_contents_observer_
,
188 AboutToNavigateRenderFrame(_
, _
)).Times(0);
189 CreateSyncObserver(kContinueUrl
);
193 // Verify that when Sync is configured successfully, the observer loads the
194 // continue URL and cleans up after itself.
195 TEST_F(OneClickSigninSyncObserverTest
,
196 OnSyncStateChanged_SyncConfiguredSuccessfully
) {
197 CreateSyncObserver(kContinueUrl
);
198 sync_service_
->set_first_setup_in_progress(false);
199 sync_service_
->set_sync_active(true);
201 EXPECT_CALL(*web_contents_observer_
, AboutToNavigateRenderFrame(_
, _
));
202 sync_service_
->NotifyObservers();
203 EXPECT_EQ(GURL(kContinueUrl
), web_contents()->GetVisibleURL());
206 // Verify that when Sync configuration fails, the observer does not load the
207 // continue URL, but still cleans up after itself.
208 TEST_F(OneClickSigninSyncObserverTest
,
209 OnSyncStateChanged_SyncConfigurationFailed
) {
210 CreateSyncObserver(kContinueUrl
);
211 sync_service_
->set_first_setup_in_progress(false);
212 sync_service_
->set_sync_active(false);
214 EXPECT_CALL(*web_contents_observer_
,
215 AboutToNavigateRenderFrame(_
, _
)).Times(0);
216 sync_service_
->NotifyObservers();
217 EXPECT_NE(GURL(kContinueUrl
), web_contents()->GetVisibleURL());
220 // Verify that when Sync sends a notification while setup is not yet complete,
221 // the observer does not load the continue URL, and continues to wait.
222 TEST_F(OneClickSigninSyncObserverTest
,
223 OnSyncStateChanged_SyncConfigurationInProgress
) {
224 CreateSyncObserver(kContinueUrl
);
225 sync_service_
->set_first_setup_in_progress(true);
226 sync_service_
->set_sync_active(false);
228 EXPECT_CALL(*web_contents_observer_
,
229 AboutToNavigateRenderFrame(_
, _
)).Times(0);
230 sync_service_
->NotifyObservers();
231 EXPECT_NE(GURL(kContinueUrl
), web_contents()->GetVisibleURL());
233 // Trigger an event to force state to be cleaned up.
237 // Verify that if the continue_url is to the settings page, no navigation is
238 // triggered, since it would be redundant.
239 TEST_F(OneClickSigninSyncObserverTest
,
240 OnSyncStateChanged_SyncConfiguredSuccessfully_SourceIsSettings
) {
241 GURL continue_url
= signin::GetPromoURL(
242 signin_metrics::SOURCE_SETTINGS
, false);
243 CreateSyncObserver(continue_url
.spec());
244 sync_service_
->set_first_setup_in_progress(false);
245 sync_service_
->set_sync_active(true);
247 EXPECT_CALL(*web_contents_observer_
,
248 AboutToNavigateRenderFrame(_
, _
)).Times(0);
249 sync_service_
->NotifyObservers();
250 EXPECT_NE(GURL(kContinueUrl
), web_contents()->GetVisibleURL());