OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 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 #ifndef CHROME_TEST_LIVE_SYNC_LIVE_SESSIONS_SYNC_TEST_H_ |
| 6 #define CHROME_TEST_LIVE_SYNC_LIVE_SESSIONS_SYNC_TEST_H_ |
| 7 #pragma once |
| 8 |
| 9 #include <algorithm> |
| 10 #include <vector> |
| 11 |
| 12 #include "base/compiler_specific.h" |
| 13 #include "base/scoped_vector.h" |
| 14 #include "base/ref_counted.h" |
| 15 #include "base/waitable_event.h" |
| 16 #include "chrome/browser/browser_process.h" |
| 17 #include "chrome/browser/browser_window.h" |
| 18 #include "chrome/browser/profile.h" |
| 19 #include "chrome/browser/renderer_host/render_view_host_delegate.h" |
| 20 #include "chrome/browser/sessions/base_session_service.h" |
| 21 #include "chrome/browser/sessions/session_service_test_helper.h" |
| 22 #include "chrome/browser/tab_contents/tab_contents.h" |
| 23 #include "chrome/test/live_sync/live_sync_test.h" |
| 24 #include "chrome/test/ui_test_utils.h" |
| 25 #include "googleurl/src/gurl.h" |
| 26 #include "webkit/glue/window_open_disposition.h" |
| 27 |
| 28 // Helper for accessing session service internals. |
| 29 class TestSessionService |
| 30 : public SessionServiceTestHelper, |
| 31 public base::RefCountedThreadSafe<TestSessionService> { |
| 32 public: |
| 33 TestSessionService() |
| 34 : SessionServiceTestHelper(), |
| 35 done_saving_(false, false), |
| 36 got_windows_(false, false), |
| 37 profile_(NULL), |
| 38 window_bounds_(0, 1, 2, 3) {} |
| 39 TestSessionService(SessionService * service, |
| 40 Profile* profile) |
| 41 : SessionServiceTestHelper(service), |
| 42 done_saving_(false, false), |
| 43 got_windows_(false, false), |
| 44 profile_(profile), |
| 45 window_bounds_(0, 1, 2, 3) {} |
| 46 |
| 47 void SetUp() { |
| 48 ASSERT_TRUE(service()) << "SetUp() called without setting SessionService"; |
| 49 ASSERT_TRUE(profile_); |
| 50 service()->SetWindowType(window_id_, Browser::TYPE_NORMAL); |
| 51 service()->SetWindowBounds(window_id_, window_bounds_, false); |
| 52 } |
| 53 |
| 54 // Trigger saving the current session commands to file. |
| 55 void Save() { |
| 56 service()->Save(); |
| 57 } |
| 58 |
| 59 // Synchronously reads the contents of the current session. |
| 60 std::vector<SessionWindow*>* ReadWindows() { |
| 61 // The session backend will post the callback as a task to whatever thread |
| 62 // called it. In our case, we don't want the main test thread to have tasks |
| 63 // posted to, so we perform the actual call to session service from the same |
| 64 // thread the work will be done on (backend_thread aka file thread). As a |
| 65 // result, it will directly call back, instead of posting a task, and we can |
| 66 // block on that callback. |
| 67 BrowserThread::PostTask( |
| 68 BrowserThread::FILE, |
| 69 FROM_HERE, |
| 70 NewRunnableMethod(this, &TestSessionService::DoReadWindows)); |
| 71 |
| 72 // Wait for callback to happen. |
| 73 got_windows_.Wait(); |
| 74 |
| 75 // By the time we reach here we've received the windows, so return them. |
| 76 return windows_; |
| 77 } |
| 78 |
| 79 // Makes the actual call to session service. |
| 80 // Lives on the file thread. |
| 81 void DoReadWindows() { |
| 82 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { |
| 83 LOG(ERROR) << "DoReadWindows called from wrong thread!"; |
| 84 windows_ = NULL; |
| 85 got_windows_.Signal(); |
| 86 return; |
| 87 } |
| 88 SessionService::SessionCallback* callback = |
| 89 NewCallback(this, &TestSessionService::OnGotSession); |
| 90 service()->GetCurrentSession(&consumer_, callback); |
| 91 } |
| 92 |
| 93 // Internal method used in the callback to obtain the current session. |
| 94 // Lives on and called from backend thread (file_thread). |
| 95 // We don't own windows so need to make a deep copy. |
| 96 void OnGotSession(int handle, std::vector<SessionWindow*>* windows) { |
| 97 // Hacky. We need to make a deep copy of the session windows. One way to do |
| 98 // this is to use the session model associators functionality to create |
| 99 // foreign sessions, which themselves wrap a SessionWindow vector. We just |
| 100 // need to make sure to destroy all the foreign sessions we created when |
| 101 // we're done. That's what the foreign_sessions_ ScopedVector is for. |
| 102 sync_pb::SessionSpecifics session; |
| 103 profile_->GetProfileSyncService()-> |
| 104 GetSessionModelAssociator()-> |
| 105 FillSpecificsFromSessions(windows, &session); |
| 106 |
| 107 std::vector<ForeignSession*> foreign_sessions; |
| 108 profile_->GetProfileSyncService()-> |
| 109 GetSessionModelAssociator()-> |
| 110 AppendForeignSessionFromSpecifics(&session, &foreign_sessions); |
| 111 ASSERT_EQ(foreign_sessions.size(), 1U); |
| 112 foreign_sessions_.push_back(foreign_sessions[0]); |
| 113 windows_ = &foreign_sessions[0]->windows; |
| 114 got_windows_.Signal(); |
| 115 } |
| 116 |
| 117 private: |
| 118 ~TestSessionService() { |
| 119 ReleaseService(); // We don't own this, so don't destroy it. |
| 120 } |
| 121 |
| 122 friend class base::RefCountedThreadSafe<TestSessionService>; |
| 123 |
| 124 // Current session buffer. |
| 125 std::vector<SessionWindow*>* windows_; |
| 126 |
| 127 // List of all foreign sessions we created in ReadWindows. We delete them all |
| 128 // at destruction time so that complex tests can keep comparing against old |
| 129 // SessionWindow data. Note that since we're constantly creating new foreign |
| 130 // sessions, we don't have to worry about duplicates. |
| 131 ScopedVector<ForeignSession> foreign_sessions_; |
| 132 |
| 133 // Barrier for saving session. |
| 134 base::WaitableEvent done_saving_; |
| 135 |
| 136 // Barrier for getting current windows in ReadWindows. |
| 137 base::WaitableEvent got_windows_; |
| 138 |
| 139 // Consumer used to obtain the current session. |
| 140 CancelableRequestConsumer consumer_; |
| 141 |
| 142 // Sync profile associated with this session service. |
| 143 Profile* profile_; |
| 144 |
| 145 SessionID window_id_; |
| 146 const gfx::Rect window_bounds_; |
| 147 }; |
| 148 |
| 149 class LiveSessionsSyncTest : public LiveSyncTest { |
| 150 public: |
| 151 explicit LiveSessionsSyncTest(TestType test_type) |
| 152 : LiveSyncTest(test_type), |
| 153 done_closing_(false, false) {} |
| 154 virtual ~LiveSessionsSyncTest() {} |
| 155 |
| 156 // Used to access the session service associated with a specific sync profile. |
| 157 SessionService* GetSessionService(int index) { |
| 158 return GetProfile(index)->GetSessionService(); |
| 159 } |
| 160 |
| 161 // Used to access the session service test helper associated with a specific |
| 162 // sync profile. |
| 163 TestSessionService* GetHelper(int index) { |
| 164 return test_session_services_[index]->get(); |
| 165 } |
| 166 |
| 167 // Used to access the browser associated with a specific sync profile. |
| 168 Browser* GetBrowser(int index) { |
| 169 return browsers_[index]; |
| 170 } |
| 171 |
| 172 // Sets up the TestSessionService helper and the new browser windows. |
| 173 bool SetupClients() { |
| 174 if (!LiveSyncTest::SetupClients()) { |
| 175 return false; |
| 176 } |
| 177 |
| 178 // Go through and make the TestSessionServices and Browsers. |
| 179 for (int i = 0; i < num_clients(); ++i) { |
| 180 scoped_refptr<TestSessionService>* new_tester = |
| 181 new scoped_refptr<TestSessionService>; |
| 182 *new_tester = new TestSessionService( |
| 183 GetSessionService(i), GetProfile(i)); |
| 184 test_session_services_.push_back(new_tester); |
| 185 GetHelper(i)->SetUp(); |
| 186 |
| 187 browsers_.push_back(Browser::Create(GetProfile(i))); |
| 188 } |
| 189 |
| 190 return true; |
| 191 } |
| 192 |
| 193 // Open a single tab and return the TabContents. TabContents must be checked |
| 194 // to ensure the tab opened successsfully. |
| 195 TabContents* OpenTab(int index, GURL url) WARN_UNUSED_RESULT { |
| 196 TabContents* tab = GetBrowser(index)-> |
| 197 AddSelectedTabWithURL(url, PageTransition::START_PAGE); |
| 198 |
| 199 // Wait for the page to finish loading. |
| 200 ui_test_utils::WaitForNavigation( |
| 201 &GetBrowser(index)->GetSelectedTabContents()->controller()); |
| 202 |
| 203 return tab; |
| 204 } |
| 205 |
| 206 // Creates and verifies the creation of a new window with one tab displaying |
| 207 // the specified GURL. |
| 208 // Returns: the SessionWindow associated with the new window. |
| 209 std::vector<SessionWindow*>* InitializeNewWindowWithTab(int index, GURL url) |
| 210 WARN_UNUSED_RESULT { |
| 211 if (!OpenTab(index, url)) { |
| 212 return NULL; |
| 213 } |
| 214 GetHelper(index)->Save(); |
| 215 std::vector<SessionWindow*>* windows = GetHelper(index)->ReadWindows(); |
| 216 if (windows->size() != 1) { |
| 217 LOG(ERROR) << "InitializeNewWindowWithTab called with open windows!"; |
| 218 return NULL; |
| 219 } |
| 220 if (1U != (*windows)[0]->tabs.size()) |
| 221 return NULL; |
| 222 SortSessionWindows(windows); |
| 223 return windows; |
| 224 } |
| 225 |
| 226 // Checks that window count and foreign session count are 0. |
| 227 bool CheckInitialState(int index) WARN_UNUSED_RESULT { |
| 228 if (0 != GetNumWindows(index)) |
| 229 return false; |
| 230 if (0 != GetNumForeignSessions(index)) |
| 231 return false; |
| 232 return true; |
| 233 } |
| 234 |
| 235 // Returns number of open windows for a profile. |
| 236 int GetNumWindows(int index) { |
| 237 // We don't own windows. |
| 238 std::vector<SessionWindow*>* windows = GetHelper(index)->ReadWindows(); |
| 239 return windows->size(); |
| 240 } |
| 241 |
| 242 // Returns number of foreign sessions for a profile. |
| 243 int GetNumForeignSessions(int index) { |
| 244 ScopedVector<ForeignSession> sessions; |
| 245 if (!GetProfile(index)->GetProfileSyncService()-> |
| 246 GetSessionModelAssociator()->GetSessionData(&sessions.get())) |
| 247 return 0; |
| 248 return sessions.size(); |
| 249 } |
| 250 |
| 251 // Fills the sessions vector with the model associator's foreign session data. |
| 252 // Caller owns sessions. |
| 253 bool GetSessionData(int index, std::vector<ForeignSession*>* sessions) |
| 254 WARN_UNUSED_RESULT { |
| 255 if (!GetProfile(index)->GetProfileSyncService()-> |
| 256 GetSessionModelAssociator()->GetSessionData(sessions)) |
| 257 return false; |
| 258 SortForeignSessions(sessions); |
| 259 return true; |
| 260 } |
| 261 |
| 262 // Compare session windows based on their first tab's url. |
| 263 // Returns true if the virtual url of the lhs is < the rhs. |
| 264 static bool CompareSessionWindows(SessionWindow* lhs, SessionWindow* rhs) { |
| 265 if (!lhs || |
| 266 !rhs || |
| 267 lhs->tabs.size() < 1 || |
| 268 rhs->tabs.size() < 1 || |
| 269 lhs->tabs[0]->navigations.size() < 1 || |
| 270 rhs->tabs[0]->navigations.size() < 1) { |
| 271 // Catchall for uncomparable data. |
| 272 return false; |
| 273 } |
| 274 |
| 275 return lhs->tabs[0]->navigations[0].virtual_url() < |
| 276 rhs->tabs[0]->navigations[0].virtual_url(); |
| 277 } |
| 278 |
| 279 // Sort session windows using our custom comparator (first tab url |
| 280 // comparison). |
| 281 void SortSessionWindows(std::vector<SessionWindow*>* windows) { |
| 282 std::sort(windows->begin(), windows->end(), |
| 283 LiveSessionsSyncTest::CompareSessionWindows); |
| 284 } |
| 285 |
| 286 // Compares a foreign session based on the first session window. |
| 287 // Returns true based on the comparison of the session windows. |
| 288 static bool CompareForeignSessions(ForeignSession* lhs, ForeignSession* rhs) { |
| 289 if (!lhs || |
| 290 !rhs || |
| 291 lhs->windows.size() < 1 || |
| 292 rhs->windows.size() < 1) { |
| 293 // Catchall for uncomparable data. |
| 294 return false; |
| 295 } |
| 296 |
| 297 return CompareSessionWindows(lhs->windows[0], rhs->windows[0]); |
| 298 } |
| 299 |
| 300 // Sort a foreign session vector using our custom foreign session comparator. |
| 301 void SortForeignSessions(std::vector<ForeignSession*>* sessions) { |
| 302 std::sort(sessions->begin(), sessions->end(), |
| 303 LiveSessionsSyncTest::CompareForeignSessions); |
| 304 } |
| 305 |
| 306 // Verifies that two SessionWindows match. |
| 307 // Returns: |
| 308 // - true if all the following match: |
| 309 // 1. number of SessionWindows per vector, |
| 310 // 2. number of tabs per SessionWindow, |
| 311 // 3. number of tab navigations per nab, |
| 312 // 4. actual tab navigations |
| 313 // - false otherwise. |
| 314 bool WindowsMatch(const std::vector<SessionWindow*> &win1, |
| 315 const std::vector<SessionWindow*> &win2) WARN_UNUSED_RESULT { |
| 316 SessionTab* client0_tab; |
| 317 SessionTab* client1_tab; |
| 318 if (win1.size() != win2.size()) |
| 319 return false; |
| 320 for (size_t i = 0; i < win1.size(); ++i) { |
| 321 if (win1[i]->tabs.size() != win2[i]->tabs.size()) |
| 322 return false; |
| 323 for (size_t j = 0; j < win1[i]->tabs.size(); ++j) { |
| 324 client0_tab = win1[i]->tabs[j]; |
| 325 client1_tab = win2[i]->tabs[j]; |
| 326 for (size_t k = 0; k < client0_tab->navigations.size(); ++k) { |
| 327 GetHelper(0)->AssertNavigationEquals(client0_tab->navigations[k], |
| 328 client1_tab->navigations[k]); |
| 329 } |
| 330 } |
| 331 } |
| 332 |
| 333 return true; |
| 334 } |
| 335 |
| 336 // Retrieves the foreign sessions for a particular profile and compares them |
| 337 // with a reference SessionWindow list. |
| 338 // Returns true if the session windows of the foreign session matches the |
| 339 // reference. |
| 340 bool CheckForeignSessionsAgainst(int index, |
| 341 const std::vector<std::vector<SessionWindow*>* >& windows) |
| 342 WARN_UNUSED_RESULT { |
| 343 ScopedVector<ForeignSession> sessions; |
| 344 if (!GetSessionData(index, &sessions.get())) |
| 345 return false; |
| 346 if ((size_t)(num_clients()-1) != sessions.size()) |
| 347 return false; |
| 348 |
| 349 int window_index = 0; |
| 350 for (size_t j = 0; j < sessions->size(); ++j, ++window_index) { |
| 351 if (window_index == index) |
| 352 window_index++; // Skip self. |
| 353 if (!WindowsMatch(sessions[j]->windows, *windows[window_index])) |
| 354 return false; |
| 355 } |
| 356 |
| 357 return true; |
| 358 } |
| 359 |
| 360 protected: |
| 361 // Clean up our mess. |
| 362 virtual void CleanUpOnMainThread() { |
| 363 // Close all browsers. We need to do this now, as opposed to letting the |
| 364 // test framework handle it, because we created our own browser for each |
| 365 // sync profile. |
| 366 BrowserList::CloseAllBrowsers(); |
| 367 ui_test_utils::RunAllPendingInMessageLoop(); |
| 368 |
| 369 // All browsers should be closed at this point, else when the framework |
| 370 // calls QuitBrowsers() we could see memory corruption. |
| 371 ASSERT_EQ(0U, BrowserList::size()); |
| 372 |
| 373 LiveSyncTest::CleanUpOnMainThread(); |
| 374 } |
| 375 |
| 376 // Vector of our TestSessionService helpers. |
| 377 ScopedVector<scoped_refptr<TestSessionService> > test_session_services_; |
| 378 |
| 379 // Vector of our browsers for each profile. |
| 380 std::vector<Browser*> browsers_; |
| 381 |
| 382 // Barrier for closing the browsers we create in UI thread. |
| 383 base::WaitableEvent done_closing_; |
| 384 |
| 385 DISALLOW_COPY_AND_ASSIGN(LiveSessionsSyncTest); |
| 386 }; |
| 387 |
| 388 class SingleClientLiveSessionsSyncTest : public LiveSessionsSyncTest { |
| 389 public: |
| 390 SingleClientLiveSessionsSyncTest() |
| 391 : LiveSessionsSyncTest(SINGLE_CLIENT) {} |
| 392 virtual ~SingleClientLiveSessionsSyncTest() {} |
| 393 |
| 394 private: |
| 395 DISALLOW_COPY_AND_ASSIGN(SingleClientLiveSessionsSyncTest); |
| 396 }; |
| 397 |
| 398 class TwoClientLiveSessionsSyncTest : public LiveSessionsSyncTest { |
| 399 public: |
| 400 TwoClientLiveSessionsSyncTest() : LiveSessionsSyncTest(TWO_CLIENT) {} |
| 401 virtual ~TwoClientLiveSessionsSyncTest() {} |
| 402 |
| 403 private: |
| 404 DISALLOW_COPY_AND_ASSIGN(TwoClientLiveSessionsSyncTest); |
| 405 }; |
| 406 |
| 407 class MultipleClientLiveSessionsSyncTest : public LiveSessionsSyncTest { |
| 408 public: |
| 409 MultipleClientLiveSessionsSyncTest() |
| 410 : LiveSessionsSyncTest(MULTIPLE_CLIENT) {} |
| 411 virtual ~MultipleClientLiveSessionsSyncTest() {} |
| 412 |
| 413 private: |
| 414 DISALLOW_COPY_AND_ASSIGN(MultipleClientLiveSessionsSyncTest); |
| 415 }; |
| 416 |
| 417 #endif // CHROME_TEST_LIVE_SYNC_LIVE_SESSIONS_SYNC_TEST_H_ |
| 418 |
OLD | NEW |