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/stl_util.h" 10 #include "base/test/test_timeouts.h" 11 #include "base/time/time.h" 12 #include "chrome/browser/profiles/profile.h" 13 #include "chrome/browser/sync/glue/session_model_associator.h" 14 #include "chrome/browser/sync/profile_sync_service.h" 15 #include "chrome/browser/sync/profile_sync_service_factory.h" 16 #include "chrome/browser/sync/profile_sync_service_harness.h" 17 #include "chrome/browser/sync/test/integration/sync_datatype_helper.h" 18 #include "chrome/browser/sync/test/integration/sync_test.h" 19 #include "chrome/browser/ui/singleton_tabs.h" 20 #include "chrome/test/base/ui_test_utils.h" 21 #include "url/gurl.h" 22 23 using sync_datatype_helper::test; 24 25 namespace sessions_helper { 26 27 ScopedWindowMap::ScopedWindowMap() { 28 } 29 30 ScopedWindowMap::ScopedWindowMap(SessionWindowMap* windows) { 31 Reset(windows); 32 } 33 34 ScopedWindowMap::~ScopedWindowMap() { 35 STLDeleteContainerPairSecondPointers(windows_.begin(), windows_.end()); 36 } 37 38 SessionWindowMap* ScopedWindowMap::GetMutable() { 39 return &windows_; 40 } 41 42 const SessionWindowMap* ScopedWindowMap::Get() const { 43 return &windows_; 44 } 45 46 void ScopedWindowMap::Reset(SessionWindowMap* windows) { 47 STLDeleteContainerPairSecondPointers(windows_.begin(), windows_.end()); 48 windows_.clear(); 49 std::swap(*windows, windows_); 50 } 51 52 bool GetLocalSession(int index, const browser_sync::SyncedSession** session) { 53 return ProfileSyncServiceFactory::GetInstance()->GetForProfile( 54 test()->GetProfile(index))->GetSessionModelAssociator()->GetLocalSession( 55 session); 56 } 57 58 bool ModelAssociatorHasTabWithUrl(int index, const GURL& url) { 59 content::RunAllPendingInMessageLoop(); 60 const browser_sync::SyncedSession* local_session; 61 if (!GetLocalSession(index, &local_session)) { 62 return false; 63 } 64 65 if (local_session->windows.size() == 0) { 66 DVLOG(1) << "Empty windows vector"; 67 return false; 68 } 69 70 int nav_index; 71 sessions::SerializedNavigationEntry nav; 72 for (SessionWindowMap::const_iterator it = 73 local_session->windows.begin(); 74 it != local_session->windows.end(); ++it) { 75 if (it->second->tabs.size() == 0) { 76 DVLOG(1) << "Empty tabs vector"; 77 continue; 78 } 79 for (std::vector<SessionTab*>::const_iterator tab_it = 80 it->second->tabs.begin(); 81 tab_it != it->second->tabs.end(); ++tab_it) { 82 if ((*tab_it)->navigations.size() == 0) { 83 DVLOG(1) << "Empty navigations vector"; 84 continue; 85 } 86 nav_index = (*tab_it)->current_navigation_index; 87 nav = (*tab_it)->navigations[nav_index]; 88 if (nav.virtual_url() == url) { 89 DVLOG(1) << "Found tab with url " << url.spec(); 90 DVLOG(1) << "Timestamp is " << nav.timestamp().ToInternalValue(); 91 if (nav.title().empty()) { 92 DVLOG(1) << "Title empty -- tab hasn't finished loading yet"; 93 continue; 94 } 95 return true; 96 } 97 } 98 } 99 DVLOG(1) << "Could not find tab with url " << url.spec(); 100 return false; 101 } 102 103 bool OpenTab(int index, const GURL& url) { 104 DVLOG(1) << "Opening tab: " << url.spec() << " using profile " 105 << index << "."; 106 chrome::ShowSingletonTab(test()->GetBrowser(index), url); 107 return WaitForTabsToLoad(index, std::vector<GURL>(1, url)); 108 } 109 110 bool OpenMultipleTabs(int index, const std::vector<GURL>& urls) { 111 Browser* browser = test()->GetBrowser(index); 112 for (std::vector<GURL>::const_iterator it = urls.begin(); 113 it != urls.end(); ++it) { 114 DVLOG(1) << "Opening tab: " << it->spec() << " using profile " << index 115 << "."; 116 chrome::ShowSingletonTab(browser, *it); 117 } 118 return WaitForTabsToLoad(index, urls); 119 } 120 121 bool WaitForTabsToLoad(int index, const std::vector<GURL>& urls) { 122 DVLOG(1) << "Waiting for session to propagate to associator."; 123 base::TimeTicks start_time = base::TimeTicks::Now(); 124 base::TimeTicks end_time = start_time + TestTimeouts::action_max_timeout(); 125 bool found; 126 for (std::vector<GURL>::const_iterator it = urls.begin(); 127 it != urls.end(); ++it) { 128 found = false; 129 while (!found) { 130 found = ModelAssociatorHasTabWithUrl(index, *it); 131 if (base::TimeTicks::Now() >= end_time) { 132 LOG(ERROR) << "Failed to find all tabs after " 133 << TestTimeouts::action_max_timeout().InSecondsF() 134 << " seconds."; 135 return false; 136 } 137 if (!found) { 138 ProfileSyncServiceFactory::GetInstance()->GetForProfile( 139 test()->GetProfile(index))->GetSessionModelAssociator()-> 140 BlockUntilLocalChangeForTest(TestTimeouts::action_max_timeout()); 141 content::RunMessageLoop(); 142 } 143 } 144 } 145 return true; 146 } 147 148 bool GetLocalWindows(int index, SessionWindowMap* local_windows) { 149 // The local session provided by GetLocalSession is owned, and has lifetime 150 // controlled, by the model associator, so we must make our own copy. 151 const browser_sync::SyncedSession* local_session; 152 if (!GetLocalSession(index, &local_session)) { 153 return false; 154 } 155 for (SessionWindowMap::const_iterator w = local_session->windows.begin(); 156 w != local_session->windows.end(); ++w) { 157 const SessionWindow& window = *(w->second); 158 SessionWindow* new_window = new SessionWindow(); 159 new_window->window_id.set_id(window.window_id.id()); 160 for (size_t t = 0; t < window.tabs.size(); ++t) { 161 const SessionTab& tab = *window.tabs.at(t); 162 SessionTab* new_tab = new SessionTab(); 163 new_tab->navigations.resize(tab.navigations.size()); 164 std::copy(tab.navigations.begin(), tab.navigations.end(), 165 new_tab->navigations.begin()); 166 new_window->tabs.push_back(new_tab); 167 } 168 (*local_windows)[new_window->window_id.id()] = new_window; 169 } 170 171 return true; 172 } 173 174 bool OpenTabAndGetLocalWindows(int index, 175 const GURL& url, 176 SessionWindowMap* local_windows) { 177 if (!OpenTab(index, url)) { 178 return false; 179 } 180 return GetLocalWindows(index, local_windows); 181 } 182 183 bool CheckInitialState(int index) { 184 if (0 != GetNumWindows(index)) 185 return false; 186 if (0 != GetNumForeignSessions(index)) 187 return false; 188 return true; 189 } 190 191 int GetNumWindows(int index) { 192 const browser_sync::SyncedSession* local_session; 193 if (!GetLocalSession(index, &local_session)) { 194 return 0; 195 } 196 return local_session->windows.size(); 197 } 198 199 int GetNumForeignSessions(int index) { 200 SyncedSessionVector sessions; 201 if (!ProfileSyncServiceFactory::GetInstance()->GetForProfile( 202 test()->GetProfile(index))-> 203 GetSessionModelAssociator()->GetAllForeignSessions(&sessions)) 204 return 0; 205 return sessions.size(); 206 } 207 208 bool GetSessionData(int index, SyncedSessionVector* sessions) { 209 if (!ProfileSyncServiceFactory::GetInstance()->GetForProfile( 210 test()->GetProfile(index))-> 211 GetSessionModelAssociator()->GetAllForeignSessions(sessions)) 212 return false; 213 SortSyncedSessions(sessions); 214 return true; 215 } 216 217 bool CompareSyncedSessions(const browser_sync::SyncedSession* lhs, 218 const browser_sync::SyncedSession* rhs) { 219 if (!lhs || 220 !rhs || 221 lhs->windows.size() < 1 || 222 rhs->windows.size() < 1) { 223 // Catchall for uncomparable data. 224 return false; 225 } 226 227 return lhs->windows < rhs->windows; 228 } 229 230 void SortSyncedSessions(SyncedSessionVector* sessions) { 231 std::sort(sessions->begin(), sessions->end(), 232 CompareSyncedSessions); 233 } 234 235 bool NavigationEquals(const sessions::SerializedNavigationEntry& expected, 236 const sessions::SerializedNavigationEntry& actual) { 237 if (expected.virtual_url() != actual.virtual_url()) { 238 LOG(ERROR) << "Expected url " << expected.virtual_url() 239 << ", actual " << actual.virtual_url(); 240 return false; 241 } 242 if (expected.referrer().url != actual.referrer().url) { 243 LOG(ERROR) << "Expected referrer " 244 << expected.referrer().url 245 << ", actual " 246 << actual.referrer().url; 247 return false; 248 } 249 if (expected.title() != actual.title()) { 250 LOG(ERROR) << "Expected title " << expected.title() 251 << ", actual " << actual.title(); 252 return false; 253 } 254 if (expected.transition_type() != actual.transition_type()) { 255 LOG(ERROR) << "Expected transition " 256 << expected.transition_type() 257 << ", actual " 258 << actual.transition_type(); 259 return false; 260 } 261 return true; 262 } 263 264 bool WindowsMatch(const SessionWindowMap& win1, 265 const SessionWindowMap& win2) { 266 SessionTab* client0_tab; 267 SessionTab* client1_tab; 268 if (win1.size() != win2.size()) 269 return false; 270 for (SessionWindowMap::const_iterator i = win1.begin(); 271 i != win1.end(); ++i) { 272 SessionWindowMap::const_iterator j = win2.find(i->first); 273 if (j == win2.end()) 274 return false; 275 if (i->second->tabs.size() != j->second->tabs.size()) 276 return false; 277 for (size_t t = 0; t < i->second->tabs.size(); ++t) { 278 client0_tab = i->second->tabs[t]; 279 client1_tab = j->second->tabs[t]; 280 for (size_t n = 0; n < client0_tab->navigations.size(); ++n) { 281 if (!NavigationEquals(client0_tab->navigations[n], 282 client1_tab->navigations[n])) { 283 return false; 284 } 285 } 286 } 287 } 288 289 return true; 290 } 291 292 bool CheckForeignSessionsAgainst( 293 int index, 294 const std::vector<ScopedWindowMap>& windows) { 295 SyncedSessionVector sessions; 296 if (!GetSessionData(index, &sessions)) 297 return false; 298 if ((size_t)(test()->num_clients()-1) != sessions.size()) 299 return false; 300 301 int window_index = 0; 302 for (size_t j = 0; j < sessions.size(); ++j, ++window_index) { 303 if (window_index == index) 304 window_index++; // Skip self. 305 if (!WindowsMatch(sessions[j]->windows, 306 *(windows[window_index].Get()))) 307 return false; 308 } 309 310 return true; 311 } 312 313 void DeleteForeignSession(int index, std::string session_tag) { 314 ProfileSyncServiceFactory::GetInstance()->GetForProfile( 315 test()->GetProfile(index))-> 316 GetSessionModelAssociator()->DeleteForeignSession(session_tag); 317 } 318 319 } // namespace sessions_helper 320