Home | History | Annotate | Download | only in integration
      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/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 "chrome/test/base/ui_test_utils.h"
     28 #include "url/gurl.h"
     29 
     30 using sync_datatype_helper::test;
     31 
     32 namespace sessions_helper {
     33 
     34 ScopedWindowMap::ScopedWindowMap() {
     35 }
     36 
     37 ScopedWindowMap::ScopedWindowMap(SessionWindowMap* windows) {
     38   Reset(windows);
     39 }
     40 
     41 ScopedWindowMap::~ScopedWindowMap() {
     42   STLDeleteContainerPairSecondPointers(windows_.begin(), windows_.end());
     43 }
     44 
     45 SessionWindowMap* ScopedWindowMap::GetMutable() {
     46   return &windows_;
     47 }
     48 
     49 const SessionWindowMap* ScopedWindowMap::Get() const {
     50   return &windows_;
     51 }
     52 
     53 void ScopedWindowMap::Reset(SessionWindowMap* windows) {
     54   STLDeleteContainerPairSecondPointers(windows_.begin(), windows_.end());
     55   windows_.clear();
     56   std::swap(*windows, windows_);
     57 }
     58 
     59 bool GetLocalSession(int index, const browser_sync::SyncedSession** session) {
     60   return ProfileSyncServiceFactory::GetInstance()->GetForProfile(
     61       test()->GetProfile(index))->GetOpenTabsUIDelegate()->
     62           GetLocalSession(session);
     63 }
     64 
     65 bool ModelAssociatorHasTabWithUrl(int index, const GURL& url) {
     66   content::RunAllPendingInMessageLoop();
     67   const browser_sync::SyncedSession* local_session;
     68   if (!GetLocalSession(index, &local_session)) {
     69     return false;
     70   }
     71 
     72   if (local_session->windows.size() == 0) {
     73     DVLOG(1) << "Empty windows vector";
     74     return false;
     75   }
     76 
     77   int nav_index;
     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";
     84       continue;
     85     }
     86     for (std::vector<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";
     91         continue;
     92       }
     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";
    100           continue;
    101         }
    102         return true;
    103       }
    104     }
    105   }
    106   DVLOG(1) << "Could not find tab with url " << url.spec();
    107   return false;
    108 }
    109 
    110 bool OpenTab(int index, const GURL& url) {
    111   DVLOG(1) << "Opening tab: " << url.spec() << " using profile "
    112            << index << ".";
    113   chrome::ShowSingletonTab(test()->GetBrowser(index), url);
    114   return WaitForTabsToLoad(index, std::vector<GURL>(1, url));
    115 }
    116 
    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
    122              << ".";
    123     chrome::ShowSingletonTab(browser, *it);
    124   }
    125   return WaitForTabsToLoad(index, urls);
    126 }
    127 
    128 namespace {
    129 
    130 class TabEventHandler : public browser_sync::LocalSessionEventHandler {
    131  public:
    132   TabEventHandler() : weak_factory_(this) {
    133     base::MessageLoop::current()->PostDelayedTask(
    134         FROM_HERE,
    135         base::Bind(&TabEventHandler::QuitLoop, weak_factory_.GetWeakPtr()),
    136         TestTimeouts::action_max_timeout());
    137   }
    138 
    139   virtual void OnLocalTabModified(
    140       browser_sync::SyncedTabDelegate* modified_tab) OVERRIDE {
    141     // Unwind to ensure SessionsSyncManager has processed the event.
    142     base::MessageLoop::current()->PostTask(
    143         FROM_HERE,
    144         base::Bind(&TabEventHandler::QuitLoop, weak_factory_.GetWeakPtr()));
    145   }
    146 
    147   virtual 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(
    151         FROM_HERE,
    152         base::Bind(&TabEventHandler::QuitLoop, weak_factory_.GetWeakPtr()));
    153   }
    154 
    155  private:
    156   void QuitLoop() {
    157     base::MessageLoop::current()->Quit();
    158   }
    159 
    160   base::WeakPtrFactory<TabEventHandler> weak_factory_;
    161 };
    162 
    163 }  // namespace
    164 
    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();
    169   bool found;
    170   for (std::vector<GURL>::const_iterator it = urls.begin();
    171        it != urls.end(); ++it) {
    172     found = false;
    173     while (!found) {
    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()
    178                    << " seconds.";
    179         return false;
    180       }
    181       if (!found) {
    182         TabEventHandler handler;
    183         browser_sync::NotificationServiceSessionsRouter router(
    184             test()->GetProfile(index),
    185             syncer::SyncableService::StartSyncFlare());
    186         router.StartRoutingTo(&handler);
    187         content::RunMessageLoop();
    188       }
    189     }
    190   }
    191   return true;
    192 }
    193 
    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)) {
    199     return false;
    200   }
    201   for (SessionWindowMap::const_iterator w = local_session->windows.begin();
    202        w != local_session->windows.end(); ++w) {
    203     const SessionWindow& window = *(w->second);
    204     SessionWindow* new_window = new 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 SessionTab& tab = *window.tabs.at(t);
    208       SessionTab* new_tab = new 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);
    213     }
    214     (*local_windows)[new_window->window_id.id()] = new_window;
    215   }
    216 
    217   return true;
    218 }
    219 
    220 bool OpenTabAndGetLocalWindows(int index,
    221                                const GURL& url,
    222                                SessionWindowMap* local_windows) {
    223   if (!OpenTab(index, url)) {
    224     return false;
    225   }
    226   return GetLocalWindows(index, local_windows);
    227 }
    228 
    229 bool CheckInitialState(int index) {
    230   if (0 != GetNumWindows(index))
    231     return false;
    232   if (0 != GetNumForeignSessions(index))
    233     return false;
    234   return true;
    235 }
    236 
    237 int GetNumWindows(int index) {
    238   const browser_sync::SyncedSession* local_session;
    239   if (!GetLocalSession(index, &local_session)) {
    240     return 0;
    241   }
    242   return local_session->windows.size();
    243 }
    244 
    245 int GetNumForeignSessions(int index) {
    246   SyncedSessionVector sessions;
    247   if (!ProfileSyncServiceFactory::GetInstance()->GetForProfile(
    248           test()->GetProfile(index))->
    249           GetOpenTabsUIDelegate()->GetAllForeignSessions(
    250               &sessions)) {
    251     return 0;
    252   }
    253   return sessions.size();
    254 }
    255 
    256 bool GetSessionData(int index, SyncedSessionVector* sessions) {
    257   if (!ProfileSyncServiceFactory::GetInstance()->GetForProfile(
    258           test()->GetProfile(index))->
    259           GetOpenTabsUIDelegate()->GetAllForeignSessions(
    260               sessions)) {
    261     return false;
    262   }
    263   SortSyncedSessions(sessions);
    264   return true;
    265 }
    266 
    267 bool CompareSyncedSessions(const browser_sync::SyncedSession* lhs,
    268                            const browser_sync::SyncedSession* rhs) {
    269   if (!lhs ||
    270       !rhs ||
    271       lhs->windows.size() < 1 ||
    272       rhs->windows.size() < 1) {
    273     // Catchall for uncomparable data.
    274     return false;
    275   }
    276 
    277   return lhs->windows < rhs->windows;
    278 }
    279 
    280 void SortSyncedSessions(SyncedSessionVector* sessions) {
    281   std::sort(sessions->begin(), sessions->end(),
    282             CompareSyncedSessions);
    283 }
    284 
    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();
    290     return false;
    291   }
    292   if (expected.referrer().url != actual.referrer().url) {
    293     LOG(ERROR) << "Expected referrer "
    294                << expected.referrer().url
    295                << ", actual "
    296                << actual.referrer().url;
    297     return false;
    298   }
    299   if (expected.title() != actual.title()) {
    300     LOG(ERROR) << "Expected title " << expected.title()
    301                << ", actual " << actual.title();
    302     return false;
    303   }
    304   if (expected.transition_type() != actual.transition_type()) {
    305     LOG(ERROR) << "Expected transition "
    306                << expected.transition_type()
    307                << ", actual "
    308                << actual.transition_type();
    309     return false;
    310   }
    311   return true;
    312 }
    313 
    314 bool WindowsMatch(const SessionWindowMap& win1,
    315                   const SessionWindowMap& win2) {
    316   SessionTab* client0_tab;
    317   SessionTab* client1_tab;
    318   if (win1.size() != win2.size())
    319     return false;
    320   for (SessionWindowMap::const_iterator i = win1.begin();
    321        i != win1.end(); ++i) {
    322     SessionWindowMap::const_iterator j = win2.find(i->first);
    323     if (j == win2.end())
    324       return false;
    325     if (i->second->tabs.size() != j->second->tabs.size())
    326       return false;
    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])) {
    333           return false;
    334         }
    335       }
    336     }
    337   }
    338 
    339   return true;
    340 }
    341 
    342 bool CheckForeignSessionsAgainst(
    343     int index,
    344     const std::vector<ScopedWindowMap>& windows) {
    345   SyncedSessionVector sessions;
    346   if (!GetSessionData(index, &sessions))
    347     return false;
    348   if ((size_t)(test()->num_clients()-1) != sessions.size())
    349     return false;
    350 
    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())))
    357       return false;
    358   }
    359 
    360   return true;
    361 }
    362 
    363 namespace {
    364 
    365 // Helper class used in the implementation of AwaitCheckForeignSessionsAgainst.
    366 class CheckForeignSessionsChecker : public MultiClientStatusChangeChecker {
    367  public:
    368   CheckForeignSessionsChecker(int index,
    369                               const std::vector<ScopedWindowMap>& windows);
    370   virtual ~CheckForeignSessionsChecker();
    371 
    372   virtual bool IsExitConditionSatisfied() OVERRIDE;
    373   virtual std::string GetDebugMessage() const OVERRIDE;
    374  private:
    375   int index_;
    376   const std::vector<ScopedWindowMap>& windows_;
    377 };
    378 
    379 CheckForeignSessionsChecker::CheckForeignSessionsChecker(
    380     int index, const std::vector<ScopedWindowMap>& windows)
    381     : MultiClientStatusChangeChecker(
    382         sync_datatype_helper::test()->GetSyncServices()),
    383       index_(index),
    384       windows_(windows) {}
    385 
    386 CheckForeignSessionsChecker::~CheckForeignSessionsChecker() {}
    387 
    388 bool CheckForeignSessionsChecker::IsExitConditionSatisfied() {
    389   return CheckForeignSessionsAgainst(index_, windows_);
    390 }
    391 
    392 std::string CheckForeignSessionsChecker::GetDebugMessage() const {
    393   return "Waiting for matching foreign sessions";
    394 }
    395 
    396 }  //  namespace
    397 
    398 bool AwaitCheckForeignSessionsAgainst(
    399     int index, const std::vector<ScopedWindowMap>& windows) {
    400   CheckForeignSessionsChecker checker(index, windows);
    401   checker.Wait();
    402   return !checker.TimedOut();
    403 }
    404 
    405 void DeleteForeignSession(int index, std::string session_tag) {
    406   ProfileSyncServiceFactory::GetInstance()->GetForProfile(
    407       test()->GetProfile(index))->
    408           GetOpenTabsUIDelegate()->DeleteForeignSession(session_tag);
    409 }
    410 
    411 }  // namespace sessions_helper
    412