blob: 98e0936e214d9914a5802d3d8427b0ce63d44bab [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/sync/test/integration/sessions_helper.h"
#include <stddef.h>
#include <set>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/sync/sessions/notification_service_sessions_router.h"
#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
#include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
#include "chrome/browser/ui/singleton_tabs.h"
#include "chrome/common/chrome_switches.h"
#include "components/browser_sync/profile_sync_service.h"
#include "components/sync/driver/sync_client.h"
#include "components/sync_sessions/open_tabs_ui_delegate.h"
#include "content/public/test/test_utils.h"
#include "url/gurl.h"
using sync_datatype_helper::test;
namespace sessions_helper {
bool GetLocalSession(int index, const sync_sessions::SyncedSession** session) {
return ProfileSyncServiceFactory::GetInstance()->GetForProfile(
test()->GetProfile(index))->GetOpenTabsUIDelegate()->
GetLocalSession(session);
}
bool ModelAssociatorHasTabWithUrl(int index, const GURL& url) {
content::RunAllPendingInMessageLoop();
const sync_sessions::SyncedSession* local_session;
if (!GetLocalSession(index, &local_session)) {
return false;
}
if (local_session->windows.size() == 0) {
DVLOG(1) << "Empty windows vector";
return false;
}
int nav_index;
sessions::SerializedNavigationEntry nav;
for (auto it = local_session->windows.begin();
it != local_session->windows.end(); ++it) {
if (it->second->tabs.size() == 0) {
DVLOG(1) << "Empty tabs vector";
continue;
}
for (auto tab_it = it->second->tabs.begin();
tab_it != it->second->tabs.end(); ++tab_it) {
if ((*tab_it)->navigations.size() == 0) {
DVLOG(1) << "Empty navigations vector";
continue;
}
nav_index = (*tab_it)->current_navigation_index;
nav = (*tab_it)->navigations[nav_index];
if (nav.virtual_url() == url) {
DVLOG(1) << "Found tab with url " << url.spec();
DVLOG(1) << "Timestamp is " << nav.timestamp().ToInternalValue();
if (nav.title().empty()) {
DVLOG(1) << "Title empty -- tab hasn't finished loading yet";
continue;
}
return true;
}
}
}
DVLOG(1) << "Could not find tab with url " << url.spec();
return false;
}
bool OpenTab(int index, const GURL& url) {
DVLOG(1) << "Opening tab: " << url.spec() << " using profile "
<< index << ".";
chrome::ShowSingletonTab(test()->GetBrowser(index), url);
return WaitForTabsToLoad(index, std::vector<GURL>(1, url));
}
bool OpenMultipleTabs(int index, const std::vector<GURL>& urls) {
Browser* browser = test()->GetBrowser(index);
for (std::vector<GURL>::const_iterator it = urls.begin();
it != urls.end(); ++it) {
DVLOG(1) << "Opening tab: " << it->spec() << " using profile " << index
<< ".";
chrome::ShowSingletonTab(browser, *it);
}
return WaitForTabsToLoad(index, urls);
}
namespace {
class TabEventHandler : public sync_sessions::LocalSessionEventHandler {
public:
TabEventHandler() : weak_factory_(this) {
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::Bind(&TabEventHandler::QuitLoop, weak_factory_.GetWeakPtr()),
TestTimeouts::action_max_timeout());
}
void OnLocalTabModified(
sync_sessions::SyncedTabDelegate* modified_tab) override {
// Unwind to ensure SessionsSyncManager has processed the event.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&TabEventHandler::QuitLoop, weak_factory_.GetWeakPtr()));
}
void OnFaviconsChanged(const std::set<GURL>& /* page_urls */,
const GURL& /* icon_url */) override {
// Unwind to ensure SessionsSyncManager has processed the event.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&TabEventHandler::QuitLoop, weak_factory_.GetWeakPtr()));
}
private:
void QuitLoop() { base::MessageLoop::current()->QuitWhenIdle(); }
base::WeakPtrFactory<TabEventHandler> weak_factory_;
};
} // namespace
bool WaitForTabsToLoad(int index, const std::vector<GURL>& urls) {
DVLOG(1) << "Waiting for session to propagate to associator.";
base::TimeTicks start_time = base::TimeTicks::Now();
base::TimeTicks end_time = start_time + TestTimeouts::action_max_timeout();
bool found;
for (std::vector<GURL>::const_iterator it = urls.begin();
it != urls.end(); ++it) {
found = false;
while (!found) {
found = ModelAssociatorHasTabWithUrl(index, *it);
if (base::TimeTicks::Now() >= end_time) {
LOG(ERROR) << "Failed to find all tabs after "
<< TestTimeouts::action_max_timeout().InSecondsF()
<< " seconds.";
return false;
}
if (!found) {
TabEventHandler handler;
sync_sessions::NotificationServiceSessionsRouter router(
test()->GetProfile(index),
ProfileSyncServiceFactory::GetInstance()
->GetForProfile(test()->GetProfile(index))
->GetSyncClient()
->GetSyncSessionsClient(),
syncer::SyncableService::StartSyncFlare());
router.StartRoutingTo(&handler);
content::RunMessageLoop();
}
}
}
return true;
}
bool GetLocalWindows(int index, ScopedWindowMap* local_windows) {
// The local session provided by GetLocalSession is owned, and has lifetime
// controlled, by the model associator, so we must make our own copy.
const sync_sessions::SyncedSession* local_session;
if (!GetLocalSession(index, &local_session)) {
return false;
}
for (auto w = local_session->windows.begin();
w != local_session->windows.end(); ++w) {
const sessions::SessionWindow& window = *(w->second);
std::unique_ptr<sessions::SessionWindow> new_window =
base::MakeUnique<sessions::SessionWindow>();
new_window->window_id.set_id(window.window_id.id());
for (size_t t = 0; t < window.tabs.size(); ++t) {
const sessions::SessionTab& tab = *window.tabs.at(t);
std::unique_ptr<sessions::SessionTab> new_tab =
base::MakeUnique<sessions::SessionTab>();
new_tab->navigations.resize(tab.navigations.size());
std::copy(tab.navigations.begin(), tab.navigations.end(),
new_tab->navigations.begin());
new_window->tabs.push_back(std::move(new_tab));
}
auto id = new_window->window_id.id();
(*local_windows)[id] = std::move(new_window);
}
return true;
}
bool OpenTabAndGetLocalWindows(int index,
const GURL& url,
ScopedWindowMap* local_windows) {
if (!OpenTab(index, url)) {
return false;
}
return GetLocalWindows(index, local_windows);
}
bool CheckInitialState(int index) {
if (0 != GetNumWindows(index))
return false;
if (0 != GetNumForeignSessions(index))
return false;
return true;
}
int GetNumWindows(int index) {
const sync_sessions::SyncedSession* local_session;
if (!GetLocalSession(index, &local_session)) {
return 0;
}
return local_session->windows.size();
}
int GetNumForeignSessions(int index) {
SyncedSessionVector sessions;
if (!ProfileSyncServiceFactory::GetInstance()->GetForProfile(
test()->GetProfile(index))->
GetOpenTabsUIDelegate()->GetAllForeignSessions(
&sessions)) {
return 0;
}
return sessions.size();
}
bool GetSessionData(int index, SyncedSessionVector* sessions) {
if (!ProfileSyncServiceFactory::GetInstance()->GetForProfile(
test()->GetProfile(index))->
GetOpenTabsUIDelegate()->GetAllForeignSessions(
sessions)) {
return false;
}
SortSyncedSessions(sessions);
return true;
}
bool CompareSyncedSessions(const sync_sessions::SyncedSession* lhs,
const sync_sessions::SyncedSession* rhs) {
if (!lhs ||
!rhs ||
lhs->windows.size() < 1 ||
rhs->windows.size() < 1) {
// Catchall for uncomparable data.
return false;
}
return lhs->windows < rhs->windows;
}
void SortSyncedSessions(SyncedSessionVector* sessions) {
std::sort(sessions->begin(), sessions->end(),
CompareSyncedSessions);
}
bool NavigationEquals(const sessions::SerializedNavigationEntry& expected,
const sessions::SerializedNavigationEntry& actual) {
if (expected.virtual_url() != actual.virtual_url()) {
LOG(ERROR) << "Expected url " << expected.virtual_url()
<< ", actual " << actual.virtual_url();
return false;
}
if (expected.referrer_url() != actual.referrer_url()) {
LOG(ERROR) << "Expected referrer "
<< expected.referrer_url()
<< ", actual "
<< actual.referrer_url();
return false;
}
if (expected.title() != actual.title()) {
LOG(ERROR) << "Expected title " << expected.title()
<< ", actual " << actual.title();
return false;
}
if (!ui::PageTransitionTypeIncludingQualifiersIs(expected.transition_type(),
actual.transition_type())) {
LOG(ERROR) << "Expected transition "
<< expected.transition_type()
<< ", actual "
<< actual.transition_type();
return false;
}
return true;
}
namespace {
template <typename T1, typename T2>
bool WindowsMatchImpl(const T1& win1, const T2& win2) {
sessions::SessionTab* client0_tab;
sessions::SessionTab* client1_tab;
if (win1.size() != win2.size()) {
LOG(ERROR) << "Win size doesn't match, win1 size: "
<< win1.size()
<< ", win2 size: "
<< win2.size();
return false;
}
for (auto i = win1.begin(); i != win1.end(); ++i) {
auto j = win2.find(i->first);
if (j == win2.end()) {
LOG(ERROR) << "Session doesn't match";
return false;
}
if (i->second->tabs.size() != j->second->tabs.size()) {
LOG(ERROR) << "Tab size doesn't match, tab1 size: "
<< i->second->tabs.size()
<< ", tab2 size: "
<< j->second->tabs.size();
return false;
}
for (size_t t = 0; t < i->second->tabs.size(); ++t) {
client0_tab = i->second->tabs[t].get();
client1_tab = j->second->tabs[t].get();
for (size_t n = 0; n < client0_tab->navigations.size(); ++n) {
if (!NavigationEquals(client0_tab->navigations[n],
client1_tab->navigations[n])) {
return false;
}
}
}
}
return true;
}
} // namespace
bool WindowsMatch(const ScopedWindowMap& win1, const ScopedWindowMap& win2) {
return WindowsMatchImpl(win1, win2);
}
bool WindowsMatch(const SessionWindowMap& win1, const ScopedWindowMap& win2) {
return WindowsMatchImpl(win1, win2);
}
bool CheckForeignSessionsAgainst(
int index,
const std::vector<ScopedWindowMap>& windows) {
SyncedSessionVector sessions;
if (!GetSessionData(index, &sessions)) {
LOG(ERROR) << "Cannot get session data";
return false;
}
for (size_t w_index = 0; w_index < windows.size(); ++w_index) {
// Skip the client's local window
if (static_cast<int>(w_index) == index)
continue;
size_t s_index = 0;
for (; s_index < sessions.size(); ++s_index) {
if (WindowsMatch(sessions[s_index]->windows, windows[w_index]))
break;
}
if (s_index == sessions.size()) {
LOG(ERROR) << "Cannot find window #" << w_index;
return false;
}
}
return true;
}
void DeleteForeignSession(int index, std::string session_tag) {
ProfileSyncServiceFactory::GetInstance()
->GetForProfile(test()->GetProfile(index))
->GetOpenTabsUIDelegate()
->DeleteForeignSession(session_tag);
}
} // namespace sessions_helper
ForeignSessionsMatchChecker::ForeignSessionsMatchChecker(
int index,
const std::vector<sessions_helper::ScopedWindowMap>& windows)
: MultiClientStatusChangeChecker(
sync_datatype_helper::test()->GetSyncServices()),
index_(index),
windows_(windows) {}
bool ForeignSessionsMatchChecker::IsExitConditionSatisfied() {
return sessions_helper::CheckForeignSessionsAgainst(index_, windows_);
}
std::string ForeignSessionsMatchChecker::GetDebugMessage() const {
return "Waiting for matching foreign sessions";
}