Commit a61d2bb4 authored by Mikel Astiz's avatar Mikel Astiz Committed by Commit Bot

Reland "Avoid recycling sync tabs if commit pending"

This is a reland of 62414cfc

Test improved to avoid flakes.

Original change's description:
> Avoid recycling sync tabs if commit pending
>
> When a tab is closed, it's possible that the corresponding history
> hasn't been committed yet, and hence there is a risk that synced history
> is lost if the entity is recycled (for another tab that is opened).
>
> In this patch, and behind a feature toggle, this issue is prevented by
> *not* freeing tab nodes while the sync entity is unsynced. Old tabs are
> excluded from this (to avoid problems with expired history) and a max
> cap is also introduced to the number of tabs in this state, in order to
> avoid memory regressions.
>
> Bug: 882489
> Change-Id: I6dd796642f9553f2713a0814731897a4ffb13f0b
> Reviewed-on: https://chromium-review.googlesource.com/c/1356541
> Commit-Queue: Mikel Astiz <mastiz@chromium.org>
> Reviewed-by: Marc Treib <treib@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#612971}

TBR=treib@chromium.org

Bug: 882489
Change-Id: Ib3f3ff9e4d620512435d9b22ba7b2c338f92203f
Reviewed-on: https://chromium-review.googlesource.com/c/1357086Reviewed-by: default avatarMikel Astiz <mastiz@chromium.org>
Commit-Queue: Mikel Astiz <mastiz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#612980}
parent 3a9cafd9
......@@ -5,6 +5,7 @@
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/sessions/session_service.h"
#include "chrome/browser/sessions/session_tab_helper.h"
#include "chrome/browser/sync/session_sync_service_factory.h"
......@@ -24,6 +25,7 @@
#include "components/sync/protocol/proto_value_conversions.h"
#include "components/sync/test/fake_server/sessions_hierarchy.h"
#include "components/sync_sessions/session_sync_service.h"
#include "components/sync_sessions/synced_session_tracker.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/mojo/window_open_disposition.mojom.h"
......@@ -47,6 +49,7 @@ using sessions_helper::OpenTabFromSourceIndex;
using sessions_helper::ScopedWindowMap;
using sessions_helper::SessionWindowMap;
using sessions_helper::SyncedSessionVector;
using sessions_helper::WaitForTabsToLoad;
using sessions_helper::WindowsMatch;
using typed_urls_helper::GetUrlFromClient;
......@@ -227,6 +230,7 @@ IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, NavigateThenCloseTab) {
// Close one of the two tabs immediately after issuing an navigation. We also
// issue another navigation to make sure association logic kicks in.
NavigateTab(0, GURL(kURL3));
ASSERT_TRUE(WaitForTabsToLoad(0, {GURL(kURL1), GURL(kURL3)}));
CloseTab(/*index=*/0, /*tab_index=*/1);
NavigateTab(0, GURL(kURL4));
......@@ -239,6 +243,38 @@ IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, NavigateThenCloseTab) {
IsUrlSyncedChecker(kURL3, GetFakeServer(), GetSyncService(0)).Wait());
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest,
NavigateThenCloseTabThenOpenTab) {
base::test::ScopedFeatureList override_features;
override_features.InitAndEnableFeature(
sync_sessions::kDeferRecyclingOfSyncTabNodesIfUnsynced);
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
ASSERT_TRUE(CheckInitialState(0));
// Two tabs are opened initially.
ASSERT_TRUE(OpenTab(0, GURL(kURL1)));
ASSERT_TRUE(OpenTab(0, GURL(kURL2)));
WaitForHierarchyOnServer(SessionsHierarchy({{kURL1, kURL2}}));
// Close one of the two tabs immediately after issuing an navigation. In
// addition, a new tab is opened.
NavigateTab(0, GURL(kURL3));
ASSERT_TRUE(WaitForTabsToLoad(0, {GURL(kURL1), GURL(kURL3)}));
CloseTab(/*index=*/0, /*tab_index=*/1);
ASSERT_TRUE(OpenTab(0, GURL(kURL4)));
DLOG(INFO) << "Waiting for kURL4 to be synced";
ASSERT_TRUE(
IsUrlSyncedChecker(kURL4, GetFakeServer(), GetSyncService(0)).Wait());
// All URLs should be synced, for synced history to be complete. In
// particular, |kURL3| should be synced despite the tab being closed.
DLOG(INFO) << "Waiting for kURL3 to be synced";
EXPECT_TRUE(
IsUrlSyncedChecker(kURL3, GetFakeServer(), GetSyncService(0)).Wait());
}
IN_PROC_BROWSER_TEST_F(SingleClientSessionsSyncTest, TimestampMatchesHistory) {
ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
......
......@@ -91,6 +91,9 @@ LocalSessionEventHandlerImpl::~LocalSessionEventHandlerImpl() {}
void LocalSessionEventHandlerImpl::OnSessionRestoreComplete() {
std::unique_ptr<WriteBatch> batch = delegate_->CreateLocalSessionWriteBatch();
// The initial state of the tracker may contain tabs that are unmmapped but
// haven't been marked as free yet.
CleanupLocalTabs(batch.get());
AssociateWindows(RELOAD_TABS, batch.get());
batch->Commit();
}
......@@ -101,6 +104,16 @@ LocalSessionEventHandlerImpl::GetTabSpecificsFromDelegateForTest(
return GetTabSpecificsFromDelegate(tab_delegate);
}
void LocalSessionEventHandlerImpl::CleanupLocalTabs(WriteBatch* batch) {
std::set<int> deleted_tab_node_ids =
session_tracker_->CleanupLocalTabs(base::BindRepeating(
&Delegate::IsTabNodeUnsynced, base::Unretained(delegate_)));
for (int tab_node_id : deleted_tab_node_ids) {
batch->Delete(tab_node_id);
}
}
void LocalSessionEventHandlerImpl::AssociateWindows(ReloadTabsOption option,
WriteBatch* batch) {
DCHECK(!IsSessionRestoreInProgress(sessions_client_));
......@@ -219,11 +232,7 @@ void LocalSessionEventHandlerImpl::AssociateWindows(ReloadTabsOption option,
}
}
std::set<int> deleted_tab_node_ids;
session_tracker_->CleanupLocalTabs(&deleted_tab_node_ids);
for (int tab_node_id : deleted_tab_node_ids) {
batch->Delete(tab_node_id);
}
CleanupLocalTabs(batch);
// Always update the header. Sync takes care of dropping this update
// if the entity specifics are identical (i.e windows, client name did
......
......@@ -48,6 +48,7 @@ class LocalSessionEventHandlerImpl : public LocalSessionEventHandler {
public:
virtual ~Delegate();
virtual std::unique_ptr<WriteBatch> CreateLocalSessionWriteBatch() = 0;
virtual bool IsTabNodeUnsynced(int tab_node_id) = 0;
// Analogous to SessionsGlobalIdMapper.
virtual void TrackLocalNavigationId(base::Time timestamp,
int unique_id) = 0;
......@@ -79,6 +80,8 @@ class LocalSessionEventHandlerImpl : public LocalSessionEventHandler {
private:
enum ReloadTabsOption { RELOAD_TABS, DONT_RELOAD_TABS };
void CleanupLocalTabs(WriteBatch* batch);
void AssociateWindows(ReloadTabsOption option,
WriteBatch* batch);
......
......@@ -8,6 +8,7 @@
#include <vector>
#include "base/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h"
#include "components/sessions/core/serialized_navigation_entry.h"
#include "components/sessions/core/serialized_navigation_entry_test_helper.h"
#include "components/sync/base/time.h"
......@@ -71,6 +72,7 @@ class MockDelegate : public LocalSessionEventHandlerImpl::Delegate {
MOCK_METHOD0(CreateLocalSessionWriteBatch,
std::unique_ptr<LocalSessionEventHandlerImpl::WriteBatch>());
MOCK_METHOD1(IsTabNodeUnsynced, bool(int tab_node_id));
MOCK_METHOD2(TrackLocalNavigationId,
void(base::Time timestamp, int unique_id));
MOCK_METHOD1(OnPageFaviconUpdated, void(const GURL& page_url));
......@@ -355,6 +357,16 @@ TEST_F(LocalSessionEventHandlerImplTest, DontUpdateWindowIdForPlaceholderTab) {
UpdateTrackerWithSpecifics(placeholder_tab, base::Time::Now(),
&session_tracker_);
// Mimic the header being restored from peristence too.
session_tracker_.PutWindowInSession(
kSessionTag, SessionID::FromSerializedValue(kWindowId1));
session_tracker_.PutTabInWindow(kSessionTag,
SessionID::FromSerializedValue(kWindowId1),
SessionID::FromSerializedValue(kTabId1));
session_tracker_.PutTabInWindow(kSessionTag,
SessionID::FromSerializedValue(kWindowId1),
SessionID::FromSerializedValue(kTabId2));
// Window ID has changed when the browser is started.
TestSyncedWindowDelegate* window = AddWindow(kWindowId2);
AddTab(kWindowId2, kFoo1, kTabId1);
......@@ -578,6 +590,78 @@ TEST_F(LocalSessionEventHandlerImplTest, PropagateNewTab) {
AddTab(kWindowId1, kBar1, kTabId2);
}
TEST_F(LocalSessionEventHandlerImplTest, PropagateClosedTab) {
AddWindow(kWindowId1);
AddTab(kWindowId1, kFoo1, kTabId1);
TestSyncedTabDelegate* tab2 = AddTab(kWindowId1, kBar1, kTabId2);
InitHandler();
// Closing a tab (later below) is expected to verify if the sync entity is
// unsynced.
EXPECT_CALL(mock_delegate_, IsTabNodeUnsynced(/*tab_node_id=*/0));
// Closing a tab is expected to update the header and the remaining tab (this
// test issues a navigation for it, but it would have been updated anyway).
auto mock_batch = std::make_unique<StrictMock<MockWriteBatch>>();
EXPECT_CALL(
*mock_batch,
Put(Pointee(MatchesHeader(kSessionTag, {kWindowId1}, {kTabId2}))));
EXPECT_CALL(*mock_batch,
Put(Pointee(MatchesTab(kSessionTag, kWindowId1, kTabId2,
/*tab_node_id=*/1, /*urls=*/{kBar1}))));
EXPECT_CALL(*mock_batch, Commit());
EXPECT_CALL(mock_delegate_, CreateLocalSessionWriteBatch())
.WillOnce(Return(ByMove(std::move(mock_batch))));
// Close tab and force reassociation.
window_getter_.CloseTab(SessionID::FromSerializedValue(kTabId1));
handler_->OnLocalTabModified(tab2);
}
TEST_F(LocalSessionEventHandlerImplTest,
PropagateClosedTabWithDeferredRecyclingAndImmediateDeletion) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatures(
/*enabled_features=*/{kDeferRecyclingOfSyncTabNodesIfUnsynced,
kTabNodePoolImmediateDeletion},
/*disabled_features=*/{});
// We start with three tabs.
AddWindow(kWindowId1);
AddTab(kWindowId1, kFoo1, kTabId1);
AddTab(kWindowId1, kBar1, kTabId2);
TestSyncedTabDelegate* tab3 = AddTab(kWindowId1, kBaz1, kTabId3);
InitHandler();
// |kTabId2| is unsynced, so it shouldn't be deleted even if it's closed.
EXPECT_CALL(mock_delegate_, IsTabNodeUnsynced(/*tab_node_id=*/0))
.WillOnce(Return(false));
EXPECT_CALL(mock_delegate_, IsTabNodeUnsynced(/*tab_node_id=*/1))
.WillOnce(Return(true));
// Closing two tabs (later below) is expected to update the header and the
// remaining tab. In addition, one of the two closed tabs (the one that is
// synced) should be deleted.
auto mock_batch = std::make_unique<StrictMock<MockWriteBatch>>();
EXPECT_CALL(
*mock_batch,
Put(Pointee(MatchesHeader(kSessionTag, {kWindowId1}, {kTabId3}))));
EXPECT_CALL(*mock_batch,
Put(Pointee(MatchesTab(kSessionTag, kWindowId1, kTabId3,
/*tab_node_id=*/2, /*urls=*/{kBaz1}))));
EXPECT_CALL(*mock_batch, Delete(/*tab_node_id=*/0));
EXPECT_CALL(*mock_batch, Commit());
EXPECT_CALL(mock_delegate_, CreateLocalSessionWriteBatch())
.WillOnce(Return(ByMove(std::move(mock_batch))));
// Close two tabs and force reassociation.
window_getter_.CloseTab(SessionID::FromSerializedValue(kTabId1));
window_getter_.CloseTab(SessionID::FromSerializedValue(kTabId2));
handler_->OnLocalTabModified(tab3);
}
TEST_F(LocalSessionEventHandlerImplTest, PropagateNewCustomTab) {
InitHandler();
......
......@@ -313,6 +313,12 @@ SessionSyncBridge::CreateLocalSessionWriteBatch() {
change_processor());
}
bool SessionSyncBridge::IsTabNodeUnsynced(int tab_node_id) {
const std::string storage_key = SessionStore::GetTabStorageKey(
syncing_->store->local_session_info().session_tag, tab_node_id);
return change_processor()->IsEntityUnsynced(storage_key);
}
void SessionSyncBridge::TrackLocalNavigationId(base::Time timestamp,
int unique_id) {
global_id_mapper_.TrackNavigationId(timestamp, unique_id);
......
......@@ -72,6 +72,7 @@ class SessionSyncBridge : public syncer::ModelTypeSyncBridge,
// LocalSessionEventHandlerImpl::Delegate implementation.
std::unique_ptr<LocalSessionEventHandlerImpl::WriteBatch>
CreateLocalSessionWriteBatch() override;
bool IsTabNodeUnsynced(int tab_node_id) override;
void TrackLocalNavigationId(base::Time timestamp, int unique_id) override;
void OnPageFaviconUpdated(const GURL& page_url) override;
void OnFaviconVisited(const GURL& page_url, const GURL& favicon_url) override;
......
......@@ -15,8 +15,22 @@
namespace sync_sessions {
const base::Feature kDeferRecyclingOfSyncTabNodesIfUnsynced{
"DeferRecyclingOfSyncTabNodesIfUnsynced",
base::FEATURE_DISABLED_BY_DEFAULT};
namespace {
// Maximum time we allow a local tab stay unmapped (i.e. closed) but not freed
// due to data not having been committed yet. After that time, the data will
// be dropped.
constexpr base::TimeDelta kMaxUnmappedButUnsyncedLocalTabAge =
base::TimeDelta::FromDays(1);
// This is a generous cap to avoid issues with situations like sync being in
// error state (e.g. auth error) during which many tabs could be opened and
// closed, and still the information would not be committed.
constexpr int kMaxUnmappedButUnsyncedLocalTabCount = 20;
// Helper for iterating through all tabs within a window, and all navigations
// within a tab, to find if there's a valid syncable url.
bool ShouldSyncSessionWindow(SyncSessionsClient* sessions_client,
......@@ -319,7 +333,10 @@ std::vector<const SyncedSession*> SyncedSessionTracker::LookupSessions(
return sessions;
}
void SyncedSessionTracker::CleanupSessionImpl(const std::string& session_tag) {
void SyncedSessionTracker::CleanupSessionImpl(
const std::string& session_tag,
const base::RepeatingCallback<bool(int /*tab_node_id*/)>&
is_tab_node_unsynced_cb) {
TrackedSession* session = LookupTrackedSession(session_tag);
if (!session)
return;
......@@ -328,13 +345,36 @@ void SyncedSessionTracker::CleanupSessionImpl(const std::string& session_tag) {
session->synced_window_map.erase(window_pair.first);
session->unmapped_windows.clear();
for (const auto& tab_pair : session->unmapped_tabs) {
session->synced_tab_map.erase(tab_pair.first);
int num_unmapped_and_unsynced = 0;
auto tab_it = session->unmapped_tabs.begin();
while (tab_it != session->unmapped_tabs.end()) {
SessionID tab_id = tab_it->first;
if (session_tag == local_session_tag_) {
int tab_node_id = session->tab_node_pool.GetTabNodeIdFromTabId(tab_id);
const base::TimeDelta time_since_last_modified =
base::Time::Now() - tab_it->second->timestamp;
if ((time_since_last_modified < kMaxUnmappedButUnsyncedLocalTabAge) &&
num_unmapped_and_unsynced < kMaxUnmappedButUnsyncedLocalTabCount &&
is_tab_node_unsynced_cb.Run(tab_node_id) &&
base::FeatureList::IsEnabled(
kDeferRecyclingOfSyncTabNodesIfUnsynced)) {
// Our caller has decided that this tab node cannot be reused at this
// point because there are pending changes to be committed that would
// otherwise be lost). Hence, it stays unmapped but we do not free the
// tab node for now (until future retries).
++tab_it;
++num_unmapped_and_unsynced;
continue;
}
session->tab_node_pool.FreeTab(tab_id);
}
if (session_tag == local_session_tag_)
session->tab_node_pool.FreeTab(tab_pair.first);
session->synced_tab_map.erase(tab_id);
tab_it = session->unmapped_tabs.erase(tab_it);
}
session->unmapped_tabs.clear();
}
bool SyncedSessionTracker::IsTabUnmappedForTesting(SessionID tab_id) {
......@@ -490,14 +530,23 @@ sessions::SessionTab* SyncedSessionTracker::GetTab(
}
void SyncedSessionTracker::CleanupSession(const std::string& session_tag) {
CleanupSessionImpl(session_tag);
DCHECK_NE(session_tag, local_session_tag_);
// |is_tab_node_unsynced_cb| is only used for the local session, not needed
// here.
CleanupSessionImpl(
session_tag,
/*=is_tab_node_unsynced_cb=*/base::RepeatingCallback<bool(int)>());
}
void SyncedSessionTracker::CleanupLocalTabs(std::set<int>* deleted_node_ids) {
std::set<int> SyncedSessionTracker::CleanupLocalTabs(
const base::RepeatingCallback<bool(int /*tab_node_id*/)>&
is_tab_node_unsynced_cb) {
DCHECK(!local_session_tag_.empty());
TrackedSession* session = GetTrackedSession(local_session_tag_);
CleanupSessionImpl(local_session_tag_);
session->tab_node_pool.CleanupTabNodes(deleted_node_ids);
CleanupSessionImpl(local_session_tag_, is_tab_node_unsynced_cb);
std::set<int> deleted_node_ids;
session->tab_node_pool.CleanupTabNodes(&deleted_node_ids);
return deleted_node_ids;
}
int SyncedSessionTracker::LookupTabNodeFromTabId(const std::string& session_tag,
......@@ -647,8 +696,11 @@ void UpdateTrackerWithSpecifics(const sync_pb::SessionSpecifics& specifics,
PopulateSyncedSessionFromSpecifics(session_tag, header, modification_time,
session, tracker);
// Delete any closed windows and unused tabs as necessary.
tracker->CleanupSession(session_tag);
// Delete any closed windows and unused tabs as necessary. We exclude the
// local session here because it should be cleaned up explicitly with
// CleanupLocalTabs().
if (session_tag != tracker->GetLocalSessionTag())
tracker->CleanupSession(session_tag);
} else if (specifics.has_tab()) {
const sync_pb::SessionTab& tab_s = specifics.tab();
SessionID tab_id = SessionID::FromSerializedValue(tab_s.tab_id());
......
......@@ -13,6 +13,7 @@
#include <string>
#include <vector>
#include "base/feature_list.h"
#include "base/macros.h"
#include "components/sessions/core/session_id.h"
#include "components/sessions/core/session_types.h"
......@@ -27,6 +28,10 @@ namespace sync_sessions {
class SyncSessionsClient;
// TODO(crbug.com/882489): Remove feature toggle during code cleanup when a
// satisfying solution is found for closed tabs.
extern const base::Feature kDeferRecyclingOfSyncTabNodesIfUnsynced;
// Class to manage synced sessions. The tracker will own all SyncedSession
// and SyncedSessionTab objects it creates, and deletes them appropriately on
// destruction.
......@@ -172,10 +177,15 @@ class SyncedSessionTracker {
// Gets the session tag previously set with InitLocalSession().
const std::string& GetLocalSessionTag() const;
// Similar to CleanupSession() but also triggers garbage collection of free
// tab nodes and consequently fills |deleted_node_ids| with the set of
// locally free tab nodes to be deleted.
void CleanupLocalTabs(std::set<int>* deleted_node_ids);
// Similar to CleanupSession() but also marks unmapped tabs (i.e. closed ones)
// as free tab nodes (which can be reused by future tabs) and triggers garbage
// collection (i.e. deletion) of free tab nodes. It returns the set of locally
// free tab nodes to be deleted. |is_tab_node_unsynced_cb| allows callers to
// prevent tab nodes from being "free" (and hence reusable), which in practice
// is useful to avoid overriding data that hasn't been synced yet.
std::set<int> CleanupLocalTabs(
const base::RepeatingCallback<bool(int /*tab_node_id*/)>&
is_tab_node_unsynced_cb);
// Returns the tab node ID for |tab_id| if an existing tab node was found, or
// kInvalidTabNodeID otherwise.
......@@ -265,7 +275,10 @@ class SyncedSessionTracker {
bool exclude_local_session) const;
// Implementation of CleanupSession()/CleanupLocalTabs().
void CleanupSessionImpl(const std::string& session_tag);
void CleanupSessionImpl(
const std::string& session_tag,
const base::RepeatingCallback<bool(int /*tab_node_id*/)>&
is_tab_node_unsynced_cb);
// The client of the sync sessions datatype.
SyncSessionsClient* const sessions_client_;
......
......@@ -8,12 +8,14 @@
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "components/sessions/core/serialized_navigation_entry_test_helper.h"
#include "components/sync_sessions/mock_sync_sessions_client.h"
#include "components/sync_sessions/synced_tab_delegate.h"
#include "components/sync_sessions/test_matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::AssertionFailure;
using testing::AssertionResult;
using testing::AssertionSuccess;
......@@ -23,7 +25,7 @@ using testing::IsNull;
using testing::Ne;
using testing::NotNull;
using testing::Pointee;
using testing::_;
using testing::Return;
namespace sync_sessions {
......@@ -64,7 +66,6 @@ class SyncedSessionTrackerTest : public testing::Test {
SyncedSessionTrackerTest() : tracker_(&sessions_client_) {}
~SyncedSessionTrackerTest() override {}
SyncedSessionTracker* GetTracker() { return &tracker_; }
TabNodePool* GetLocalTabNodePool() {
return &tracker_.LookupTrackedSession(tracker_.local_session_tag_)
->tab_node_pool;
......@@ -140,133 +141,130 @@ class SyncedSessionTrackerTest : public testing::Test {
<< unmapped_tab_count;
}
MockSyncSessionsClient* GetSyncSessionsClient() { return &sessions_client_; }
private:
protected:
testing::NiceMock<MockSyncSessionsClient> sessions_client_;
testing::NiceMock<
base::MockCallback<base::RepeatingCallback<bool(int /*tab_node_id*/)>>>
is_tab_node_unsynced_cb_;
SyncedSessionTracker tracker_;
};
TEST_F(SyncedSessionTrackerTest, GetSession) {
SyncedSession* session1 = GetTracker()->GetSession(kTag);
SyncedSession* session2 = GetTracker()->GetSession(kTag2);
ASSERT_EQ(session1, GetTracker()->LookupSession(kTag));
ASSERT_EQ(session1, GetTracker()->GetSession(kTag));
SyncedSession* session1 = tracker_.GetSession(kTag);
SyncedSession* session2 = tracker_.GetSession(kTag2);
ASSERT_EQ(session1, tracker_.LookupSession(kTag));
ASSERT_EQ(session1, tracker_.GetSession(kTag));
ASSERT_NE(session1, session2);
// Should clean up memory on its own.
}
TEST_F(SyncedSessionTrackerTest, GetTabUnmapped) {
sessions::SessionTab* tab = GetTracker()->GetTab(kTag, kTab1);
ASSERT_EQ(tab, GetTracker()->GetTab(kTag, kTab1));
sessions::SessionTab* tab = tracker_.GetTab(kTag, kTab1);
ASSERT_EQ(tab, tracker_.GetTab(kTag, kTab1));
// Should clean up memory on its own.
}
TEST_F(SyncedSessionTrackerTest, PutWindowInSession) {
GetTracker()->PutWindowInSession(kTag, kWindow1);
const SyncedSession* session = GetTracker()->LookupSession(kTag);
tracker_.PutWindowInSession(kTag, kWindow1);
const SyncedSession* session = tracker_.LookupSession(kTag);
ASSERT_EQ(1U, session->windows.size());
// Doing it again should have no effect.
GetTracker()->PutWindowInSession(kTag, kWindow1);
tracker_.PutWindowInSession(kTag, kWindow1);
ASSERT_EQ(1U, session->windows.size());
// Should clean up memory on its own.
}
TEST_F(SyncedSessionTrackerTest, PutTabInWindow) {
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
const SyncedSession* session = GetTracker()->LookupSession(kTag);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
const SyncedSession* session = tracker_.LookupSession(kTag);
ASSERT_EQ(1U, session->windows.size());
ASSERT_EQ(1U, session->windows.at(kWindow1)->wrapped_window.tabs.size());
ASSERT_EQ(GetTracker()->GetTab(kTag, kTab1),
ASSERT_EQ(tracker_.GetTab(kTag, kTab1),
session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
// Should clean up memory on its own.
}
TEST_F(SyncedSessionTrackerTest, LookupAllSessions) {
EXPECT_THAT(
GetTracker()->LookupAllSessions(SyncedSessionTracker::PRESENTABLE),
IsEmpty());
EXPECT_THAT(tracker_.LookupAllSessions(SyncedSessionTracker::PRESENTABLE),
IsEmpty());
GetTracker()->InitLocalSession(kTag, kSessionName, kDeviceType);
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
EXPECT_THAT(GetTracker()->LookupAllSessions(SyncedSessionTracker::RAW),
EXPECT_THAT(tracker_.LookupAllSessions(SyncedSessionTracker::RAW),
ElementsAre(HasSessionTag(kTag)));
EXPECT_THAT(
GetTracker()->LookupAllSessions(SyncedSessionTracker::PRESENTABLE),
IsEmpty());
EXPECT_THAT(tracker_.LookupAllSessions(SyncedSessionTracker::PRESENTABLE),
IsEmpty());
sessions::SessionTab* tab = GetTracker()->GetTab(kTag, kTab1);
sessions::SessionTab* tab = tracker_.GetTab(kTag, kTab1);
ASSERT_TRUE(tab);
tab->navigations.push_back(
sessions::SerializedNavigationEntryTestHelper::CreateNavigation(kValidUrl,
kTitle));
EXPECT_THAT(
GetTracker()->LookupAllSessions(SyncedSessionTracker::PRESENTABLE),
ElementsAre(HasSessionTag(kTag)));
EXPECT_THAT(tracker_.LookupAllSessions(SyncedSessionTracker::PRESENTABLE),
ElementsAre(HasSessionTag(kTag)));
GetTracker()->GetSession(kTag2);
GetTracker()->PutWindowInSession(kTag2, kWindow1);
GetTracker()->PutTabInWindow(kTag2, kWindow1, kTab2);
tracker_.GetSession(kTag2);
tracker_.PutWindowInSession(kTag2, kWindow1);
tracker_.PutTabInWindow(kTag2, kWindow1, kTab2);
sessions::SessionTab* tab2 = GetTracker()->GetTab(kTag2, kTab2);
sessions::SessionTab* tab2 = tracker_.GetTab(kTag2, kTab2);
ASSERT_TRUE(tab2);
tab2->navigations.push_back(
sessions::SerializedNavigationEntryTestHelper::CreateNavigation(kValidUrl,
kTitle));
EXPECT_THAT(
GetTracker()->LookupAllSessions(SyncedSessionTracker::PRESENTABLE),
ElementsAre(HasSessionTag(kTag), HasSessionTag(kTag2)));
EXPECT_THAT(tracker_.LookupAllSessions(SyncedSessionTracker::PRESENTABLE),
ElementsAre(HasSessionTag(kTag), HasSessionTag(kTag2)));
}
TEST_F(SyncedSessionTrackerTest, LookupAllForeignSessions) {
const char kInvalidUrl[] = "invalid.url";
ON_CALL(*GetSyncSessionsClient(), ShouldSyncURL(GURL(kInvalidUrl)))
ON_CALL(sessions_client_, ShouldSyncURL(GURL(kInvalidUrl)))
.WillByDefault(testing::Return(false));
EXPECT_THAT(
GetTracker()->LookupAllForeignSessions(SyncedSessionTracker::PRESENTABLE),
tracker_.LookupAllForeignSessions(SyncedSessionTracker::PRESENTABLE),
IsEmpty());
GetTracker()->GetSession(kTag);
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
sessions::SessionTab* tab = GetTracker()->GetTab(kTag, kTab1);
tracker_.GetSession(kTag);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
sessions::SessionTab* tab = tracker_.GetTab(kTag, kTab1);
ASSERT_TRUE(tab);
tab->navigations.push_back(
sessions::SerializedNavigationEntryTestHelper::CreateNavigation(kValidUrl,
kTitle));
GetTracker()->GetSession(kTag2);
GetTracker()->GetSession(kTag3);
GetTracker()->PutWindowInSession(kTag3, kWindow1);
GetTracker()->PutTabInWindow(kTag3, kWindow1, kTab1);
tab = GetTracker()->GetTab(kTag3, kTab1);
tracker_.GetSession(kTag2);
tracker_.GetSession(kTag3);
tracker_.PutWindowInSession(kTag3, kWindow1);
tracker_.PutTabInWindow(kTag3, kWindow1, kTab1);
tab = tracker_.GetTab(kTag3, kTab1);
ASSERT_TRUE(tab);
tab->navigations.push_back(
sessions::SerializedNavigationEntryTestHelper::CreateNavigation(
kInvalidUrl, kTitle));
// Only the session with a valid window and tab gets returned.
EXPECT_THAT(
GetTracker()->LookupAllForeignSessions(SyncedSessionTracker::PRESENTABLE),
tracker_.LookupAllForeignSessions(SyncedSessionTracker::PRESENTABLE),
ElementsAre(HasSessionTag(kTag)));
EXPECT_THAT(GetTracker()->LookupAllForeignSessions(SyncedSessionTracker::RAW),
EXPECT_THAT(tracker_.LookupAllForeignSessions(SyncedSessionTracker::RAW),
ElementsAre(HasSessionTag(kTag), HasSessionTag(kTag2),
HasSessionTag(kTag3)));
}
TEST_F(SyncedSessionTrackerTest, LookupSessionWindows) {
std::vector<const sessions::SessionWindow*> windows;
ASSERT_FALSE(GetTracker()->LookupSessionWindows(kTag, &windows));
GetTracker()->GetSession(kTag);
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutWindowInSession(kTag, kWindow2);
GetTracker()->GetSession(kTag2);
GetTracker()->PutWindowInSession(kTag2, kWindow1);
GetTracker()->PutWindowInSession(kTag2, kWindow2);
ASSERT_TRUE(GetTracker()->LookupSessionWindows(kTag, &windows));
ASSERT_FALSE(tracker_.LookupSessionWindows(kTag, &windows));
tracker_.GetSession(kTag);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutWindowInSession(kTag, kWindow2);
tracker_.GetSession(kTag2);
tracker_.PutWindowInSession(kTag2, kWindow1);
tracker_.PutWindowInSession(kTag2, kWindow2);
ASSERT_TRUE(tracker_.LookupSessionWindows(kTag, &windows));
ASSERT_EQ(2U, windows.size()); // Only windows from kTag session.
ASSERT_NE((sessions::SessionWindow*)nullptr, windows[0]);
ASSERT_NE((sessions::SessionWindow*)nullptr, windows[1]);
......@@ -274,81 +272,81 @@ TEST_F(SyncedSessionTrackerTest, LookupSessionWindows) {
}
TEST_F(SyncedSessionTrackerTest, LookupSessionTab) {
ASSERT_THAT(GetTracker()->LookupSessionTab(kTag, SessionID::InvalidValue()),
ASSERT_THAT(tracker_.LookupSessionTab(kTag, SessionID::InvalidValue()),
IsNull());
ASSERT_THAT(GetTracker()->LookupSessionTab(kTag, kTab1), IsNull());
GetTracker()->GetSession(kTag);
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
ASSERT_THAT(GetTracker()->LookupSessionTab(kTag, kTab1), NotNull());
ASSERT_THAT(tracker_.LookupSessionTab(kTag, kTab1), IsNull());
tracker_.GetSession(kTag);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
ASSERT_THAT(tracker_.LookupSessionTab(kTag, kTab1), NotNull());
}
TEST_F(SyncedSessionTrackerTest, Complex) {
std::vector<sessions::SessionTab *> tabs1, tabs2;
sessions::SessionTab* temp_tab;
ASSERT_TRUE(GetTracker()->Empty());
ASSERT_EQ(0U, GetTracker()->num_synced_sessions());
ASSERT_EQ(0U, GetTracker()->num_synced_tabs(kTag));
tabs1.push_back(GetTracker()->GetTab(kTag, kTab1));
tabs1.push_back(GetTracker()->GetTab(kTag, kTab2));
tabs1.push_back(GetTracker()->GetTab(kTag, kTab3));
ASSERT_EQ(3U, GetTracker()->num_synced_tabs(kTag));
ASSERT_EQ(1U, GetTracker()->num_synced_sessions());
temp_tab = GetTracker()->GetTab(kTag, kTab1); // Already created.
ASSERT_EQ(3U, GetTracker()->num_synced_tabs(kTag));
ASSERT_EQ(1U, GetTracker()->num_synced_sessions());
ASSERT_TRUE(tracker_.Empty());
ASSERT_EQ(0U, tracker_.num_synced_sessions());
ASSERT_EQ(0U, tracker_.num_synced_tabs(kTag));
tabs1.push_back(tracker_.GetTab(kTag, kTab1));
tabs1.push_back(tracker_.GetTab(kTag, kTab2));
tabs1.push_back(tracker_.GetTab(kTag, kTab3));
ASSERT_EQ(3U, tracker_.num_synced_tabs(kTag));
ASSERT_EQ(1U, tracker_.num_synced_sessions());
temp_tab = tracker_.GetTab(kTag, kTab1); // Already created.
ASSERT_EQ(3U, tracker_.num_synced_tabs(kTag));
ASSERT_EQ(1U, tracker_.num_synced_sessions());
ASSERT_EQ(tabs1[0], temp_tab);
tabs2.push_back(GetTracker()->GetTab(kTag2, kTab1));
ASSERT_EQ(1U, GetTracker()->num_synced_tabs(kTag2));
ASSERT_EQ(2U, GetTracker()->num_synced_sessions());
ASSERT_FALSE(GetTracker()->DeleteForeignSession(kTag3));
SyncedSession* session = GetTracker()->GetSession(kTag);
ASSERT_EQ(2U, GetTracker()->num_synced_sessions());
SyncedSession* session2 = GetTracker()->GetSession(kTag2);
ASSERT_EQ(2U, GetTracker()->num_synced_sessions());
SyncedSession* session3 = GetTracker()->GetSession(kTag3);
tabs2.push_back(tracker_.GetTab(kTag2, kTab1));
ASSERT_EQ(1U, tracker_.num_synced_tabs(kTag2));
ASSERT_EQ(2U, tracker_.num_synced_sessions());
ASSERT_FALSE(tracker_.DeleteForeignSession(kTag3));
SyncedSession* session = tracker_.GetSession(kTag);
ASSERT_EQ(2U, tracker_.num_synced_sessions());
SyncedSession* session2 = tracker_.GetSession(kTag2);
ASSERT_EQ(2U, tracker_.num_synced_sessions());
SyncedSession* session3 = tracker_.GetSession(kTag3);
session3->device_type = sync_pb::SyncEnums_DeviceType_TYPE_LINUX;
ASSERT_EQ(3U, GetTracker()->num_synced_sessions());
ASSERT_EQ(3U, tracker_.num_synced_sessions());
ASSERT_TRUE(session);
ASSERT_TRUE(session2);
ASSERT_TRUE(session3);
ASSERT_NE(session, session2);
ASSERT_NE(session2, session3);
ASSERT_TRUE(GetTracker()->DeleteForeignSession(kTag3));
ASSERT_EQ(2U, GetTracker()->num_synced_sessions());
ASSERT_TRUE(tracker_.DeleteForeignSession(kTag3));
ASSERT_EQ(2U, tracker_.num_synced_sessions());
GetTracker()->PutWindowInSession(kTag, kWindow1); // Create a window.
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab3); // No longer unmapped.
ASSERT_EQ(3U, GetTracker()->num_synced_tabs(kTag)); // Has not changed.
tracker_.PutWindowInSession(kTag, kWindow1); // Create a window.
tracker_.PutTabInWindow(kTag, kWindow1, kTab3); // No longer unmapped.
ASSERT_EQ(3U, tracker_.num_synced_tabs(kTag)); // Has not changed.
ASSERT_EQ(tabs1[0], GetTracker()->LookupSessionTab(kTag, kTab1));
ASSERT_EQ(tabs1[2], GetTracker()->LookupSessionTab(kTag, kTab3));
ASSERT_THAT(GetTracker()->LookupSessionTab(kTag, kTab4), IsNull());
ASSERT_EQ(tabs1[0], tracker_.LookupSessionTab(kTag, kTab1));
ASSERT_EQ(tabs1[2], tracker_.LookupSessionTab(kTag, kTab3));
ASSERT_THAT(tracker_.LookupSessionTab(kTag, kTab4), IsNull());
std::vector<const sessions::SessionWindow*> windows;
ASSERT_TRUE(GetTracker()->LookupSessionWindows(kTag, &windows));
ASSERT_TRUE(tracker_.LookupSessionWindows(kTag, &windows));
ASSERT_EQ(1U, windows.size());
ASSERT_TRUE(GetTracker()->LookupSessionWindows(kTag2, &windows));
ASSERT_TRUE(tracker_.LookupSessionWindows(kTag2, &windows));
ASSERT_EQ(0U, windows.size());
// The sessions don't have valid tabs, lookup should not succeed.
std::vector<const SyncedSession*> sessions;
EXPECT_THAT(
GetTracker()->LookupAllForeignSessions(SyncedSessionTracker::PRESENTABLE),
tracker_.LookupAllForeignSessions(SyncedSessionTracker::PRESENTABLE),
IsEmpty());
EXPECT_THAT(GetTracker()->LookupAllForeignSessions(SyncedSessionTracker::RAW),
EXPECT_THAT(tracker_.LookupAllForeignSessions(SyncedSessionTracker::RAW),
ElementsAre(HasSessionTag(kTag), HasSessionTag(kTag2)));
GetTracker()->Clear();
ASSERT_EQ(0U, GetTracker()->num_synced_tabs(kTag));
ASSERT_EQ(0U, GetTracker()->num_synced_tabs(kTag2));
ASSERT_EQ(0U, GetTracker()->num_synced_sessions());
tracker_.Clear();
ASSERT_EQ(0U, tracker_.num_synced_tabs(kTag));
ASSERT_EQ(0U, tracker_.num_synced_tabs(kTag2));
ASSERT_EQ(0U, tracker_.num_synced_sessions());
}
TEST_F(SyncedSessionTrackerTest, ManyGetTabs) {
ASSERT_TRUE(GetTracker()->Empty());
ASSERT_TRUE(tracker_.Empty());
const int kMaxSessions = 10;
const int kMaxTabs = 1000;
const int kMaxAttempts = 10000;
......@@ -358,7 +356,7 @@ TEST_F(SyncedSessionTrackerTest, ManyGetTabs) {
// More attempts than tabs means we'll sometimes get the same tabs,
// sometimes have to allocate new tabs.
int rand_tab_num = base::RandInt(0, kMaxTabs);
sessions::SessionTab* tab = GetTracker()->GetTab(
sessions::SessionTab* tab = tracker_.GetTab(
tag, SessionID::FromSerializedValue(rand_tab_num + 1));
ASSERT_TRUE(tab);
}
......@@ -366,83 +364,83 @@ TEST_F(SyncedSessionTrackerTest, ManyGetTabs) {
}
TEST_F(SyncedSessionTrackerTest, LookupTabNodeIds) {
GetTracker()->OnTabNodeSeen(kTag, 1, kTab1);
GetTracker()->OnTabNodeSeen(kTag, 2, kTab2);
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag), ElementsAre(1, 2));
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag2), IsEmpty());
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag), ElementsAre(1, 2));
GetTracker()->OnTabNodeSeen(kTag, 3, kTab3);
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag), ElementsAre(1, 2, 3));
GetTracker()->OnTabNodeSeen(kTag2, 21, kTab4);
GetTracker()->OnTabNodeSeen(kTag2, 22, kTab5);
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag2), ElementsAre(21, 22));
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag), ElementsAre(1, 2, 3));
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag3), IsEmpty());
GetTracker()->PutWindowInSession(kTag3, kWindow2);
GetTracker()->PutTabInWindow(kTag3, kWindow2, kTab2);
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag3), IsEmpty());
EXPECT_FALSE(GetTracker()->DeleteForeignSession(kTag3));
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag3), IsEmpty());
EXPECT_FALSE(GetTracker()->DeleteForeignSession(kTag));
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag), IsEmpty());
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag2), ElementsAre(21, 22));
GetTracker()->OnTabNodeSeen(kTag2, 21, kTab6);
GetTracker()->OnTabNodeSeen(kTag2, 23, kTab7);
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag2), ElementsAre(21, 22, 23));
EXPECT_FALSE(GetTracker()->DeleteForeignSession(kTag2));
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag2), IsEmpty());
tracker_.OnTabNodeSeen(kTag, 1, kTab1);
tracker_.OnTabNodeSeen(kTag, 2, kTab2);
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag), ElementsAre(1, 2));
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag2), IsEmpty());
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag), ElementsAre(1, 2));
tracker_.OnTabNodeSeen(kTag, 3, kTab3);
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag), ElementsAre(1, 2, 3));
tracker_.OnTabNodeSeen(kTag2, 21, kTab4);
tracker_.OnTabNodeSeen(kTag2, 22, kTab5);
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag2), ElementsAre(21, 22));
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag), ElementsAre(1, 2, 3));
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag3), IsEmpty());
tracker_.PutWindowInSession(kTag3, kWindow2);
tracker_.PutTabInWindow(kTag3, kWindow2, kTab2);
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag3), IsEmpty());
EXPECT_FALSE(tracker_.DeleteForeignSession(kTag3));
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag3), IsEmpty());
EXPECT_FALSE(tracker_.DeleteForeignSession(kTag));
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag), IsEmpty());
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag2), ElementsAre(21, 22));
tracker_.OnTabNodeSeen(kTag2, 21, kTab6);
tracker_.OnTabNodeSeen(kTag2, 23, kTab7);
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag2), ElementsAre(21, 22, 23));
EXPECT_FALSE(tracker_.DeleteForeignSession(kTag2));
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag2), IsEmpty());
}
TEST_F(SyncedSessionTrackerTest, LookupUnmappedTabs) {
EXPECT_THAT(GetTracker()->LookupUnmappedTabs(kTag), IsEmpty());
EXPECT_THAT(tracker_.LookupUnmappedTabs(kTag), IsEmpty());
sessions::SessionTab* tab = GetTracker()->GetTab(kTag, kTab1);
sessions::SessionTab* tab = tracker_.GetTab(kTag, kTab1);
ASSERT_THAT(tab, NotNull());
EXPECT_THAT(GetTracker()->LookupUnmappedTabs(kTag), ElementsAre(tab));
EXPECT_THAT(GetTracker()->LookupUnmappedTabs(kTag2), IsEmpty());
EXPECT_THAT(tracker_.LookupUnmappedTabs(kTag), ElementsAre(tab));
EXPECT_THAT(tracker_.LookupUnmappedTabs(kTag2), IsEmpty());
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
EXPECT_THAT(GetTracker()->LookupUnmappedTabs(kTag), IsEmpty());
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
EXPECT_THAT(tracker_.LookupUnmappedTabs(kTag), IsEmpty());
}
TEST_F(SyncedSessionTrackerTest, SessionTracking) {
ASSERT_TRUE(GetTracker()->Empty());
ASSERT_TRUE(tracker_.Empty());
// Create some session information that is stale.
SyncedSession* session1 = GetTracker()->GetSession(kTag);
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab2);
GetTracker()->GetTab(kTag, kTab3)->window_id =
SyncedSession* session1 = tracker_.GetSession(kTag);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab2);
tracker_.GetTab(kTag, kTab3)->window_id =
SessionID::FromSerializedValue(1); // Unmapped.
GetTracker()->GetTab(kTag, kTab4)->window_id =
tracker_.GetTab(kTag, kTab4)->window_id =
SessionID::FromSerializedValue(1); // Unmapped.
GetTracker()->PutWindowInSession(kTag, kWindow2);
GetTracker()->PutTabInWindow(kTag, kWindow2, kTab5);
GetTracker()->PutTabInWindow(kTag, kWindow2, kTab6);
tracker_.PutWindowInSession(kTag, kWindow2);
tracker_.PutTabInWindow(kTag, kWindow2, kTab5);
tracker_.PutTabInWindow(kTag, kWindow2, kTab6);
ASSERT_EQ(2U, session1->windows.size());
ASSERT_EQ(2U, session1->windows[kWindow1]->wrapped_window.tabs.size());
ASSERT_EQ(2U, session1->windows[kWindow2]->wrapped_window.tabs.size());
ASSERT_EQ(6U, GetTracker()->num_synced_tabs(kTag));
ASSERT_EQ(6U, tracker_.num_synced_tabs(kTag));
// Create a session that should not be affected.
SyncedSession* session2 = GetTracker()->GetSession(kTag2);
GetTracker()->PutWindowInSession(kTag2, kWindow3);
GetTracker()->PutTabInWindow(kTag2, kWindow3, kTab2);
SyncedSession* session2 = tracker_.GetSession(kTag2);
tracker_.PutWindowInSession(kTag2, kWindow3);
tracker_.PutTabInWindow(kTag2, kWindow3, kTab2);
ASSERT_EQ(1U, session2->windows.size());
ASSERT_EQ(1U, session2->windows[kWindow3]->wrapped_window.tabs.size());
ASSERT_EQ(1U, GetTracker()->num_synced_tabs(kTag2));
ASSERT_EQ(1U, tracker_.num_synced_tabs(kTag2));
// Reset tracking and get the current windows/tabs.
// We simulate moving a tab from one window to another, then closing the
......@@ -450,27 +448,27 @@ TEST_F(SyncedSessionTrackerTest, SessionTracking) {
// on the remaining window.
// New tab, arrived before meta node so unmapped.
GetTracker()->GetTab(kTag, kTab7);
GetTracker()->ResetSessionTracking(kTag);
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
tracker_.GetTab(kTag, kTab7);
tracker_.ResetSessionTracking(kTag);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
// Tab 1 is closed.
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab3); // No longer unmapped.
tracker_.PutTabInWindow(kTag, kWindow1, kTab3); // No longer unmapped.
// Tab 3 was unmapped and does not get used.
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab5); // Moved from window 1.
tracker_.PutTabInWindow(kTag, kWindow1, kTab5); // Moved from window 1.
// Window 1 was closed, along with tab 5.
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab7); // No longer unmapped.
tracker_.PutTabInWindow(kTag, kWindow1, kTab7); // No longer unmapped.
// Session 2 should not be affected.
GetTracker()->CleanupSession(kTag);
tracker_.CleanupSession(kTag);
// Verify that only those parts of the session not owned have been removed.
ASSERT_EQ(1U, session1->windows.size());
ASSERT_EQ(4U, session1->windows[kWindow1]->wrapped_window.tabs.size());
ASSERT_EQ(1U, session2->windows.size());
ASSERT_EQ(1U, session2->windows[kWindow3]->wrapped_window.tabs.size());
ASSERT_EQ(2U, GetTracker()->num_synced_sessions());
ASSERT_EQ(4U, GetTracker()->num_synced_tabs(kTag));
ASSERT_EQ(1U, GetTracker()->num_synced_tabs(kTag2));
ASSERT_EQ(2U, tracker_.num_synced_sessions());
ASSERT_EQ(4U, tracker_.num_synced_tabs(kTag));
ASSERT_EQ(1U, tracker_.num_synced_tabs(kTag2));
ASSERT_TRUE(VerifyTabIntegrity(kTag));
// All memory should be properly deallocated by destructor for the
......@@ -482,349 +480,387 @@ TEST_F(SyncedSessionTrackerTest, DeleteForeignTab) {
int tab_node_id_2 = 2;
std::set<int> result;
GetTracker()->OnTabNodeSeen(kTag, tab_node_id_1, kTab1);
GetTracker()->OnTabNodeSeen(kTag, tab_node_id_2, kTab2);
tracker_.OnTabNodeSeen(kTag, tab_node_id_1, kTab1);
tracker_.OnTabNodeSeen(kTag, tab_node_id_2, kTab2);
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag),
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag),
ElementsAre(tab_node_id_1, tab_node_id_2));
GetTracker()->DeleteForeignTab(kTag, tab_node_id_1);
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag), ElementsAre(tab_node_id_2));
tracker_.DeleteForeignTab(kTag, tab_node_id_1);
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag), ElementsAre(tab_node_id_2));
GetTracker()->DeleteForeignTab(kTag, tab_node_id_2);
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag), IsEmpty());
tracker_.DeleteForeignTab(kTag, tab_node_id_2);
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag), IsEmpty());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
TEST_F(SyncedSessionTrackerTest, CleanupLocalTabs) {
std::set<int> free_node_ids;
GetTracker()->InitLocalSession(kTag, kSessionName, kDeviceType);
tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
// Start with two restored tab nodes.
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
GetTracker()->ReassociateLocalTab(kTabNode2, kTab2);
GetTracker()->CleanupLocalTabs(&free_node_ids);
EXPECT_TRUE(free_node_ids.empty());
tracker_.ReassociateLocalTab(kTabNode1, kTab1);
tracker_.ReassociateLocalTab(kTabNode2, kTab2);
EXPECT_TRUE(
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
// Associate with no tabs. The tab pool should now be full.
GetTracker()->ResetSessionTracking(kTag);
GetTracker()->CleanupLocalTabs(&free_node_ids);
EXPECT_TRUE(free_node_ids.empty());
tracker_.ResetSessionTracking(kTag);
EXPECT_TRUE(
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
// Associate with only 1 tab open. A tab node should be reused.
GetTracker()->ResetSessionTracking(kTag);
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
EXPECT_EQ(kTabNode1, GetTracker()->AssociateLocalTabWithFreeTabNode(kTab1));
GetTracker()->CleanupLocalTabs(&free_node_ids);
EXPECT_TRUE(free_node_ids.empty());
tracker_.ResetSessionTracking(kTag);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
EXPECT_EQ(kTabNode1, tracker_.AssociateLocalTabWithFreeTabNode(kTab1));
EXPECT_TRUE(
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
// Simulate a tab opening, which should use the last free tab node.
EXPECT_EQ(kTabNode2, GetTracker()->AssociateLocalTabWithFreeTabNode(kTab2));
EXPECT_EQ(kTabNode2, GetTracker()->LookupTabNodeFromTabId(kTag, kTab2));
EXPECT_EQ(kTabNode2, tracker_.AssociateLocalTabWithFreeTabNode(kTab2));
EXPECT_EQ(kTabNode2, tracker_.LookupTabNodeFromTabId(kTag, kTab2));
// Simulate another tab opening, which should create a new associated tab
// node.
EXPECT_EQ(kTabNode3, GetTracker()->AssociateLocalTabWithFreeTabNode(kTab3));
EXPECT_EQ(kTabNode3, GetTracker()->LookupTabNodeFromTabId(kTag, kTab3));
EXPECT_EQ(kTabNode3, tracker_.AssociateLocalTabWithFreeTabNode(kTab3));
EXPECT_EQ(kTabNode3, tracker_.LookupTabNodeFromTabId(kTag, kTab3));
// Previous tabs should still be associated.
EXPECT_EQ(kTabNode1, GetTracker()->LookupTabNodeFromTabId(kTag, kTab1));
EXPECT_EQ(kTabNode2, GetTracker()->LookupTabNodeFromTabId(kTag, kTab2));
EXPECT_EQ(kTabNode1, tracker_.LookupTabNodeFromTabId(kTag, kTab1));
EXPECT_EQ(kTabNode2, tracker_.LookupTabNodeFromTabId(kTag, kTab2));
// Associate with no tabs. All tabs should be freed again, and the pool
// should now be full.
GetTracker()->ResetSessionTracking(kTag);
GetTracker()->CleanupLocalTabs(&free_node_ids);
EXPECT_TRUE(free_node_ids.empty());
tracker_.ResetSessionTracking(kTag);
EXPECT_TRUE(
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
TEST_F(SyncedSessionTrackerTest, ReassociateTabMapped) {
std::set<int> free_node_ids;
TEST_F(SyncedSessionTrackerTest, CleanupLocalTabsWithDeferredRecycling) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(kDeferRecyclingOfSyncTabNodesIfUnsynced);
tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
// Start with four restored tab nodes, one of which is mapped (|kTab1|).
tracker_.ReassociateLocalTab(kTabNode1, kTab1);
tracker_.ReassociateLocalTab(kTabNode2, kTab2);
tracker_.ReassociateLocalTab(kTabNode3, kTab3);
tracker_.ReassociateLocalTab(kTabNode4, kTab4);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
// Modification times are very recent except for one tab, |kTab3|, which is
// old, and hence is expected to be recycled immediately, even if it's
// unsynced.
tracker_.GetTab(kTag, kTab1)->timestamp = base::Time::Now();
tracker_.GetTab(kTag, kTab2)->timestamp = base::Time::Now();
tracker_.GetTab(kTag, kTab3)->timestamp =
base::Time::Now() - base::TimeDelta::FromDays(100);
tracker_.GetTab(kTag, kTab4)->timestamp = base::Time::Now();
// Among the unmapped (closed) ones, |kTab2| and |kTab3| are unsynced.
// However, |kTab3| is old so it's irrelevant whether it's unsynced and the
// callback shouldn't even run.
EXPECT_CALL(is_tab_node_unsynced_cb_, Run(kTabNode2)).WillOnce(Return(true));
EXPECT_CALL(is_tab_node_unsynced_cb_, Run(kTabNode3)).Times(0);
EXPECT_CALL(is_tab_node_unsynced_cb_, Run(kTabNode4)).WillOnce(Return(false));
// During cleanup, only two tabs should be freed:
// - |kTabNode3| because of its age, although it's unsynced.
// - |kTabNode4| because it's synced.
EXPECT_TRUE(
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
ASSERT_EQ(kTabNode1, tracker_.LookupTabNodeFromTabId(kTag, kTab1));
EXPECT_EQ(kTabNode2, tracker_.LookupTabNodeFromTabId(kTag, kTab2));
EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
tracker_.LookupTabNodeFromTabId(kTag, kTab3));
EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
tracker_.LookupTabNodeFromTabId(kTag, kTab4));
// |kTabNode2| now becomes synced (commit succeeded), which means it should be
// freed during cleanup.
EXPECT_CALL(is_tab_node_unsynced_cb_, Run(kTabNode2)).WillOnce(Return(false));
EXPECT_TRUE(
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
tracker_.LookupTabNodeFromTabId(kTag, kTab2));
EXPECT_EQ(kTabNode2, tracker_.AssociateLocalTabWithFreeTabNode(kTab5));
}
TEST_F(SyncedSessionTrackerTest, ReassociateTabMapped) {
// First create the tab normally.
GetTracker()->InitLocalSession(kTag, kSessionName, kDeviceType);
tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
EXPECT_FALSE(IsLocalTabNodeAssociated(kTabNode1));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
tracker_.ReassociateLocalTab(kTabNode1, kTab1);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab1));
// Map it to a window with the same tab id as it was created with.
GetTracker()->ResetSessionTracking(kTag);
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
GetTracker()->CleanupLocalTabs(&free_node_ids);
tracker_.ResetSessionTracking(kTag);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
const SyncedSession* session = GetTracker()->LookupSession(kTag);
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
const SyncedSession* session = tracker_.LookupSession(kTag);
ASSERT_EQ(1U, session->windows.size());
ASSERT_EQ(1U, session->windows.at(kWindow1)->wrapped_window.tabs.size());
ASSERT_EQ(GetTracker()->GetTab(kTag, kTab1),
ASSERT_EQ(tracker_.GetTab(kTag, kTab1),
session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
// Then reassociate with a new tab id.
GetTracker()->ReassociateLocalTab(kTabNode1, kTab2);
tracker_.ReassociateLocalTab(kTabNode1, kTab2);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
// Reset tracking, and put the new tab id into the window.
GetTracker()->ResetSessionTracking(kTag);
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab2));
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab2);
GetTracker()->CleanupLocalTabs(&free_node_ids);
tracker_.ResetSessionTracking(kTag);
EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab2));
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab2);
EXPECT_TRUE(
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(free_node_ids.empty());
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
// Now that it's been mapped, it should be accessible both via the
// GetSession as well as the GetTab.
ASSERT_EQ(GetTracker()->GetTab(kTag, kTab2),
ASSERT_EQ(tracker_.GetTab(kTag, kTab2),
session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
ASSERT_EQ(GetTracker()->LookupTabNodeIds(kTag).size(),
GetTracker()->LookupTabNodeIds(kTag).count(kTabNode1));
ASSERT_EQ(tracker_.LookupTabNodeIds(kTag).size(),
tracker_.LookupTabNodeIds(kTag).count(kTabNode1));
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
TEST_F(SyncedSessionTrackerTest, ReassociateTabMappedTwice) {
std::set<int> free_node_ids;
// First create the tab normally.
GetTracker()->InitLocalSession(kTag, kSessionName, kDeviceType);
tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
EXPECT_FALSE(IsLocalTabNodeAssociated(kTabNode1));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
tracker_.ReassociateLocalTab(kTabNode1, kTab1);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab1));
// Map it to a window with the same tab id as it was created with.
GetTracker()->ResetSessionTracking(kTag);
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
GetTracker()->CleanupLocalTabs(&free_node_ids);
tracker_.ResetSessionTracking(kTag);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
EXPECT_TRUE(
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(free_node_ids.empty());
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
const SyncedSession* session = GetTracker()->LookupSession(kTag);
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
const SyncedSession* session = tracker_.LookupSession(kTag);
ASSERT_EQ(1U, session->windows.size());
ASSERT_EQ(1U, session->windows.at(kWindow1)->wrapped_window.tabs.size());
EXPECT_EQ(GetTracker()->GetTab(kTag, kTab1),
EXPECT_EQ(tracker_.GetTab(kTag, kTab1),
session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
// Then reassociate with a new tab id.
GetTracker()->ReassociateLocalTab(kTabNode1, kTab2);
tracker_.ReassociateLocalTab(kTabNode1, kTab2);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
// Tab 1 should no longer be associated with any SessionTab object. At this
// point there's no need to verify it's unmapped state.
EXPECT_THAT(GetTracker()->LookupSessionTab(kTag, kTab1), IsNull());
EXPECT_THAT(tracker_.LookupSessionTab(kTag, kTab1), IsNull());
// Reset tracking and add back both the old tab and the new tab (both of which
// refer to the same tab node id).
GetTracker()->ResetSessionTracking(kTag);
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab2));
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab2);
GetTracker()->CleanupLocalTabs(&free_node_ids);
tracker_.ResetSessionTracking(kTag);
EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab2));
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab2);
EXPECT_TRUE(
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(free_node_ids.empty());
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
// Now that it's been mapped, it should be accessible both via the
// GetSession as well as the GetTab.
EXPECT_EQ(GetTracker()->GetTab(kTag, kTab2),
EXPECT_EQ(tracker_.GetTab(kTag, kTab2),
session->windows.at(kWindow1)->wrapped_window.tabs[1].get());
EXPECT_EQ(GetTracker()->LookupTabNodeIds(kTag).size(),
GetTracker()->LookupTabNodeIds(kTag).count(kTabNode1));
EXPECT_EQ(tracker_.LookupTabNodeIds(kTag).size(),
tracker_.LookupTabNodeIds(kTag).count(kTabNode1));
// Attempting to access the original tab will create a new SessionTab object.
EXPECT_NE(GetTracker()->GetTab(kTag, kTab1),
GetTracker()->GetTab(kTag, kTab2));
EXPECT_NE(tracker_.GetTab(kTag, kTab1), tracker_.GetTab(kTag, kTab2));
EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
GetTracker()->LookupTabNodeFromTabId(kTag, kTab1));
tracker_.LookupTabNodeFromTabId(kTag, kTab1));
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
TEST_F(SyncedSessionTrackerTest, ReassociateTabUnmapped) {
std::set<int> free_node_ids;
// First create the old tab in an unmapped state.
GetTracker()->InitLocalSession(kTag, kSessionName, kDeviceType);
tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
EXPECT_FALSE(IsLocalTabNodeAssociated(kTabNode1));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
tracker_.ReassociateLocalTab(kTabNode1, kTab1);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab1));
// Map it to a window, but reassociated with a new tab id.
GetTracker()->ResetSessionTracking(kTag);
GetTracker()->ReassociateLocalTab(kTabNode1, kTab2);
tracker_.ResetSessionTracking(kTag);
tracker_.ReassociateLocalTab(kTabNode1, kTab2);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab2));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab2);
GetTracker()->CleanupLocalTabs(&free_node_ids);
EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab2));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab2);
EXPECT_TRUE(
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(free_node_ids.empty());
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
// Now that it's been mapped, it should be accessible both via the
// GetSession as well as GetTab.
const SyncedSession* session = GetTracker()->LookupSession(kTag);
ASSERT_EQ(GetTracker()->GetTab(kTag, kTab2),
const SyncedSession* session = tracker_.LookupSession(kTag);
ASSERT_EQ(tracker_.GetTab(kTag, kTab2),
session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
ASSERT_EQ(GetTracker()->LookupTabNodeIds(kTag).size(),
GetTracker()->LookupTabNodeIds(kTag).count(kTabNode1));
ASSERT_EQ(tracker_.LookupTabNodeIds(kTag).size(),
tracker_.LookupTabNodeIds(kTag).count(kTabNode1));
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
TEST_F(SyncedSessionTrackerTest, ReassociateTabOldUnmappedNewMapped) {
std::set<int> free_node_ids;
// First create the old tab in an unmapped state.
GetTracker()->InitLocalSession(kTag, kSessionName, kDeviceType);
tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
EXPECT_FALSE(IsLocalTabNodeAssociated(kTabNode1));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
tracker_.ReassociateLocalTab(kTabNode1, kTab1);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab1));
// Map an unseen tab to a window, then reassociate the existing tab to the
// mapped tab id.
GetTracker()->ResetSessionTracking(kTag);
tracker_.ResetSessionTracking(kTag);
EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab2);
GetTracker()->CleanupLocalTabs(&free_node_ids);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab2);
EXPECT_TRUE(
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab2);
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
tracker_.ReassociateLocalTab(kTabNode1, kTab2);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(free_node_ids.empty());
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
// Now that it's been mapped, it should be accessible both via the
// GetSession as well as GetTab.
const SyncedSession* session = GetTracker()->LookupSession(kTag);
ASSERT_EQ(GetTracker()->GetTab(kTag, kTab2),
const SyncedSession* session = tracker_.LookupSession(kTag);
ASSERT_EQ(tracker_.GetTab(kTag, kTab2),
session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
ASSERT_EQ(GetTracker()->LookupTabNodeIds(kTag).size(),
GetTracker()->LookupTabNodeIds(kTag).count(kTabNode1));
ASSERT_EQ(tracker_.LookupTabNodeIds(kTag).size(),
tracker_.LookupTabNodeIds(kTag).count(kTabNode1));
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
TEST_F(SyncedSessionTrackerTest, ReassociateTabSameTabId) {
std::set<int> free_node_ids;
// First create the tab normally.
GetTracker()->InitLocalSession(kTag, kSessionName, kDeviceType);
tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
EXPECT_FALSE(IsLocalTabNodeAssociated(kTabNode1));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
tracker_.ReassociateLocalTab(kTabNode1, kTab1);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab1));
// Map it to a window.
GetTracker()->ResetSessionTracking(kTag);
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
GetTracker()->CleanupLocalTabs(&free_node_ids);
tracker_.ResetSessionTracking(kTag);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
EXPECT_TRUE(
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
const SyncedSession* session = GetTracker()->LookupSession(kTag);
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
const SyncedSession* session = tracker_.LookupSession(kTag);
ASSERT_EQ(1U, session->windows.size());
ASSERT_EQ(1U, session->windows.at(kWindow1)->wrapped_window.tabs.size());
ASSERT_EQ(GetTracker()->GetTab(kTag, kTab1),
ASSERT_EQ(tracker_.GetTab(kTag, kTab1),
session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
// Reassociate, using the same tab id.
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
tracker_.ReassociateLocalTab(kTabNode1, kTab1);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
// Reset tracking, and put the tab id back into the same window.
GetTracker()->ResetSessionTracking(kTag);
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab1));
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
GetTracker()->CleanupLocalTabs(&free_node_ids);
tracker_.ResetSessionTracking(kTag);
EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab1));
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
EXPECT_TRUE(
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(free_node_ids.empty());
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
// Now that it's been mapped, it should be accessible both via the
// GetSession as well as the GetTab.
ASSERT_EQ(GetTracker()->GetTab(kTag, kTab1),
ASSERT_EQ(tracker_.GetTab(kTag, kTab1),
session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
ASSERT_EQ(GetTracker()->LookupTabNodeIds(kTag).size(),
GetTracker()->LookupTabNodeIds(kTag).count(kTabNode1));
ASSERT_EQ(tracker_.LookupTabNodeIds(kTag).size(),
tracker_.LookupTabNodeIds(kTag).count(kTabNode1));
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
TEST_F(SyncedSessionTrackerTest, ReassociateTabOldMappedNewUnmapped) {
std::set<int> free_node_ids;
// First create an unmapped tab.
GetTracker()->InitLocalSession(kTag, kSessionName, kDeviceType);
tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
EXPECT_FALSE(IsLocalTabNodeAssociated(kTabNode1));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
tracker_.ReassociateLocalTab(kTabNode1, kTab1);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab1));
// Now, map the first one, deleting the second one.
GetTracker()->ResetSessionTracking(kTag);
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
GetTracker()->CleanupLocalTabs(&free_node_ids);
tracker_.ResetSessionTracking(kTag);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
const SyncedSession* session = GetTracker()->LookupSession(kTag);
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
const SyncedSession* session = tracker_.LookupSession(kTag);
ASSERT_EQ(1U, session->windows.size());
ASSERT_EQ(1U, session->windows.at(kWindow1)->wrapped_window.tabs.size());
ASSERT_EQ(GetTracker()->GetTab(kTag, kTab1),
ASSERT_EQ(tracker_.GetTab(kTag, kTab1),
session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
// Create a second unmapped tab.
GetTracker()->ReassociateLocalTab(kTabNode2, kTab2);
tracker_.ReassociateLocalTab(kTabNode2, kTab2);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode2));
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab2));
EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab2));
// Reassociate the second tab with node of the first tab.
GetTracker()->ReassociateLocalTab(kTabNode1, kTab2);
tracker_.ReassociateLocalTab(kTabNode1, kTab2);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
EXPECT_FALSE(IsLocalTabNodeAssociated(kTabNode2));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
// Now map the new one.
GetTracker()->ResetSessionTracking(kTag);
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab2);
GetTracker()->CleanupLocalTabs(&free_node_ids);
tracker_.ResetSessionTracking(kTag);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab2);
tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
// Now that it's been mapped, it should be accessible both via the
// GetSession as well as the GetTab.
ASSERT_EQ(GetTracker()->GetTab(kTag, kTab2),
ASSERT_EQ(tracker_.GetTab(kTag, kTab2),
session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
......@@ -835,13 +871,13 @@ TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithHeader) {
header.mutable_header()->add_window()->set_window_id(kWindow1.id());
header.mutable_header()->mutable_window(0)->add_tab(kTab1.id());
header.mutable_header()->mutable_window(0)->add_tab(kTab2.id());
UpdateTrackerWithSpecifics(header, base::Time::Now(), GetTracker());
UpdateTrackerWithSpecifics(header, base::Time::Now(), &tracker_);
EXPECT_THAT(
GetTracker()->LookupSession(kTag),
tracker_.LookupSession(kTag),
MatchesSyncedSession(kTag, {{kWindow1.id(), {kTab1.id(), kTab2.id()}}}));
EXPECT_THAT(GetTracker()->LookupSessionTab(kTag, kTab1), NotNull());
EXPECT_THAT(GetTracker()->LookupSessionTab(kTag, kTab2), NotNull());
EXPECT_THAT(tracker_.LookupSessionTab(kTag, kTab1), NotNull());
EXPECT_THAT(tracker_.LookupSessionTab(kTag, kTab2), NotNull());
}
TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithIdenticalHeader) {
......@@ -850,18 +886,18 @@ TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithIdenticalHeader) {
header.mutable_header()->add_window()->set_window_id(kWindow1.id());
header.mutable_header()->mutable_window(0)->add_tab(kTab1.id());
header.mutable_header()->mutable_window(0)->add_tab(kTab2.id());
UpdateTrackerWithSpecifics(header, base::Time::Now(), GetTracker());
UpdateTrackerWithSpecifics(header, base::Time::Now(), &tracker_);
ASSERT_THAT(
GetTracker()->LookupSession(kTag),
tracker_.LookupSession(kTag),
MatchesSyncedSession(kTag, {{kWindow1.id(), {kTab1.id(), kTab2.id()}}}));
// Update with an exact header entity.
UpdateTrackerWithSpecifics(header, base::Time::Now(), GetTracker());
UpdateTrackerWithSpecifics(header, base::Time::Now(), &tracker_);
EXPECT_THAT(
GetTracker()->LookupSession(kTag),
tracker_.LookupSession(kTag),
MatchesSyncedSession(kTag, {{kWindow1.id(), {kTab1.id(), kTab2.id()}}}));
EXPECT_THAT(GetTracker()->LookupSessionTab(kTag, kTab1), NotNull());
EXPECT_THAT(tracker_.LookupSessionTab(kTag, kTab1), NotNull());
}
// Verifies that an invalid header (with duplicated tab IDs) is discarded.
......@@ -871,9 +907,9 @@ TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithHeaderWithDuplicateTabIds) {
header.mutable_header()->add_window()->set_window_id(kWindow1.id());
header.mutable_header()->mutable_window(0)->add_tab(kTab1.id());
header.mutable_header()->mutable_window(0)->add_tab(kTab1.id());
UpdateTrackerWithSpecifics(header, base::Time::Now(), GetTracker());
UpdateTrackerWithSpecifics(header, base::Time::Now(), &tracker_);
EXPECT_THAT(GetTracker()->LookupSession(kTag),
EXPECT_THAT(tracker_.LookupSession(kTag),
MatchesSyncedSession(kTag, /*window_id_to_tabs=*/{}));
}
......@@ -883,9 +919,9 @@ TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithInvalidTab) {
sync_pb::SessionSpecifics tab;
tab.set_session_tag(kTag);
tab.mutable_tab()->set_tab_id(kInvalidTabId);
UpdateTrackerWithSpecifics(tab, base::Time::Now(), GetTracker());
UpdateTrackerWithSpecifics(tab, base::Time::Now(), &tracker_);
EXPECT_THAT(GetTracker()->LookupSessionTab(
EXPECT_THAT(tracker_.LookupSessionTab(
kTag, SessionID::FromSerializedValue(kInvalidTabId)),
IsNull());
}
......@@ -896,26 +932,26 @@ TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithTab) {
tab.set_tab_node_id(kTabNode1);
tab.mutable_tab()->set_window_id(kWindow1.id());
tab.mutable_tab()->set_tab_id(kTab1.id());
UpdateTrackerWithSpecifics(tab, base::Time::Now(), GetTracker());
UpdateTrackerWithSpecifics(tab, base::Time::Now(), &tracker_);
const sessions::SessionTab* tracked_tab =
GetTracker()->LookupSessionTab(kTag, kTab1);
tracker_.LookupSessionTab(kTag, kTab1);
ASSERT_THAT(tracked_tab, NotNull());
EXPECT_EQ(kWindow1, tracked_tab->window_id);
EXPECT_EQ(false, tracked_tab->pinned);
EXPECT_THAT(GetTracker()->LookupSession(kTag),
EXPECT_THAT(tracker_.LookupSession(kTag),
MatchesSyncedSession(kTag, /*window_id_to_tabs*/ {}));
// Overwrite some attribute.
tab.mutable_tab()->set_pinned(true);
UpdateTrackerWithSpecifics(tab, base::Time::Now(), GetTracker());
tracked_tab = GetTracker()->LookupSessionTab(kTag, kTab1);
UpdateTrackerWithSpecifics(tab, base::Time::Now(), &tracker_);
tracked_tab = tracker_.LookupSessionTab(kTag, kTab1);
ASSERT_THAT(tracked_tab, NotNull());
EXPECT_EQ(kWindow1, tracked_tab->window_id);
EXPECT_EQ(true, tracked_tab->pinned);
EXPECT_THAT(GetTracker()->LookupSession(kTag),
EXPECT_THAT(tracker_.LookupSession(kTag),
MatchesSyncedSession(kTag, /*window_id_to_tabs*/ {}));
}
......@@ -925,23 +961,23 @@ TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithTabThenHeader) {
tab.set_tab_node_id(kTabNode1);
tab.mutable_tab()->set_window_id(kWindow1.id());
tab.mutable_tab()->set_tab_id(kTab1.id());
UpdateTrackerWithSpecifics(tab, base::Time::Now(), GetTracker());
UpdateTrackerWithSpecifics(tab, base::Time::Now(), &tracker_);
EXPECT_THAT(GetTracker()->LookupSession(kTag),
EXPECT_THAT(tracker_.LookupSession(kTag),
MatchesSyncedSession(kTag, /*window_id_to_tabs*/ {}));
sync_pb::SessionSpecifics header;
header.set_session_tag(kTag);
header.mutable_header()->add_window()->set_window_id(kWindow1.id());
header.mutable_header()->mutable_window(0)->add_tab(kTab1.id());
UpdateTrackerWithSpecifics(header, base::Time::Now(), GetTracker());
UpdateTrackerWithSpecifics(header, base::Time::Now(), &tracker_);
const sessions::SessionTab* tracked_tab =
GetTracker()->LookupSessionTab(kTag, kTab1);
tracker_.LookupSessionTab(kTag, kTab1);
ASSERT_THAT(tracked_tab, NotNull());
EXPECT_EQ(kWindow1, tracked_tab->window_id);
EXPECT_THAT(GetTracker()->LookupSession(kTag),
EXPECT_THAT(tracker_.LookupSession(kTag),
MatchesSyncedSession(
kTag, {{kWindow1.id(), std::vector<int>{kTab1.id()}}}));
}
......@@ -953,7 +989,7 @@ TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithTwoTabsSameId) {
tab1.mutable_tab()->set_window_id(kWindow1.id());
tab1.mutable_tab()->set_tab_id(kTab1.id());
tab1.mutable_tab()->set_pinned(false);
UpdateTrackerWithSpecifics(tab1, base::Time::Now(), GetTracker());
UpdateTrackerWithSpecifics(tab1, base::Time::Now(), &tracker_);
sync_pb::SessionSpecifics tab2;
tab2.set_session_tag(kTag);
......@@ -961,35 +997,35 @@ TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithTwoTabsSameId) {
tab2.mutable_tab()->set_window_id(kWindow1.id());
tab2.mutable_tab()->set_tab_id(kTab1.id());
tab2.mutable_tab()->set_pinned(true);
UpdateTrackerWithSpecifics(tab2, base::Time::Now(), GetTracker());
UpdateTrackerWithSpecifics(tab2, base::Time::Now(), &tracker_);
EXPECT_THAT(GetTracker()->LookupSession(kTag),
EXPECT_THAT(tracker_.LookupSession(kTag),
MatchesSyncedSession(kTag, /*window_id_to_tabs*/ {}));
EXPECT_THAT(GetTracker()->LookupTabNodeIds(kTag),
EXPECT_THAT(tracker_.LookupTabNodeIds(kTag),
ElementsAre(kTabNode1, kTabNode2));
const sessions::SessionTab* tracked_tab =
GetTracker()->LookupSessionTab(kTag, kTab1);
tracker_.LookupSessionTab(kTag, kTab1);
ASSERT_THAT(tracked_tab, NotNull());
EXPECT_EQ(kWindow1, tracked_tab->window_id);
EXPECT_EQ(true, tracked_tab->pinned);
}
TEST_F(SyncedSessionTrackerTest, SerializeTrackerToSpecifics) {
GetTracker()->InitLocalSession(kTag, kSessionName, kDeviceType);
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->GetSession(kTag)->windows[kWindow1]->window_type =
tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
tracker_.PutWindowInSession(kTag, kWindow1);
tracker_.GetSession(kTag)->windows[kWindow1]->window_type =
sync_pb::SessionWindow_BrowserType_TYPE_TABBED;
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab2);
tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
tracker_.PutTabInWindow(kTag, kWindow1, kTab2);
// Unmapped tab.
GetTracker()->GetTab(kTag, kTab3);
tracker_.GetTab(kTag, kTab3);
// |kTabNode4| will be unassociated, because |kTab1| is associated twice.
GetTracker()->ReassociateLocalTab(kTabNode4, kTab1);
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
tracker_.ReassociateLocalTab(kTabNode4, kTab1);
tracker_.ReassociateLocalTab(kTabNode1, kTab1);
// Regular associations.
GetTracker()->ReassociateLocalTab(kTabNode2, kTab2);
GetTracker()->ReassociateLocalTab(kTabNode3, kTab3);
tracker_.ReassociateLocalTab(kTabNode2, kTab2);
tracker_.ReassociateLocalTab(kTabNode3, kTab3);
base::MockCallback<base::RepeatingCallback<void(
const std::string& session_name, sync_pb::SessionSpecifics* specifics)>>
......@@ -1008,32 +1044,31 @@ TEST_F(SyncedSessionTrackerTest, SerializeTrackerToSpecifics) {
Run(kSessionName, Pointee(MatchesTab(kTag, Ne(kWindow1.id()), kTab3.id(),
kTabNode3, /*urls=*/_))));
SerializeTrackerToSpecifics(*GetTracker(), callback.Get());
SerializeTrackerToSpecifics(tracker_, callback.Get());
EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(&callback));
// Serialize the header only.
EXPECT_CALL(callback, Run(kSessionName,
Pointee(MatchesHeader(kTag, {kWindow1.id()},
{kTab1.id(), kTab2.id()}))));
SerializePartialTrackerToSpecifics(*GetTracker(),
{{kTag, {TabNodePool::kInvalidTabNodeID}}},
callback.Get());
SerializePartialTrackerToSpecifics(
tracker_, {{kTag, {TabNodePool::kInvalidTabNodeID}}}, callback.Get());
EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(&callback));
// Serialize a known and associated tab.
EXPECT_CALL(callback, Run(kSessionName,
Pointee(MatchesTab(kTag, kWindow1.id(), kTab1.id(),
kTabNode1, /*urls=*/_))));
SerializePartialTrackerToSpecifics(*GetTracker(), {{kTag, {kTabNode1}}},
SerializePartialTrackerToSpecifics(tracker_, {{kTag, {kTabNode1}}},
callback.Get());
EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(&callback));
// Attempt to serialize unknown entities.
EXPECT_CALL(callback, Run(_, _)).Times(0);
SerializePartialTrackerToSpecifics(*GetTracker(), {{kTag, {kTabNode5}}},
SerializePartialTrackerToSpecifics(tracker_, {{kTag, {kTabNode5}}},
callback.Get());
SerializePartialTrackerToSpecifics(
*GetTracker(), {{kTag2, {TabNodePool::kInvalidTabNodeID, kTabNode1}}},
tracker_, {{kTag2, {TabNodePool::kInvalidTabNodeID, kTabNode1}}},
callback.Get());
}
......@@ -1043,12 +1078,12 @@ TEST_F(SyncedSessionTrackerTest, SerializeTrackerToSpecificsWithEmptyHeader) {
tab.set_tab_node_id(kTabNode1);
tab.mutable_tab()->set_window_id(kWindow1.id());
tab.mutable_tab()->set_tab_id(kTab1.id());
UpdateTrackerWithSpecifics(tab, base::Time::Now(), GetTracker());
UpdateTrackerWithSpecifics(tab, base::Time::Now(), &tracker_);
sync_pb::SessionSpecifics header;
header.set_session_tag(kTag);
header.mutable_header()->set_client_name(kSessionName);
UpdateTrackerWithSpecifics(header, base::Time::Now(), GetTracker());
UpdateTrackerWithSpecifics(header, base::Time::Now(), &tracker_);
base::MockCallback<base::RepeatingCallback<void(
const std::string& session_name, sync_pb::SessionSpecifics* specifics)>>
......@@ -1060,7 +1095,7 @@ TEST_F(SyncedSessionTrackerTest, SerializeTrackerToSpecificsWithEmptyHeader) {
callback,
Run(kSessionName, Pointee(MatchesTab(kTag, /*window_id=*/0, kTab1.id(),
kTabNode1, /*urls=*/_))));
SerializeTrackerToSpecifics(*GetTracker(), callback.Get());
SerializeTrackerToSpecifics(tracker_, callback.Get());
}
} // namespace sync_sessions
......@@ -29,6 +29,8 @@ namespace sync_sessions {
// 1. Associated : Sync node is used and associated with a tab.
// 2. Free : Sync node is unused.
// TODO(crbug.com/882489): Remove feature toggle during code cleanup when a
// satisfying solution is found for closed tabs.
extern const base::Feature kTabNodePoolImmediateDeletion;
class TabNodePool {
......
......@@ -247,14 +247,16 @@ TestSyncedWindowDelegate::~TestSyncedWindowDelegate() = default;
void TestSyncedWindowDelegate::OverrideTabAt(int index,
SyncedTabDelegate* delegate) {
if (index >= static_cast<int>(tab_delegates_.size()))
tab_delegates_.resize(index + 1, nullptr);
tab_delegates_[index] = delegate;
}
void TestSyncedWindowDelegate::CloseTab(SessionID tab_id) {
base::EraseIf(tab_delegates_,
[tab_id](const std::pair<int, SyncedTabDelegate*>& entry) {
return entry.second->GetSessionId() == tab_id;
});
base::EraseIf(tab_delegates_, [tab_id](SyncedTabDelegate* tab) {
return tab->GetSessionId() == tab_id;
});
}
void TestSyncedWindowDelegate::SetIsSessionRestoreInProgress(bool value) {
......@@ -294,10 +296,10 @@ bool TestSyncedWindowDelegate::IsTabPinned(const SyncedTabDelegate* tab) const {
}
SyncedTabDelegate* TestSyncedWindowDelegate::GetTabAt(int index) const {
if (tab_delegates_.find(index) != tab_delegates_.end())
return tab_delegates_.find(index)->second;
if (index >= static_cast<int>(tab_delegates_.size()))
return nullptr;
return nullptr;
return tab_delegates_[index];
}
SessionID TestSyncedWindowDelegate::GetTabIdAt(int index) const {
......
......@@ -144,7 +144,7 @@ class TestSyncedWindowDelegate : public SyncedWindowDelegate {
const SessionID window_id_;
const sync_pb::SessionWindow_BrowserType window_type_;
std::map<int, SyncedTabDelegate*> tab_delegates_;
std::vector<SyncedTabDelegate*> tab_delegates_;
bool is_session_restore_in_progress_;
DISALLOW_COPY_AND_ASSIGN(TestSyncedWindowDelegate);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment