sessions_helper.cc revision a02191e04bc25c4935f804f2c080ae28663d096d
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. 4 5#include "chrome/browser/sync/test/integration/sessions_helper.h" 6 7#include <algorithm> 8 9#include "base/bind.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/glue/session_model_associator.h" 17#include "chrome/browser/sync/open_tabs_ui_delegate.h" 18#include "chrome/browser/sync/profile_sync_service.h" 19#include "chrome/browser/sync/profile_sync_service_factory.h" 20#include "chrome/browser/sync/sessions2/notification_service_sessions_router.h" 21#include "chrome/browser/sync/sessions2/sessions_sync_manager.h" 22#include "chrome/browser/sync/test/integration/multi_client_status_change_checker.h" 23#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h" 24#include "chrome/browser/sync/test/integration/sync_datatype_helper.h" 25#include "chrome/browser/sync/test/integration/sync_test.h" 26#include "chrome/browser/ui/singleton_tabs.h" 27#include "chrome/common/chrome_switches.h" 28#include "chrome/test/base/ui_test_utils.h" 29#include "url/gurl.h" 30 31using sync_datatype_helper::test; 32 33namespace sessions_helper { 34 35ScopedWindowMap::ScopedWindowMap() { 36} 37 38ScopedWindowMap::ScopedWindowMap(SessionWindowMap* windows) { 39 Reset(windows); 40} 41 42ScopedWindowMap::~ScopedWindowMap() { 43 STLDeleteContainerPairSecondPointers(windows_.begin(), windows_.end()); 44} 45 46SessionWindowMap* ScopedWindowMap::GetMutable() { 47 return &windows_; 48} 49 50const SessionWindowMap* ScopedWindowMap::Get() const { 51 return &windows_; 52} 53 54void ScopedWindowMap::Reset(SessionWindowMap* windows) { 55 STLDeleteContainerPairSecondPointers(windows_.begin(), windows_.end()); 56 windows_.clear(); 57 std::swap(*windows, windows_); 58} 59 60bool GetLocalSession(int index, const browser_sync::SyncedSession** session) { 61 return ProfileSyncServiceFactory::GetInstance()->GetForProfile( 62 test()->GetProfile(index))->GetOpenTabsUIDelegate()-> 63 GetLocalSession(session); 64} 65 66bool ModelAssociatorHasTabWithUrl(int index, const GURL& url) { 67 content::RunAllPendingInMessageLoop(); 68 const browser_sync::SyncedSession* local_session; 69 if (!GetLocalSession(index, &local_session)) { 70 return false; 71 } 72 73 if (local_session->windows.size() == 0) { 74 DVLOG(1) << "Empty windows vector"; 75 return false; 76 } 77 78 int nav_index; 79 sessions::SerializedNavigationEntry nav; 80 for (SessionWindowMap::const_iterator it = 81 local_session->windows.begin(); 82 it != local_session->windows.end(); ++it) { 83 if (it->second->tabs.size() == 0) { 84 DVLOG(1) << "Empty tabs vector"; 85 continue; 86 } 87 for (std::vector<SessionTab*>::const_iterator tab_it = 88 it->second->tabs.begin(); 89 tab_it != it->second->tabs.end(); ++tab_it) { 90 if ((*tab_it)->navigations.size() == 0) { 91 DVLOG(1) << "Empty navigations vector"; 92 continue; 93 } 94 nav_index = (*tab_it)->current_navigation_index; 95 nav = (*tab_it)->navigations[nav_index]; 96 if (nav.virtual_url() == url) { 97 DVLOG(1) << "Found tab with url " << url.spec(); 98 DVLOG(1) << "Timestamp is " << nav.timestamp().ToInternalValue(); 99 if (nav.title().empty()) { 100 DVLOG(1) << "Title empty -- tab hasn't finished loading yet"; 101 continue; 102 } 103 return true; 104 } 105 } 106 } 107 DVLOG(1) << "Could not find tab with url " << url.spec(); 108 return false; 109} 110 111bool OpenTab(int index, const GURL& url) { 112 DVLOG(1) << "Opening tab: " << url.spec() << " using profile " 113 << index << "."; 114 chrome::ShowSingletonTab(test()->GetBrowser(index), url); 115 return WaitForTabsToLoad(index, std::vector<GURL>(1, url)); 116} 117 118bool OpenMultipleTabs(int index, const std::vector<GURL>& urls) { 119 Browser* browser = test()->GetBrowser(index); 120 for (std::vector<GURL>::const_iterator it = urls.begin(); 121 it != urls.end(); ++it) { 122 DVLOG(1) << "Opening tab: " << it->spec() << " using profile " << index 123 << "."; 124 chrome::ShowSingletonTab(browser, *it); 125 } 126 return WaitForTabsToLoad(index, urls); 127} 128 129namespace { 130 131class TabEventHandler : public browser_sync::LocalSessionEventHandler { 132 public: 133 TabEventHandler() : weak_factory_(this) { 134 base::MessageLoop::current()->PostDelayedTask( 135 FROM_HERE, 136 base::Bind(&TabEventHandler::QuitLoop, weak_factory_.GetWeakPtr()), 137 TestTimeouts::action_max_timeout()); 138 } 139 140 virtual void OnLocalTabModified( 141 browser_sync::SyncedTabDelegate* modified_tab) OVERRIDE { 142 // Unwind to ensure SessionsSyncManager has processed the event. 143 base::MessageLoop::current()->PostTask( 144 FROM_HERE, 145 base::Bind(&TabEventHandler::QuitLoop, weak_factory_.GetWeakPtr())); 146 } 147 148 virtual void OnFaviconPageUrlsUpdated( 149 const std::set<GURL>& updated_page_urls) OVERRIDE { 150 // Unwind to ensure SessionsSyncManager has processed the event. 151 base::MessageLoop::current()->PostTask( 152 FROM_HERE, 153 base::Bind(&TabEventHandler::QuitLoop, weak_factory_.GetWeakPtr())); 154 } 155 156 private: 157 void QuitLoop() { 158 base::MessageLoop::current()->Quit(); 159 } 160 161 base::WeakPtrFactory<TabEventHandler> weak_factory_; 162}; 163 164} // namespace 165 166bool WaitForTabsToLoad(int index, const std::vector<GURL>& urls) { 167 DVLOG(1) << "Waiting for session to propagate to associator."; 168 base::TimeTicks start_time = base::TimeTicks::Now(); 169 base::TimeTicks end_time = start_time + TestTimeouts::action_max_timeout(); 170 bool found; 171 for (std::vector<GURL>::const_iterator it = urls.begin(); 172 it != urls.end(); ++it) { 173 found = false; 174 while (!found) { 175 found = ModelAssociatorHasTabWithUrl(index, *it); 176 if (base::TimeTicks::Now() >= end_time) { 177 LOG(ERROR) << "Failed to find all tabs after " 178 << TestTimeouts::action_max_timeout().InSecondsF() 179 << " seconds."; 180 return false; 181 } 182 if (!found) { 183 if (CommandLine::ForCurrentProcess()->HasSwitch( 184 switches::kDisableSyncSessionsV2)) { 185 ProfileSyncServiceFactory::GetForProfile(test()->GetProfile(index))-> 186 GetSessionModelAssociatorDeprecated()-> 187 BlockUntilLocalChangeForTest(TestTimeouts::action_max_timeout()); 188 content::RunMessageLoop(); 189 } else { 190 TabEventHandler handler; 191 browser_sync::NotificationServiceSessionsRouter router( 192 test()->GetProfile(index), 193 syncer::SyncableService::StartSyncFlare()); 194 router.StartRoutingTo(&handler); 195 content::RunMessageLoop(); 196 } 197 } 198 } 199 } 200 return true; 201} 202 203bool GetLocalWindows(int index, SessionWindowMap* local_windows) { 204 // The local session provided by GetLocalSession is owned, and has lifetime 205 // controlled, by the model associator, so we must make our own copy. 206 const browser_sync::SyncedSession* local_session; 207 if (!GetLocalSession(index, &local_session)) { 208 return false; 209 } 210 for (SessionWindowMap::const_iterator w = local_session->windows.begin(); 211 w != local_session->windows.end(); ++w) { 212 const SessionWindow& window = *(w->second); 213 SessionWindow* new_window = new SessionWindow(); 214 new_window->window_id.set_id(window.window_id.id()); 215 for (size_t t = 0; t < window.tabs.size(); ++t) { 216 const SessionTab& tab = *window.tabs.at(t); 217 SessionTab* new_tab = new SessionTab(); 218 new_tab->navigations.resize(tab.navigations.size()); 219 std::copy(tab.navigations.begin(), tab.navigations.end(), 220 new_tab->navigations.begin()); 221 new_window->tabs.push_back(new_tab); 222 } 223 (*local_windows)[new_window->window_id.id()] = new_window; 224 } 225 226 return true; 227} 228 229bool OpenTabAndGetLocalWindows(int index, 230 const GURL& url, 231 SessionWindowMap* local_windows) { 232 if (!OpenTab(index, url)) { 233 return false; 234 } 235 return GetLocalWindows(index, local_windows); 236} 237 238bool CheckInitialState(int index) { 239 if (0 != GetNumWindows(index)) 240 return false; 241 if (0 != GetNumForeignSessions(index)) 242 return false; 243 return true; 244} 245 246int GetNumWindows(int index) { 247 const browser_sync::SyncedSession* local_session; 248 if (!GetLocalSession(index, &local_session)) { 249 return 0; 250 } 251 return local_session->windows.size(); 252} 253 254int GetNumForeignSessions(int index) { 255 SyncedSessionVector sessions; 256 if (!ProfileSyncServiceFactory::GetInstance()->GetForProfile( 257 test()->GetProfile(index))-> 258 GetOpenTabsUIDelegate()->GetAllForeignSessions( 259 &sessions)) { 260 return 0; 261 } 262 return sessions.size(); 263} 264 265bool GetSessionData(int index, SyncedSessionVector* sessions) { 266 if (!ProfileSyncServiceFactory::GetInstance()->GetForProfile( 267 test()->GetProfile(index))-> 268 GetOpenTabsUIDelegate()->GetAllForeignSessions( 269 sessions)) { 270 return false; 271 } 272 SortSyncedSessions(sessions); 273 return true; 274} 275 276bool CompareSyncedSessions(const browser_sync::SyncedSession* lhs, 277 const browser_sync::SyncedSession* rhs) { 278 if (!lhs || 279 !rhs || 280 lhs->windows.size() < 1 || 281 rhs->windows.size() < 1) { 282 // Catchall for uncomparable data. 283 return false; 284 } 285 286 return lhs->windows < rhs->windows; 287} 288 289void SortSyncedSessions(SyncedSessionVector* sessions) { 290 std::sort(sessions->begin(), sessions->end(), 291 CompareSyncedSessions); 292} 293 294bool NavigationEquals(const sessions::SerializedNavigationEntry& expected, 295 const sessions::SerializedNavigationEntry& actual) { 296 if (expected.virtual_url() != actual.virtual_url()) { 297 LOG(ERROR) << "Expected url " << expected.virtual_url() 298 << ", actual " << actual.virtual_url(); 299 return false; 300 } 301 if (expected.referrer().url != actual.referrer().url) { 302 LOG(ERROR) << "Expected referrer " 303 << expected.referrer().url 304 << ", actual " 305 << actual.referrer().url; 306 return false; 307 } 308 if (expected.title() != actual.title()) { 309 LOG(ERROR) << "Expected title " << expected.title() 310 << ", actual " << actual.title(); 311 return false; 312 } 313 if (expected.transition_type() != actual.transition_type()) { 314 LOG(ERROR) << "Expected transition " 315 << expected.transition_type() 316 << ", actual " 317 << actual.transition_type(); 318 return false; 319 } 320 return true; 321} 322 323bool WindowsMatch(const SessionWindowMap& win1, 324 const SessionWindowMap& win2) { 325 SessionTab* client0_tab; 326 SessionTab* client1_tab; 327 if (win1.size() != win2.size()) 328 return false; 329 for (SessionWindowMap::const_iterator i = win1.begin(); 330 i != win1.end(); ++i) { 331 SessionWindowMap::const_iterator j = win2.find(i->first); 332 if (j == win2.end()) 333 return false; 334 if (i->second->tabs.size() != j->second->tabs.size()) 335 return false; 336 for (size_t t = 0; t < i->second->tabs.size(); ++t) { 337 client0_tab = i->second->tabs[t]; 338 client1_tab = j->second->tabs[t]; 339 for (size_t n = 0; n < client0_tab->navigations.size(); ++n) { 340 if (!NavigationEquals(client0_tab->navigations[n], 341 client1_tab->navigations[n])) { 342 return false; 343 } 344 } 345 } 346 } 347 348 return true; 349} 350 351bool CheckForeignSessionsAgainst( 352 int index, 353 const std::vector<ScopedWindowMap>& windows) { 354 SyncedSessionVector sessions; 355 if (!GetSessionData(index, &sessions)) 356 return false; 357 if ((size_t)(test()->num_clients()-1) != sessions.size()) 358 return false; 359 360 int window_index = 0; 361 for (size_t j = 0; j < sessions.size(); ++j, ++window_index) { 362 if (window_index == index) 363 window_index++; // Skip self. 364 if (!WindowsMatch(sessions[j]->windows, 365 *(windows[window_index].Get()))) 366 return false; 367 } 368 369 return true; 370} 371 372namespace { 373 374// Helper class used in the implementation of AwaitCheckForeignSessionsAgainst. 375class CheckForeignSessionsChecker : public MultiClientStatusChangeChecker { 376 public: 377 CheckForeignSessionsChecker(int index, 378 const std::vector<ScopedWindowMap>& windows); 379 virtual ~CheckForeignSessionsChecker(); 380 381 virtual bool IsExitConditionSatisfied() OVERRIDE; 382 virtual std::string GetDebugMessage() const OVERRIDE; 383 private: 384 int index_; 385 const std::vector<ScopedWindowMap>& windows_; 386}; 387 388CheckForeignSessionsChecker::CheckForeignSessionsChecker( 389 int index, const std::vector<ScopedWindowMap>& windows) 390 : MultiClientStatusChangeChecker( 391 sync_datatype_helper::test()->GetSyncServices()), 392 index_(index), 393 windows_(windows) {} 394 395CheckForeignSessionsChecker::~CheckForeignSessionsChecker() {} 396 397bool CheckForeignSessionsChecker::IsExitConditionSatisfied() { 398 return CheckForeignSessionsAgainst(index_, windows_); 399} 400 401std::string CheckForeignSessionsChecker::GetDebugMessage() const { 402 return "Waiting for matching foreign sessions"; 403} 404 405} // namespace 406 407bool AwaitCheckForeignSessionsAgainst( 408 int index, const std::vector<ScopedWindowMap>& windows) { 409 CheckForeignSessionsChecker checker(index, windows); 410 checker.Wait(); 411 return !checker.TimedOut(); 412} 413 414void DeleteForeignSession(int index, std::string session_tag) { 415 ProfileSyncServiceFactory::GetInstance()->GetForProfile( 416 test()->GetProfile(index))-> 417 GetOpenTabsUIDelegate()->DeleteForeignSession(session_tag); 418} 419 420} // namespace sessions_helper 421