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