Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / sync / test / integration / sessions_helper.cc
blobd8ecce4a042f4c9a0498cb14efe360f9015b48b5
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"
7 #include <algorithm>
9 #include "base/bind.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"
31 #include "url/gurl.h"
33 using sync_datatype_helper::test;
35 namespace sessions_helper {
37 ScopedWindowMap::ScopedWindowMap() {
40 ScopedWindowMap::ScopedWindowMap(SessionWindowMap* windows) {
41 Reset(windows);
44 ScopedWindowMap::~ScopedWindowMap() {
45 STLDeleteContainerPairSecondPointers(windows_.begin(), windows_.end());
48 SessionWindowMap* ScopedWindowMap::GetMutable() {
49 return &windows_;
52 const SessionWindowMap* ScopedWindowMap::Get() const {
53 return &windows_;
56 void ScopedWindowMap::Reset(SessionWindowMap* windows) {
57 STLDeleteContainerPairSecondPointers(windows_.begin(), windows_.end());
58 windows_.clear();
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)) {
72 return false;
75 if (local_session->windows.size() == 0) {
76 DVLOG(1) << "Empty windows vector";
77 return false;
80 int nav_index;
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";
87 continue;
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";
94 continue;
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";
103 continue;
105 return true;
109 DVLOG(1) << "Could not find tab with url " << url.spec();
110 return false;
113 bool OpenTab(int index, const GURL& url) {
114 DVLOG(1) << "Opening tab: " << url.spec() << " using profile "
115 << index << ".";
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
125 << ".";
126 chrome::ShowSingletonTab(browser, *it);
128 return WaitForTabsToLoad(index, urls);
131 namespace {
133 class TabEventHandler : public browser_sync::LocalSessionEventHandler {
134 public:
135 TabEventHandler() : weak_factory_(this) {
136 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
137 FROM_HERE,
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(
146 FROM_HERE,
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(
154 FROM_HERE,
155 base::Bind(&TabEventHandler::QuitLoop, weak_factory_.GetWeakPtr()));
158 private:
159 void QuitLoop() {
160 base::MessageLoop::current()->Quit();
163 base::WeakPtrFactory<TabEventHandler> weak_factory_;
166 } // namespace
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();
172 bool found;
173 for (std::vector<GURL>::const_iterator it = urls.begin();
174 it != urls.end(); ++it) {
175 found = false;
176 while (!found) {
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()
181 << " seconds.";
182 return false;
184 if (!found) {
185 TabEventHandler handler;
186 browser_sync::NotificationServiceSessionsRouter router(
187 test()->GetProfile(index),
188 syncer::SyncableService::StartSyncFlare());
189 router.StartRoutingTo(&handler);
190 content::RunMessageLoop();
194 return true;
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)) {
202 return false;
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;
220 return true;
223 bool OpenTabAndGetLocalWindows(int index,
224 const GURL& url,
225 SessionWindowMap* local_windows) {
226 if (!OpenTab(index, url)) {
227 return false;
229 return GetLocalWindows(index, local_windows);
232 bool CheckInitialState(int index) {
233 if (0 != GetNumWindows(index))
234 return false;
235 if (0 != GetNumForeignSessions(index))
236 return false;
237 return true;
240 int GetNumWindows(int index) {
241 const sync_driver::SyncedSession* local_session;
242 if (!GetLocalSession(index, &local_session)) {
243 return 0;
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(
253 &sessions)) {
254 return 0;
256 return sessions.size();
259 bool GetSessionData(int index, SyncedSessionVector* sessions) {
260 if (!ProfileSyncServiceFactory::GetInstance()->GetForProfile(
261 test()->GetProfile(index))->
262 GetOpenTabsUIDelegate()->GetAllForeignSessions(
263 sessions)) {
264 return false;
266 SortSyncedSessions(sessions);
267 return true;
270 bool CompareSyncedSessions(const sync_driver::SyncedSession* lhs,
271 const sync_driver::SyncedSession* rhs) {
272 if (!lhs ||
273 !rhs ||
274 lhs->windows.size() < 1 ||
275 rhs->windows.size() < 1) {
276 // Catchall for uncomparable data.
277 return false;
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();
293 return false;
295 if (expected.referrer_url() != actual.referrer_url()) {
296 LOG(ERROR) << "Expected referrer "
297 << expected.referrer_url()
298 << ", actual "
299 << actual.referrer_url();
300 return false;
302 if (expected.title() != actual.title()) {
303 LOG(ERROR) << "Expected title " << expected.title()
304 << ", actual " << actual.title();
305 return false;
307 if (expected.transition_type() != actual.transition_type()) {
308 LOG(ERROR) << "Expected transition "
309 << expected.transition_type()
310 << ", actual "
311 << actual.transition_type();
312 return false;
314 return true;
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: "
323 << win1.size()
324 << ", win2 size: "
325 << win2.size();
326 return false;
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";
333 return false;
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()
338 << ", tab2 size: "
339 << j->second->tabs.size();
340 return false;
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])) {
348 return false;
354 return true;
357 bool CheckForeignSessionsAgainst(
358 int index,
359 const std::vector<ScopedWindowMap>& windows) {
360 SyncedSessionVector sessions;
362 if (!GetSessionData(index, &sessions)) {
363 LOG(ERROR) << "Cannot get session data";
364 return false;
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)
370 continue;
372 size_t s_index = 0;
374 for (; s_index < sessions.size(); ++s_index) {
375 if (WindowsMatch(sessions[s_index]->windows, *(windows[w_index].Get())))
376 break;
379 if (s_index == sessions.size()) {
380 LOG(ERROR) << "Cannot find window #" << w_index;
381 return false;
385 return true;
388 namespace {
390 // Helper class used in the implementation of AwaitCheckForeignSessionsAgainst.
391 class CheckForeignSessionsChecker : public MultiClientStatusChangeChecker {
392 public:
393 CheckForeignSessionsChecker(int index,
394 const std::vector<ScopedWindowMap>& windows);
395 ~CheckForeignSessionsChecker() override;
397 bool IsExitConditionSatisfied() override;
398 std::string GetDebugMessage() const override;
400 private:
401 int index_;
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()),
409 index_(index),
410 windows_(windows) {}
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";
422 } // namespace
424 bool AwaitCheckForeignSessionsAgainst(
425 int index, const std::vector<ScopedWindowMap>& windows) {
426 CheckForeignSessionsChecker checker(index, windows);
427 checker.Wait();
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