Commit 783900c2 authored by zea's avatar zea Committed by Commit bot

[Sync] Handle reassociation of tabs where the old tab is already mapped.

If ReassociateLocalTab is called with a tab node that is already mapped to a
tab, it's possible to wind up with the synced session tracker holding two
tab objects referring to the same tab id. This can lead to memory corruption.

This CL makes the tracker defensive against that scenario, and adds some more
data verification in the unit tests

BUG=714524, 639009

Review-Url: https://codereview.chromium.org/2856913007
Cr-Commit-Position: refs/heads/master@{#469553}
parent a367e903
......@@ -864,9 +864,6 @@ bool SessionsSyncManager::InitFromSyncModel(
rewritten_specifics.tab_node_id(), new_tab_id);
UpdateTrackerWithSpecifics(rewritten_specifics,
remote.GetModifiedTime());
session_tracker_.ReassociateLocalTab(
rewritten_specifics.tab_node_id(), new_tab_id);
}
}
}
......
......@@ -1695,7 +1695,8 @@ TEST_F(SessionsSyncManagerTest, ProcessForeignDeleteTabsWithReusedNodeIds) {
TEST_F(SessionsSyncManagerTest, AssociationReusesNodes) {
SyncChangeList changes;
AddTab(AddWindow()->GetSessionId(), kFoo1);
TestSyncedWindowDelegate* window = AddWindow();
TestSyncedTabDelegate* tab = AddTab(window->GetSessionId(), kFoo1);
InitWithSyncDataTakeOutput(SyncDataList(), &changes);
ASSERT_TRUE(ChangeTypeMatches(changes,
{SyncChange::ACTION_ADD, SyncChange::ACTION_ADD,
......@@ -1728,6 +1729,24 @@ TEST_F(SessionsSyncManagerTest, AssociationReusesNodes) {
VerifyLocalTabChange(changes[0], 1, kFoo1);
EXPECT_EQ(tab_node_id,
changes[0].sync_data().GetSpecifics().session().tab_node_id());
changes.clear();
// Update the original tab. Ensure the same tab node is updated.
NavigateTab(tab, kFoo2);
FilterOutLocalHeaderChanges(&changes);
ASSERT_TRUE(ChangeTypeMatches(changes, {SyncChange::ACTION_UPDATE}));
VerifyLocalTabChange(changes[0], 2, kFoo2);
EXPECT_EQ(tab_node_id,
changes[0].sync_data().GetSpecifics().session().tab_node_id());
changes.clear();
// Add a new tab. It should reuse the second tab node.
AddTab(window->GetSessionId(), kBar1);
FilterOutLocalHeaderChanges(&changes);
ASSERT_TRUE(ChangeTypeMatches(changes, {SyncChange::ACTION_UPDATE}));
VerifyLocalTabChange(changes[0], 1, kBar1);
EXPECT_EQ(tab_node_id + 1,
changes[0].sync_data().GetSpecifics().session().tab_node_id());
}
// Ensure that the merge process deletes a tab node without a tab id.
......
......@@ -397,33 +397,70 @@ void SyncedSessionTracker::ReassociateLocalTab(int tab_node_id,
SessionID::id_type old_tab_id =
local_tab_pool_.GetTabIdFromTabNodeId(tab_node_id);
if (new_tab_id == old_tab_id) {
return;
}
local_tab_pool_.ReassociateTabNode(tab_node_id, new_tab_id);
sessions::SessionTab* tab_ptr = nullptr;
auto new_tab_iter = synced_tab_map_[local_session_tag_].find(new_tab_id);
auto old_tab_iter = synced_tab_map_[local_session_tag_].find(old_tab_id);
if (old_tab_id != kInvalidTabID &&
old_tab_iter != synced_tab_map_[local_session_tag_].end()) {
tab_ptr = old_tab_iter->second;
DCHECK(tab_ptr);
// Remove the tab from the synced tab map under the old id.
synced_tab_map_[local_session_tag_].erase(old_tab_iter);
} else {
if (new_tab_iter != synced_tab_map_[local_session_tag_].end()) {
// If both the old and the new tab already exist, delete the new tab
// and use the old tab in its place.
auto unmapped_tabs_iter =
unmapped_tabs_[local_session_tag_].find(new_tab_id);
if (unmapped_tabs_iter != unmapped_tabs_[local_session_tag_].end()) {
unmapped_tabs_[local_session_tag_].erase(unmapped_tabs_iter);
} else {
sessions::SessionTab* new_tab_ptr = new_tab_iter->second;
for (auto& window_iter_pair : GetSession(local_session_tag_)->windows) {
auto& window_tabs = window_iter_pair.second->wrapped_window.tabs;
auto tab_iter = std::find_if(
window_tabs.begin(), window_tabs.end(),
[&new_tab_ptr](const std::unique_ptr<sessions::SessionTab>& tab) {
return tab.get() == new_tab_ptr;
});
if (tab_iter != window_tabs.end()) {
window_tabs.erase(tab_iter);
break;
}
}
}
synced_tab_map_[local_session_tag_].erase(new_tab_iter);
}
// If the old tab is unmapped, update the tab id under which it is
// indexed.
auto unmapped_tabs_iter =
unmapped_tabs_[local_session_tag_].find(old_tab_id);
if (old_tab_id != kInvalidTabID &&
unmapped_tabs_iter != unmapped_tabs_[local_session_tag_].end()) {
std::unique_ptr<sessions::SessionTab> tab =
std::move(unmapped_tabs_iter->second);
DCHECK_EQ(tab_ptr, tab.get());
unmapped_tabs_[local_session_tag_].erase(unmapped_tabs_iter);
unmapped_tabs_[local_session_tag_][new_tab_id] = std::move(tab);
}
}
if (tab_ptr == nullptr) {
// It's possible a placeholder is already in place for the new tab. If so,
// reuse it, otherwise create a new one (which will default to unmapped).
tab_ptr = GetTab(local_session_tag_, new_tab_id);
}
// If the old tab is unmapped, update the tab id under which it is indexed.
auto unmapped_tabs_iter = unmapped_tabs_[local_session_tag_].find(old_tab_id);
if (old_tab_id != kInvalidTabID &&
unmapped_tabs_iter != unmapped_tabs_[local_session_tag_].end()) {
std::unique_ptr<sessions::SessionTab> tab =
std::move(unmapped_tabs_iter->second);
DCHECK_EQ(tab_ptr, tab.get());
unmapped_tabs_[local_session_tag_].erase(unmapped_tabs_iter);
unmapped_tabs_[local_session_tag_][new_tab_id] = std::move(tab);
}
// Update the tab id.
if (old_tab_id != kInvalidTabID) {
DVLOG(1) << "Remapped tab " << old_tab_id << " with node " << tab_node_id
......
......@@ -174,6 +174,9 @@ class SyncedSessionTracker {
// any previous SessionTab object the node was associated with. This is useful
// on restart when sync needs to reassociate tabs from a previous session with
// newly restored tabs (and can be used in conjunction with PutTabInWindow).
// If |new_tab_id| is already associated with a tab object, that tab will be
// overwritten. Reassociating a tab with a node it is already mapped to will
// have no effect.
void ReassociateLocalTab(int tab_node_id, SessionID::id_type new_tab_id);
// **** Methods for querying/manipulating overall state ****.
......
......@@ -12,6 +12,10 @@
#include "components/sync_sessions/synced_tab_delegate.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::AssertionFailure;
using testing::AssertionResult;
using testing::AssertionSuccess;
namespace sync_sessions {
namespace {
......@@ -23,7 +27,9 @@ const char kTag2[] = "tag2";
const char kTag3[] = "tag3";
const char kTitle[] = "title";
const int kWindow1 = 1;
const int kTabNode = 0;
const int kTabNode1 = 1;
const int kTabNode2 = 2;
const int kTabNode3 = 3;
const int kTab1 = 15;
const int kTab2 = 25;
const int kTab3 = 35;
......@@ -38,6 +44,63 @@ class SyncedSessionTrackerTest : public testing::Test {
SyncedSessionTracker* GetTracker() { return &tracker_; }
TabNodePool* GetTabNodePool() { return &tracker_.local_tab_pool_; }
// Verify that each tab within a session is allocated one SessionTab object,
// and that that tab object is owned either by the Session itself or the
// |unmapped_tabs_| tab holder.
AssertionResult VerifyTabIntegrity(const std::string& session_tag) {
// First get all the tabs associated with this session.
int total_tab_count = 0;
auto tab_map_iter = tracker_.synced_tab_map_.find(session_tag);
if (tab_map_iter != tracker_.synced_tab_map_.end())
total_tab_count = tab_map_iter->second.size();
// Now traverse the SyncedSession tree to verify the mapped tabs all match
// up.
int mapped_tab_count = 0;
if (tracker_.synced_session_map_.find(session_tag) !=
tracker_.synced_session_map_.end()) {
SyncedSession* session = tracker_.synced_session_map_[session_tag].get();
for (auto& window_pair : session->windows) {
mapped_tab_count += window_pair.second->wrapped_window.tabs.size();
for (auto& tab : window_pair.second->wrapped_window.tabs) {
if (tab_map_iter->second[tab->tab_id.id()] != tab.get()) {
return AssertionFailure()
<< "Mapped tab " << tab->tab_id.id()
<< " does not match synced tab map " << tab->tab_id.id();
}
}
}
}
// Wrap up by verifying all unmapped tabs are tracked.
int unmapped_tab_count = 0;
if (tracker_.unmapped_tabs_.find(session_tag) !=
tracker_.unmapped_tabs_.end()) {
unmapped_tab_count = tracker_.unmapped_tabs_[session_tag].size();
for (const auto& tab_pair : tracker_.unmapped_tabs_[session_tag]) {
if (tab_pair.first != tab_pair.second->tab_id.id()) {
return AssertionFailure()
<< "Unmapped tab " << tab_pair.second->tab_id.id()
<< " associated with wrong tab " << tab_pair.first;
}
if (tab_map_iter->second[tab_pair.second->tab_id.id()] !=
tab_pair.second.get()) {
return AssertionFailure()
<< "Unmapped tab " << tab_pair.second->tab_id.id()
<< " does not match synced tab map "
<< tab_pair.second->tab_id.id();
}
}
}
return mapped_tab_count + unmapped_tab_count == total_tab_count
? AssertionSuccess()
: AssertionFailure()
<< " Tab count mismatch. Total: " << total_tab_count
<< ". Mapped + Unmapped: " << mapped_tab_count << " + "
<< unmapped_tab_count;
}
private:
FakeSyncSessionsClient sessions_client_;
SyncedSessionTracker tracker_;
......@@ -76,6 +139,7 @@ TEST_F(SyncedSessionTrackerTest, PutTabInWindow) {
ASSERT_EQ(1U, session->windows[10]->wrapped_window.tabs.size());
ASSERT_EQ(GetTracker()->GetTab(kTag, 15),
session->windows[10]->wrapped_window.tabs[0].get());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
// Should clean up memory on its own.
}
......@@ -202,6 +266,7 @@ TEST_F(SyncedSessionTrackerTest, Complex) {
ASSERT_EQ(0U, GetTracker()->num_synced_tabs(kTag));
ASSERT_EQ(0U, GetTracker()->num_synced_tabs(kTag2));
ASSERT_EQ(0U, GetTracker()->num_synced_sessions());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
TEST_F(SyncedSessionTrackerTest, ManyGetTabs) {
......@@ -338,6 +403,7 @@ TEST_F(SyncedSessionTrackerTest, SessionTracking) {
ASSERT_EQ(2U, GetTracker()->num_synced_sessions());
ASSERT_EQ(4U, GetTracker()->num_synced_tabs(kTag));
ASSERT_EQ(1U, GetTracker()->num_synced_tabs(kTag2));
ASSERT_TRUE(VerifyTabIntegrity(kTag));
// All memory should be properly deallocated by destructor for the
// SyncedSessionTracker.
......@@ -364,14 +430,12 @@ TEST_F(SyncedSessionTrackerTest, DeleteForeignTab) {
GetTracker()->DeleteForeignTab(kTag, tab_node_id_2);
GetTracker()->LookupForeignTabNodeIds(kTag, &result);
EXPECT_TRUE(result.empty());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
TEST_F(SyncedSessionTrackerTest, CleanupLocalTabs) {
std::set<int> free_node_ids;
int tab_node_id = TabNodePool::kInvalidTabNodeID;
const int kTabNode1 = 1;
const int kTabNode2 = 2;
const int kTabNode3 = 3;
GetTracker()->SetLocalSessionTag(kTag);
......@@ -424,6 +488,7 @@ TEST_F(SyncedSessionTrackerTest, CleanupLocalTabs) {
EXPECT_TRUE(free_node_ids.empty());
EXPECT_TRUE(GetTabNodePool()->Full());
EXPECT_FALSE(GetTabNodePool()->Empty());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
TEST_F(SyncedSessionTrackerTest, ReassociateTabMapped) {
......@@ -431,9 +496,10 @@ TEST_F(SyncedSessionTrackerTest, ReassociateTabMapped) {
// First create the tab normally.
GetTracker()->SetLocalSessionTag(kTag);
EXPECT_FALSE(GetTracker()->IsLocalTabNodeAssociated(kTabNode));
GetTracker()->ReassociateLocalTab(kTabNode, kTab1);
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode));
EXPECT_FALSE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab1));
// Map it to a window with the same tab id as it was created with.
......@@ -441,6 +507,7 @@ TEST_F(SyncedSessionTrackerTest, ReassociateTabMapped) {
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
GetTracker()->CleanupLocalTabs(&free_node_ids);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
SyncedSession* session = GetTracker()->GetSession(kTag);
ASSERT_EQ(1U, session->windows.size());
......@@ -449,8 +516,9 @@ TEST_F(SyncedSessionTrackerTest, ReassociateTabMapped) {
session->windows[kWindow1]->wrapped_window.tabs[0].get());
// Then reassociate with a new tab id.
GetTracker()->ReassociateLocalTab(kTabNode, kTab2);
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab2);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
......@@ -460,6 +528,7 @@ TEST_F(SyncedSessionTrackerTest, ReassociateTabMapped) {
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab2);
GetTracker()->CleanupLocalTabs(&free_node_ids);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(free_node_ids.empty());
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
......@@ -468,8 +537,9 @@ TEST_F(SyncedSessionTrackerTest, ReassociateTabMapped) {
ASSERT_EQ(GetTracker()->GetTab(kTag, kTab2),
session->windows[kWindow1]->wrapped_window.tabs[0].get());
ASSERT_EQ(session->tab_node_ids.size(),
session->tab_node_ids.count(kTabNode));
session->tab_node_ids.count(kTabNode1));
ASSERT_EQ(1U, GetTabNodePool()->Capacity());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
TEST_F(SyncedSessionTrackerTest, ReassociateTabMappedTwice) {
......@@ -477,9 +547,10 @@ TEST_F(SyncedSessionTrackerTest, ReassociateTabMappedTwice) {
// First create the tab normally.
GetTracker()->SetLocalSessionTag(kTag);
EXPECT_FALSE(GetTracker()->IsLocalTabNodeAssociated(kTabNode));
GetTracker()->ReassociateLocalTab(kTabNode, kTab1);
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode));
EXPECT_FALSE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab1));
// Map it to a window with the same tab id as it was created with.
......@@ -487,6 +558,7 @@ TEST_F(SyncedSessionTrackerTest, ReassociateTabMappedTwice) {
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
GetTracker()->CleanupLocalTabs(&free_node_ids);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(free_node_ids.empty());
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
SyncedSession* session = GetTracker()->GetSession(kTag);
......@@ -496,8 +568,9 @@ TEST_F(SyncedSessionTrackerTest, ReassociateTabMappedTwice) {
session->windows[kWindow1]->wrapped_window.tabs[0].get());
// Then reassociate with a new tab id.
GetTracker()->ReassociateLocalTab(kTabNode, kTab2);
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab2);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
......@@ -514,6 +587,7 @@ TEST_F(SyncedSessionTrackerTest, ReassociateTabMappedTwice) {
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab2);
GetTracker()->CleanupLocalTabs(&free_node_ids);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(free_node_ids.empty());
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
......@@ -522,7 +596,7 @@ TEST_F(SyncedSessionTrackerTest, ReassociateTabMappedTwice) {
EXPECT_EQ(GetTracker()->GetTab(kTag, kTab2),
session->windows[kWindow1]->wrapped_window.tabs[1].get());
EXPECT_EQ(session->tab_node_ids.size(),
session->tab_node_ids.count(kTabNode));
session->tab_node_ids.count(kTabNode1));
EXPECT_EQ(1U, GetTabNodePool()->Capacity());
// Attempting to access the original tab will create a new SessionTab object.
......@@ -530,6 +604,7 @@ TEST_F(SyncedSessionTrackerTest, ReassociateTabMappedTwice) {
GetTracker()->GetTab(kTag, kTab2));
int tab_node_id = -1;
EXPECT_FALSE(GetTracker()->GetTabNodeFromLocalTabId(kTab1, &tab_node_id));
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
TEST_F(SyncedSessionTrackerTest, ReassociateTabUnmapped) {
......@@ -537,20 +612,23 @@ TEST_F(SyncedSessionTrackerTest, ReassociateTabUnmapped) {
// First create the old tab in an unmapped state.
GetTracker()->SetLocalSessionTag(kTag);
EXPECT_FALSE(GetTracker()->IsLocalTabNodeAssociated(kTabNode));
GetTracker()->ReassociateLocalTab(kTabNode, kTab1);
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode));
EXPECT_FALSE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab1));
// Map it to a window, but reassociated with a new tab id.
GetTracker()->ResetSessionTracking(kTag);
GetTracker()->ReassociateLocalTab(kTabNode, kTab2);
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab2);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(GetTracker()->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);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(free_node_ids.empty());
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
......@@ -560,30 +638,34 @@ TEST_F(SyncedSessionTrackerTest, ReassociateTabUnmapped) {
ASSERT_EQ(GetTracker()->GetTab(kTag, kTab2),
session->windows[kWindow1]->wrapped_window.tabs[0].get());
ASSERT_EQ(session->tab_node_ids.size(),
session->tab_node_ids.count(kTabNode));
session->tab_node_ids.count(kTabNode1));
ASSERT_EQ(1U, GetTabNodePool()->Capacity());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
TEST_F(SyncedSessionTrackerTest, ReassociateTabMapMismatch) {
TEST_F(SyncedSessionTrackerTest, ReassociateTabOldUnmappedNewMapped) {
std::set<int> free_node_ids;
// First create the old tab in an unmapped state.
GetTracker()->SetLocalSessionTag(kTag);
EXPECT_FALSE(GetTracker()->IsLocalTabNodeAssociated(kTabNode));
GetTracker()->ReassociateLocalTab(kTabNode, kTab1);
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode));
EXPECT_FALSE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab1));
// Map an unseen tab to a window, then reassociate the existing tab to the
// mapped tab id.
GetTracker()->ResetSessionTracking(kTag);
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode));
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab2);
GetTracker()->CleanupLocalTabs(&free_node_ids);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
GetTracker()->ReassociateLocalTab(kTabNode, kTab2);
GetTracker()->ReassociateLocalTab(kTabNode1, kTab2);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(free_node_ids.empty());
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
......@@ -594,8 +676,114 @@ TEST_F(SyncedSessionTrackerTest, ReassociateTabMapMismatch) {
ASSERT_EQ(GetTracker()->GetTab(kTag, kTab2),
session->windows[kWindow1]->wrapped_window.tabs[0].get());
ASSERT_EQ(session->tab_node_ids.size(),
session->tab_node_ids.count(kTabNode));
session->tab_node_ids.count(kTabNode1));
ASSERT_EQ(1U, GetTabNodePool()->Capacity());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
TEST_F(SyncedSessionTrackerTest, ReassociateTabSameTabId) {
std::set<int> free_node_ids;
// First create the tab normally.
GetTracker()->SetLocalSessionTag(kTag);
EXPECT_FALSE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab1));
// Map it to a window.
GetTracker()->ResetSessionTracking(kTag);
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab1);
GetTracker()->CleanupLocalTabs(&free_node_ids);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
SyncedSession* session = GetTracker()->GetSession(kTag);
ASSERT_EQ(1U, session->windows.size());
ASSERT_EQ(1U, session->windows[kWindow1]->wrapped_window.tabs.size());
ASSERT_EQ(GetTracker()->GetTab(kTag, kTab1),
session->windows[kWindow1]->wrapped_window.tabs[0].get());
// Reassociate, using the same tab id.
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
EXPECT_FALSE(GetTracker()->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);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(free_node_ids.empty());
EXPECT_FALSE(GetTracker()->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),
session->windows[kWindow1]->wrapped_window.tabs[0].get());
ASSERT_EQ(session->tab_node_ids.size(),
session->tab_node_ids.count(kTabNode1));
ASSERT_EQ(1U, GetTabNodePool()->Capacity());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
TEST_F(SyncedSessionTrackerTest, ReassociateTabOldMappedNewUnmapped) {
std::set<int> free_node_ids;
// First create an unmapped tab.
GetTracker()->SetLocalSessionTag(kTag);
EXPECT_FALSE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
GetTracker()->ReassociateLocalTab(kTabNode1, kTab1);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
EXPECT_TRUE(GetTracker()->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);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
SyncedSession* session = GetTracker()->GetSession(kTag);
ASSERT_EQ(1U, session->windows.size());
ASSERT_EQ(1U, session->windows[kWindow1]->wrapped_window.tabs.size());
ASSERT_EQ(GetTracker()->GetTab(kTag, kTab1),
session->windows[kWindow1]->wrapped_window.tabs[0].get());
// Create a second unmapped tab.
GetTracker()->ReassociateLocalTab(kTabNode2, kTab2);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode2));
EXPECT_TRUE(GetTracker()->IsTabUnmappedForTesting(kTab2));
// Reassociate the second tab with node of the first tab.
GetTracker()->ReassociateLocalTab(kTabNode1, kTab2);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1));
EXPECT_FALSE(GetTracker()->IsLocalTabNodeAssociated(kTabNode2));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2));
// Now map the new one.
GetTracker()->ResetSessionTracking(kTag);
GetTracker()->PutWindowInSession(kTag, kWindow1);
GetTracker()->PutTabInWindow(kTag, kWindow1, kTab2);
GetTracker()->CleanupLocalTabs(&free_node_ids);
ASSERT_TRUE(VerifyTabIntegrity(kTag));
EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab1));
EXPECT_FALSE(GetTracker()->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),
session->windows[kWindow1]->wrapped_window.tabs[0].get());
ASSERT_EQ(2U, GetTabNodePool()->Capacity());
ASSERT_TRUE(VerifyTabIntegrity(kTag));
}
} // namespace sync_sessions
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