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.
5 #include "chrome/browser/sync/test/integration/sessions_helper.h"
10 #include "base/command_line.h"
11 #include "base/location.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/stl_util.h"
15 #include "base/test/test_timeouts.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "base/time/time.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/sync/profile_sync_service.h"
20 #include "chrome/browser/sync/profile_sync_service_factory.h"
21 #include "chrome/browser/sync/sessions/notification_service_sessions_router.h"
22 #include "chrome/browser/sync/sessions/sessions_sync_manager.h"
23 #include "chrome/browser/sync/test/integration/multi_client_status_change_checker.h"
24 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
25 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
26 #include "chrome/browser/sync/test/integration/sync_test.h"
27 #include "chrome/browser/ui/singleton_tabs.h"
28 #include "chrome/common/chrome_switches.h"
29 #include "components/sync_driver/open_tabs_ui_delegate.h"
30 #include "content/public/test/test_utils.h"
33 using sync_datatype_helper::test
;
35 namespace sessions_helper
{
37 ScopedWindowMap::ScopedWindowMap() {
40 ScopedWindowMap::ScopedWindowMap(SessionWindowMap
* windows
) {
44 ScopedWindowMap::~ScopedWindowMap() {
45 STLDeleteContainerPairSecondPointers(windows_
.begin(), windows_
.end());
48 SessionWindowMap
* ScopedWindowMap::GetMutable() {
52 const SessionWindowMap
* ScopedWindowMap::Get() const {
56 void ScopedWindowMap::Reset(SessionWindowMap
* windows
) {
57 STLDeleteContainerPairSecondPointers(windows_
.begin(), windows_
.end());
59 std::swap(*windows
, windows_
);
62 bool GetLocalSession(int index
, const sync_driver::SyncedSession
** session
) {
63 return ProfileSyncServiceFactory::GetInstance()->GetForProfile(
64 test()->GetProfile(index
))->GetOpenTabsUIDelegate()->
65 GetLocalSession(session
);
68 bool ModelAssociatorHasTabWithUrl(int index
, const GURL
& url
) {
69 content::RunAllPendingInMessageLoop();
70 const sync_driver::SyncedSession
* local_session
;
71 if (!GetLocalSession(index
, &local_session
)) {
75 if (local_session
->windows
.size() == 0) {
76 DVLOG(1) << "Empty windows vector";
81 sessions::SerializedNavigationEntry nav
;
82 for (SessionWindowMap::const_iterator it
=
83 local_session
->windows
.begin();
84 it
!= local_session
->windows
.end(); ++it
) {
85 if (it
->second
->tabs
.size() == 0) {
86 DVLOG(1) << "Empty tabs vector";
89 for (std::vector
<sessions::SessionTab
*>::const_iterator tab_it
=
90 it
->second
->tabs
.begin();
91 tab_it
!= it
->second
->tabs
.end(); ++tab_it
) {
92 if ((*tab_it
)->navigations
.size() == 0) {
93 DVLOG(1) << "Empty navigations vector";
96 nav_index
= (*tab_it
)->current_navigation_index
;
97 nav
= (*tab_it
)->navigations
[nav_index
];
98 if (nav
.virtual_url() == url
) {
99 DVLOG(1) << "Found tab with url " << url
.spec();
100 DVLOG(1) << "Timestamp is " << nav
.timestamp().ToInternalValue();
101 if (nav
.title().empty()) {
102 DVLOG(1) << "Title empty -- tab hasn't finished loading yet";
109 DVLOG(1) << "Could not find tab with url " << url
.spec();
113 bool OpenTab(int index
, const GURL
& url
) {
114 DVLOG(1) << "Opening tab: " << url
.spec() << " using profile "
116 chrome::ShowSingletonTab(test()->GetBrowser(index
), url
);
117 return WaitForTabsToLoad(index
, std::vector
<GURL
>(1, url
));
120 bool OpenMultipleTabs(int index
, const std::vector
<GURL
>& urls
) {
121 Browser
* browser
= test()->GetBrowser(index
);
122 for (std::vector
<GURL
>::const_iterator it
= urls
.begin();
123 it
!= urls
.end(); ++it
) {
124 DVLOG(1) << "Opening tab: " << it
->spec() << " using profile " << index
126 chrome::ShowSingletonTab(browser
, *it
);
128 return WaitForTabsToLoad(index
, urls
);
133 class TabEventHandler
: public browser_sync::LocalSessionEventHandler
{
135 TabEventHandler() : weak_factory_(this) {
136 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
138 base::Bind(&TabEventHandler::QuitLoop
, weak_factory_
.GetWeakPtr()),
139 TestTimeouts::action_max_timeout());
142 void OnLocalTabModified(
143 browser_sync::SyncedTabDelegate
* modified_tab
) override
{
144 // Unwind to ensure SessionsSyncManager has processed the event.
145 base::ThreadTaskRunnerHandle::Get()->PostTask(
147 base::Bind(&TabEventHandler::QuitLoop
, weak_factory_
.GetWeakPtr()));
150 void OnFaviconsChanged(const std::set
<GURL
>& /* page_urls */,
151 const GURL
& /* icon_url */) override
{
152 // Unwind to ensure SessionsSyncManager has processed the event.
153 base::ThreadTaskRunnerHandle::Get()->PostTask(
155 base::Bind(&TabEventHandler::QuitLoop
, weak_factory_
.GetWeakPtr()));
160 base::MessageLoop::current()->Quit();
163 base::WeakPtrFactory
<TabEventHandler
> weak_factory_
;
168 bool WaitForTabsToLoad(int index
, const std::vector
<GURL
>& urls
) {
169 DVLOG(1) << "Waiting for session to propagate to associator.";
170 base::TimeTicks start_time
= base::TimeTicks::Now();
171 base::TimeTicks end_time
= start_time
+ TestTimeouts::action_max_timeout();
173 for (std::vector
<GURL
>::const_iterator it
= urls
.begin();
174 it
!= urls
.end(); ++it
) {
177 found
= ModelAssociatorHasTabWithUrl(index
, *it
);
178 if (base::TimeTicks::Now() >= end_time
) {
179 LOG(ERROR
) << "Failed to find all tabs after "
180 << TestTimeouts::action_max_timeout().InSecondsF()
185 TabEventHandler handler
;
186 browser_sync::NotificationServiceSessionsRouter
router(
187 test()->GetProfile(index
),
188 syncer::SyncableService::StartSyncFlare());
189 router
.StartRoutingTo(&handler
);
190 content::RunMessageLoop();
197 bool GetLocalWindows(int index
, SessionWindowMap
* local_windows
) {
198 // The local session provided by GetLocalSession is owned, and has lifetime
199 // controlled, by the model associator, so we must make our own copy.
200 const sync_driver::SyncedSession
* local_session
;
201 if (!GetLocalSession(index
, &local_session
)) {
204 for (SessionWindowMap::const_iterator w
= local_session
->windows
.begin();
205 w
!= local_session
->windows
.end(); ++w
) {
206 const sessions::SessionWindow
& window
= *(w
->second
);
207 sessions::SessionWindow
* new_window
= new sessions::SessionWindow();
208 new_window
->window_id
.set_id(window
.window_id
.id());
209 for (size_t t
= 0; t
< window
.tabs
.size(); ++t
) {
210 const sessions::SessionTab
& tab
= *window
.tabs
.at(t
);
211 sessions::SessionTab
* new_tab
= new sessions::SessionTab();
212 new_tab
->navigations
.resize(tab
.navigations
.size());
213 std::copy(tab
.navigations
.begin(), tab
.navigations
.end(),
214 new_tab
->navigations
.begin());
215 new_window
->tabs
.push_back(new_tab
);
217 (*local_windows
)[new_window
->window_id
.id()] = new_window
;
223 bool OpenTabAndGetLocalWindows(int index
,
225 SessionWindowMap
* local_windows
) {
226 if (!OpenTab(index
, url
)) {
229 return GetLocalWindows(index
, local_windows
);
232 bool CheckInitialState(int index
) {
233 if (0 != GetNumWindows(index
))
235 if (0 != GetNumForeignSessions(index
))
240 int GetNumWindows(int index
) {
241 const sync_driver::SyncedSession
* local_session
;
242 if (!GetLocalSession(index
, &local_session
)) {
245 return local_session
->windows
.size();
248 int GetNumForeignSessions(int index
) {
249 SyncedSessionVector sessions
;
250 if (!ProfileSyncServiceFactory::GetInstance()->GetForProfile(
251 test()->GetProfile(index
))->
252 GetOpenTabsUIDelegate()->GetAllForeignSessions(
256 return sessions
.size();
259 bool GetSessionData(int index
, SyncedSessionVector
* sessions
) {
260 if (!ProfileSyncServiceFactory::GetInstance()->GetForProfile(
261 test()->GetProfile(index
))->
262 GetOpenTabsUIDelegate()->GetAllForeignSessions(
266 SortSyncedSessions(sessions
);
270 bool CompareSyncedSessions(const sync_driver::SyncedSession
* lhs
,
271 const sync_driver::SyncedSession
* rhs
) {
274 lhs
->windows
.size() < 1 ||
275 rhs
->windows
.size() < 1) {
276 // Catchall for uncomparable data.
280 return lhs
->windows
< rhs
->windows
;
283 void SortSyncedSessions(SyncedSessionVector
* sessions
) {
284 std::sort(sessions
->begin(), sessions
->end(),
285 CompareSyncedSessions
);
288 bool NavigationEquals(const sessions::SerializedNavigationEntry
& expected
,
289 const sessions::SerializedNavigationEntry
& actual
) {
290 if (expected
.virtual_url() != actual
.virtual_url()) {
291 LOG(ERROR
) << "Expected url " << expected
.virtual_url()
292 << ", actual " << actual
.virtual_url();
295 if (expected
.referrer_url() != actual
.referrer_url()) {
296 LOG(ERROR
) << "Expected referrer "
297 << expected
.referrer_url()
299 << actual
.referrer_url();
302 if (expected
.title() != actual
.title()) {
303 LOG(ERROR
) << "Expected title " << expected
.title()
304 << ", actual " << actual
.title();
307 if (expected
.transition_type() != actual
.transition_type()) {
308 LOG(ERROR
) << "Expected transition "
309 << expected
.transition_type()
311 << actual
.transition_type();
317 bool WindowsMatch(const SessionWindowMap
& win1
,
318 const SessionWindowMap
& win2
) {
319 sessions::SessionTab
* client0_tab
;
320 sessions::SessionTab
* client1_tab
;
321 if (win1
.size() != win2
.size()) {
322 LOG(ERROR
) << "Win size doesn't match, win1 size: "
328 for (SessionWindowMap::const_iterator i
= win1
.begin();
329 i
!= win1
.end(); ++i
) {
330 SessionWindowMap::const_iterator j
= win2
.find(i
->first
);
331 if (j
== win2
.end()) {
332 LOG(ERROR
) << "Session doesn't match";
335 if (i
->second
->tabs
.size() != j
->second
->tabs
.size()) {
336 LOG(ERROR
) << "Tab size doesn't match, tab1 size: "
337 << i
->second
->tabs
.size()
339 << j
->second
->tabs
.size();
342 for (size_t t
= 0; t
< i
->second
->tabs
.size(); ++t
) {
343 client0_tab
= i
->second
->tabs
[t
];
344 client1_tab
= j
->second
->tabs
[t
];
345 for (size_t n
= 0; n
< client0_tab
->navigations
.size(); ++n
) {
346 if (!NavigationEquals(client0_tab
->navigations
[n
],
347 client1_tab
->navigations
[n
])) {
357 bool CheckForeignSessionsAgainst(
359 const std::vector
<ScopedWindowMap
>& windows
) {
360 SyncedSessionVector sessions
;
362 if (!GetSessionData(index
, &sessions
)) {
363 LOG(ERROR
) << "Cannot get session data";
367 for (size_t w_index
= 0; w_index
< windows
.size(); ++w_index
) {
368 // Skip the client's local window
369 if (static_cast<int>(w_index
) == index
)
374 for (; s_index
< sessions
.size(); ++s_index
) {
375 if (WindowsMatch(sessions
[s_index
]->windows
, *(windows
[w_index
].Get())))
379 if (s_index
== sessions
.size()) {
380 LOG(ERROR
) << "Cannot find window #" << w_index
;
390 // Helper class used in the implementation of AwaitCheckForeignSessionsAgainst.
391 class CheckForeignSessionsChecker
: public MultiClientStatusChangeChecker
{
393 CheckForeignSessionsChecker(int index
,
394 const std::vector
<ScopedWindowMap
>& windows
);
395 ~CheckForeignSessionsChecker() override
;
397 bool IsExitConditionSatisfied() override
;
398 std::string
GetDebugMessage() const override
;
402 const std::vector
<ScopedWindowMap
>& windows_
;
405 CheckForeignSessionsChecker::CheckForeignSessionsChecker(
406 int index
, const std::vector
<ScopedWindowMap
>& windows
)
407 : MultiClientStatusChangeChecker(
408 sync_datatype_helper::test()->GetSyncServices()),
412 CheckForeignSessionsChecker::~CheckForeignSessionsChecker() {}
414 bool CheckForeignSessionsChecker::IsExitConditionSatisfied() {
415 return CheckForeignSessionsAgainst(index_
, windows_
);
418 std::string
CheckForeignSessionsChecker::GetDebugMessage() const {
419 return "Waiting for matching foreign sessions";
424 bool AwaitCheckForeignSessionsAgainst(
425 int index
, const std::vector
<ScopedWindowMap
>& windows
) {
426 CheckForeignSessionsChecker
checker(index
, windows
);
428 return !checker
.TimedOut();
431 void DeleteForeignSession(int index
, std::string session_tag
) {
432 ProfileSyncServiceFactory::GetInstance()->GetForProfile(
433 test()->GetProfile(index
))->
434 GetOpenTabsUIDelegate()->DeleteForeignSession(session_tag
);
437 } // namespace sessions_helper