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/memory/weak_ptr.h"
12 #include "base/stl_util.h"
13 #include "base/test/test_timeouts.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/sync/open_tabs_ui_delegate.h"
17 #include "chrome/browser/sync/profile_sync_service.h"
18 #include "chrome/browser/sync/profile_sync_service_factory.h"
19 #include "chrome/browser/sync/sessions/notification_service_sessions_router.h"
20 #include "chrome/browser/sync/sessions/sessions_sync_manager.h"
21 #include "chrome/browser/sync/test/integration/multi_client_status_change_checker.h"
22 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
23 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
24 #include "chrome/browser/sync/test/integration/sync_test.h"
25 #include "chrome/browser/ui/singleton_tabs.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "content/public/test/test_utils.h"
30 using sync_datatype_helper::test
;
32 namespace sessions_helper
{
34 ScopedWindowMap::ScopedWindowMap() {
37 ScopedWindowMap::ScopedWindowMap(SessionWindowMap
* windows
) {
41 ScopedWindowMap::~ScopedWindowMap() {
42 STLDeleteContainerPairSecondPointers(windows_
.begin(), windows_
.end());
45 SessionWindowMap
* ScopedWindowMap::GetMutable() {
49 const SessionWindowMap
* ScopedWindowMap::Get() const {
53 void ScopedWindowMap::Reset(SessionWindowMap
* windows
) {
54 STLDeleteContainerPairSecondPointers(windows_
.begin(), windows_
.end());
56 std::swap(*windows
, windows_
);
59 bool GetLocalSession(int index
, const browser_sync::SyncedSession
** session
) {
60 return ProfileSyncServiceFactory::GetInstance()->GetForProfile(
61 test()->GetProfile(index
))->GetOpenTabsUIDelegate()->
62 GetLocalSession(session
);
65 bool ModelAssociatorHasTabWithUrl(int index
, const GURL
& url
) {
66 content::RunAllPendingInMessageLoop();
67 const browser_sync::SyncedSession
* local_session
;
68 if (!GetLocalSession(index
, &local_session
)) {
72 if (local_session
->windows
.size() == 0) {
73 DVLOG(1) << "Empty windows vector";
78 sessions::SerializedNavigationEntry nav
;
79 for (SessionWindowMap::const_iterator it
=
80 local_session
->windows
.begin();
81 it
!= local_session
->windows
.end(); ++it
) {
82 if (it
->second
->tabs
.size() == 0) {
83 DVLOG(1) << "Empty tabs vector";
86 for (std::vector
<sessions::SessionTab
*>::const_iterator tab_it
=
87 it
->second
->tabs
.begin();
88 tab_it
!= it
->second
->tabs
.end(); ++tab_it
) {
89 if ((*tab_it
)->navigations
.size() == 0) {
90 DVLOG(1) << "Empty navigations vector";
93 nav_index
= (*tab_it
)->current_navigation_index
;
94 nav
= (*tab_it
)->navigations
[nav_index
];
95 if (nav
.virtual_url() == url
) {
96 DVLOG(1) << "Found tab with url " << url
.spec();
97 DVLOG(1) << "Timestamp is " << nav
.timestamp().ToInternalValue();
98 if (nav
.title().empty()) {
99 DVLOG(1) << "Title empty -- tab hasn't finished loading yet";
106 DVLOG(1) << "Could not find tab with url " << url
.spec();
110 bool OpenTab(int index
, const GURL
& url
) {
111 DVLOG(1) << "Opening tab: " << url
.spec() << " using profile "
113 chrome::ShowSingletonTab(test()->GetBrowser(index
), url
);
114 return WaitForTabsToLoad(index
, std::vector
<GURL
>(1, url
));
117 bool OpenMultipleTabs(int index
, const std::vector
<GURL
>& urls
) {
118 Browser
* browser
= test()->GetBrowser(index
);
119 for (std::vector
<GURL
>::const_iterator it
= urls
.begin();
120 it
!= urls
.end(); ++it
) {
121 DVLOG(1) << "Opening tab: " << it
->spec() << " using profile " << index
123 chrome::ShowSingletonTab(browser
, *it
);
125 return WaitForTabsToLoad(index
, urls
);
130 class TabEventHandler
: public browser_sync::LocalSessionEventHandler
{
132 TabEventHandler() : weak_factory_(this) {
133 base::MessageLoop::current()->PostDelayedTask(
135 base::Bind(&TabEventHandler::QuitLoop
, weak_factory_
.GetWeakPtr()),
136 TestTimeouts::action_max_timeout());
139 void OnLocalTabModified(
140 browser_sync::SyncedTabDelegate
* modified_tab
) override
{
141 // Unwind to ensure SessionsSyncManager has processed the event.
142 base::MessageLoop::current()->PostTask(
144 base::Bind(&TabEventHandler::QuitLoop
, weak_factory_
.GetWeakPtr()));
147 void OnFaviconPageUrlsUpdated(
148 const std::set
<GURL
>& updated_page_urls
) override
{
149 // Unwind to ensure SessionsSyncManager has processed the event.
150 base::MessageLoop::current()->PostTask(
152 base::Bind(&TabEventHandler::QuitLoop
, weak_factory_
.GetWeakPtr()));
157 base::MessageLoop::current()->Quit();
160 base::WeakPtrFactory
<TabEventHandler
> weak_factory_
;
165 bool WaitForTabsToLoad(int index
, const std::vector
<GURL
>& urls
) {
166 DVLOG(1) << "Waiting for session to propagate to associator.";
167 base::TimeTicks start_time
= base::TimeTicks::Now();
168 base::TimeTicks end_time
= start_time
+ TestTimeouts::action_max_timeout();
170 for (std::vector
<GURL
>::const_iterator it
= urls
.begin();
171 it
!= urls
.end(); ++it
) {
174 found
= ModelAssociatorHasTabWithUrl(index
, *it
);
175 if (base::TimeTicks::Now() >= end_time
) {
176 LOG(ERROR
) << "Failed to find all tabs after "
177 << TestTimeouts::action_max_timeout().InSecondsF()
182 TabEventHandler handler
;
183 browser_sync::NotificationServiceSessionsRouter
router(
184 test()->GetProfile(index
),
185 syncer::SyncableService::StartSyncFlare());
186 router
.StartRoutingTo(&handler
);
187 content::RunMessageLoop();
194 bool GetLocalWindows(int index
, SessionWindowMap
* local_windows
) {
195 // The local session provided by GetLocalSession is owned, and has lifetime
196 // controlled, by the model associator, so we must make our own copy.
197 const browser_sync::SyncedSession
* local_session
;
198 if (!GetLocalSession(index
, &local_session
)) {
201 for (SessionWindowMap::const_iterator w
= local_session
->windows
.begin();
202 w
!= local_session
->windows
.end(); ++w
) {
203 const sessions::SessionWindow
& window
= *(w
->second
);
204 sessions::SessionWindow
* new_window
= new sessions::SessionWindow();
205 new_window
->window_id
.set_id(window
.window_id
.id());
206 for (size_t t
= 0; t
< window
.tabs
.size(); ++t
) {
207 const sessions::SessionTab
& tab
= *window
.tabs
.at(t
);
208 sessions::SessionTab
* new_tab
= new sessions::SessionTab();
209 new_tab
->navigations
.resize(tab
.navigations
.size());
210 std::copy(tab
.navigations
.begin(), tab
.navigations
.end(),
211 new_tab
->navigations
.begin());
212 new_window
->tabs
.push_back(new_tab
);
214 (*local_windows
)[new_window
->window_id
.id()] = new_window
;
220 bool OpenTabAndGetLocalWindows(int index
,
222 SessionWindowMap
* local_windows
) {
223 if (!OpenTab(index
, url
)) {
226 return GetLocalWindows(index
, local_windows
);
229 bool CheckInitialState(int index
) {
230 if (0 != GetNumWindows(index
))
232 if (0 != GetNumForeignSessions(index
))
237 int GetNumWindows(int index
) {
238 const browser_sync::SyncedSession
* local_session
;
239 if (!GetLocalSession(index
, &local_session
)) {
242 return local_session
->windows
.size();
245 int GetNumForeignSessions(int index
) {
246 SyncedSessionVector sessions
;
247 if (!ProfileSyncServiceFactory::GetInstance()->GetForProfile(
248 test()->GetProfile(index
))->
249 GetOpenTabsUIDelegate()->GetAllForeignSessions(
253 return sessions
.size();
256 bool GetSessionData(int index
, SyncedSessionVector
* sessions
) {
257 if (!ProfileSyncServiceFactory::GetInstance()->GetForProfile(
258 test()->GetProfile(index
))->
259 GetOpenTabsUIDelegate()->GetAllForeignSessions(
263 SortSyncedSessions(sessions
);
267 bool CompareSyncedSessions(const browser_sync::SyncedSession
* lhs
,
268 const browser_sync::SyncedSession
* rhs
) {
271 lhs
->windows
.size() < 1 ||
272 rhs
->windows
.size() < 1) {
273 // Catchall for uncomparable data.
277 return lhs
->windows
< rhs
->windows
;
280 void SortSyncedSessions(SyncedSessionVector
* sessions
) {
281 std::sort(sessions
->begin(), sessions
->end(),
282 CompareSyncedSessions
);
285 bool NavigationEquals(const sessions::SerializedNavigationEntry
& expected
,
286 const sessions::SerializedNavigationEntry
& actual
) {
287 if (expected
.virtual_url() != actual
.virtual_url()) {
288 LOG(ERROR
) << "Expected url " << expected
.virtual_url()
289 << ", actual " << actual
.virtual_url();
292 if (expected
.referrer_url() != actual
.referrer_url()) {
293 LOG(ERROR
) << "Expected referrer "
294 << expected
.referrer_url()
296 << actual
.referrer_url();
299 if (expected
.title() != actual
.title()) {
300 LOG(ERROR
) << "Expected title " << expected
.title()
301 << ", actual " << actual
.title();
304 if (expected
.transition_type() != actual
.transition_type()) {
305 LOG(ERROR
) << "Expected transition "
306 << expected
.transition_type()
308 << actual
.transition_type();
314 bool WindowsMatch(const SessionWindowMap
& win1
,
315 const SessionWindowMap
& win2
) {
316 sessions::SessionTab
* client0_tab
;
317 sessions::SessionTab
* client1_tab
;
318 if (win1
.size() != win2
.size())
320 for (SessionWindowMap::const_iterator i
= win1
.begin();
321 i
!= win1
.end(); ++i
) {
322 SessionWindowMap::const_iterator j
= win2
.find(i
->first
);
325 if (i
->second
->tabs
.size() != j
->second
->tabs
.size())
327 for (size_t t
= 0; t
< i
->second
->tabs
.size(); ++t
) {
328 client0_tab
= i
->second
->tabs
[t
];
329 client1_tab
= j
->second
->tabs
[t
];
330 for (size_t n
= 0; n
< client0_tab
->navigations
.size(); ++n
) {
331 if (!NavigationEquals(client0_tab
->navigations
[n
],
332 client1_tab
->navigations
[n
])) {
342 bool CheckForeignSessionsAgainst(
344 const std::vector
<ScopedWindowMap
>& windows
) {
345 SyncedSessionVector sessions
;
346 if (!GetSessionData(index
, &sessions
))
348 if ((size_t)(test()->num_clients()-1) != sessions
.size())
351 int window_index
= 0;
352 for (size_t j
= 0; j
< sessions
.size(); ++j
, ++window_index
) {
353 if (window_index
== index
)
354 window_index
++; // Skip self.
355 if (!WindowsMatch(sessions
[j
]->windows
,
356 *(windows
[window_index
].Get())))
365 // Helper class used in the implementation of AwaitCheckForeignSessionsAgainst.
366 class CheckForeignSessionsChecker
: public MultiClientStatusChangeChecker
{
368 CheckForeignSessionsChecker(int index
,
369 const std::vector
<ScopedWindowMap
>& windows
);
370 ~CheckForeignSessionsChecker() override
;
372 bool IsExitConditionSatisfied() override
;
373 std::string
GetDebugMessage() const override
;
377 const std::vector
<ScopedWindowMap
>& windows_
;
380 CheckForeignSessionsChecker::CheckForeignSessionsChecker(
381 int index
, const std::vector
<ScopedWindowMap
>& windows
)
382 : MultiClientStatusChangeChecker(
383 sync_datatype_helper::test()->GetSyncServices()),
387 CheckForeignSessionsChecker::~CheckForeignSessionsChecker() {}
389 bool CheckForeignSessionsChecker::IsExitConditionSatisfied() {
390 return CheckForeignSessionsAgainst(index_
, windows_
);
393 std::string
CheckForeignSessionsChecker::GetDebugMessage() const {
394 return "Waiting for matching foreign sessions";
399 bool AwaitCheckForeignSessionsAgainst(
400 int index
, const std::vector
<ScopedWindowMap
>& windows
) {
401 CheckForeignSessionsChecker
checker(index
, windows
);
403 return !checker
.TimedOut();
406 void DeleteForeignSession(int index
, std::string session_tag
) {
407 ProfileSyncServiceFactory::GetInstance()->GetForProfile(
408 test()->GetProfile(index
))->
409 GetOpenTabsUIDelegate()->DeleteForeignSession(session_tag
);
412 } // namespace sessions_helper