| // Copyright 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/sync_sessions/synced_session_tracker.h" |
| |
| #include "base/rand_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "components/sessions/core/serialized_navigation_entry_test_helper.h" |
| #include "components/sync_sessions/fake_sync_sessions_client.h" |
| #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 { |
| |
| const char kValidUrl[] = "http://www.example.com"; |
| const char kInvalidUrl[] = "invalid.url"; |
| const char kTag[] = "tag"; |
| const char kTag2[] = "tag2"; |
| const char kTag3[] = "tag3"; |
| const char kTitle[] = "title"; |
| const int kWindow1 = 1; |
| const int kTabNode1 = 1; |
| const int kTabNode2 = 2; |
| const int kTabNode3 = 3; |
| const int kTab1 = 15; |
| const int kTab2 = 25; |
| const int kTab3 = 35; |
| |
| } // namespace |
| |
| class SyncedSessionTrackerTest : public testing::Test { |
| public: |
| SyncedSessionTrackerTest() : tracker_(&sessions_client_) {} |
| ~SyncedSessionTrackerTest() override {} |
| |
| 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_; |
| }; |
| |
| TEST_F(SyncedSessionTrackerTest, GetSession) { |
| SyncedSession* session1 = GetTracker()->GetSession(kTag); |
| SyncedSession* session2 = GetTracker()->GetSession(kTag2); |
| ASSERT_EQ(session1, GetTracker()->GetSession(kTag)); |
| ASSERT_NE(session1, session2); |
| // Should clean up memory on its own. |
| } |
| |
| TEST_F(SyncedSessionTrackerTest, GetTabUnmapped) { |
| sessions::SessionTab* tab = GetTracker()->GetTab(kTag, 0); |
| ASSERT_EQ(tab, GetTracker()->GetTab(kTag, 0)); |
| // Should clean up memory on its own. |
| } |
| |
| TEST_F(SyncedSessionTrackerTest, PutWindowInSession) { |
| GetTracker()->PutWindowInSession(kTag, 0); |
| SyncedSession* session = GetTracker()->GetSession(kTag); |
| ASSERT_EQ(1U, session->windows.size()); |
| |
| // Doing it again should have no effect. |
| GetTracker()->PutWindowInSession(kTag, 0); |
| ASSERT_EQ(1U, session->windows.size()); |
| // Should clean up memory on its own. |
| } |
| |
| TEST_F(SyncedSessionTrackerTest, PutTabInWindow) { |
| GetTracker()->PutWindowInSession(kTag, 10); |
| GetTracker()->PutTabInWindow(kTag, 10, 15); // win id 10, tab id 15 |
| SyncedSession* session = GetTracker()->GetSession(kTag); |
| ASSERT_EQ(1U, session->windows.size()); |
| 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. |
| } |
| |
| TEST_F(SyncedSessionTrackerTest, LookupAllForeignSessions) { |
| std::vector<const SyncedSession*> sessions; |
| ASSERT_FALSE(GetTracker()->LookupAllForeignSessions( |
| &sessions, SyncedSessionTracker::PRESENTABLE)); |
| GetTracker()->GetSession(kTag); |
| GetTracker()->PutWindowInSession(kTag, 0); |
| GetTracker()->PutTabInWindow(kTag, 0, 15); |
| sessions::SessionTab* tab = GetTracker()->GetTab(kTag, 15); |
| ASSERT_TRUE(tab); |
| tab->navigations.push_back( |
| sessions::SerializedNavigationEntryTestHelper::CreateNavigation(kValidUrl, |
| kTitle)); |
| GetTracker()->GetSession(kTag2); |
| GetTracker()->GetSession(kTag3); |
| GetTracker()->PutWindowInSession(kTag3, 0); |
| GetTracker()->PutTabInWindow(kTag3, 0, 15); |
| tab = GetTracker()->GetTab(kTag3, 15); |
| ASSERT_TRUE(tab); |
| tab->navigations.push_back( |
| sessions::SerializedNavigationEntryTestHelper::CreateNavigation( |
| kInvalidUrl, kTitle)); |
| ASSERT_TRUE(GetTracker()->LookupAllForeignSessions( |
| &sessions, SyncedSessionTracker::PRESENTABLE)); |
| // Only the session with a valid window and tab gets returned. |
| ASSERT_EQ(1U, sessions.size()); |
| ASSERT_EQ(kTag, sessions[0]->session_tag); |
| |
| ASSERT_TRUE(GetTracker()->LookupAllForeignSessions( |
| &sessions, SyncedSessionTracker::RAW)); |
| ASSERT_EQ(3U, sessions.size()); |
| } |
| |
| TEST_F(SyncedSessionTrackerTest, LookupSessionWindows) { |
| std::vector<const sessions::SessionWindow*> windows; |
| ASSERT_FALSE(GetTracker()->LookupSessionWindows(kTag, &windows)); |
| GetTracker()->GetSession(kTag); |
| GetTracker()->PutWindowInSession(kTag, 0); |
| GetTracker()->PutWindowInSession(kTag, 2); |
| GetTracker()->GetSession(kTag2); |
| GetTracker()->PutWindowInSession(kTag2, 0); |
| GetTracker()->PutWindowInSession(kTag2, 2); |
| ASSERT_TRUE(GetTracker()->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]); |
| ASSERT_NE(windows[1], windows[0]); |
| } |
| |
| TEST_F(SyncedSessionTrackerTest, LookupSessionTab) { |
| const sessions::SessionTab* tab; |
| ASSERT_FALSE(GetTracker()->LookupSessionTab(kTag, kInvalidTabID, &tab)); |
| ASSERT_FALSE(GetTracker()->LookupSessionTab(kTag, 5, &tab)); |
| GetTracker()->GetSession(kTag); |
| GetTracker()->PutWindowInSession(kTag, 0); |
| GetTracker()->PutTabInWindow(kTag, 0, 5); |
| ASSERT_TRUE(GetTracker()->LookupSessionTab(kTag, 5, &tab)); |
| ASSERT_NE((sessions::SessionTab*)nullptr, tab); |
| } |
| |
| 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, 0)); |
| tabs1.push_back(GetTracker()->GetTab(kTag, 1)); |
| tabs1.push_back(GetTracker()->GetTab(kTag, 2)); |
| ASSERT_EQ(3U, GetTracker()->num_synced_tabs(kTag)); |
| ASSERT_EQ(0U, GetTracker()->num_synced_sessions()); |
| temp_tab = GetTracker()->GetTab(kTag, 0); // Already created. |
| ASSERT_EQ(3U, GetTracker()->num_synced_tabs(kTag)); |
| ASSERT_EQ(0U, GetTracker()->num_synced_sessions()); |
| ASSERT_EQ(tabs1[0], temp_tab); |
| tabs2.push_back(GetTracker()->GetTab(kTag2, 0)); |
| ASSERT_EQ(1U, GetTracker()->num_synced_tabs(kTag2)); |
| ASSERT_EQ(0U, GetTracker()->num_synced_sessions()); |
| ASSERT_FALSE(GetTracker()->DeleteForeignSession(kTag3)); |
| |
| SyncedSession* session = GetTracker()->GetSession(kTag); |
| SyncedSession* session2 = GetTracker()->GetSession(kTag2); |
| SyncedSession* session3 = GetTracker()->GetSession(kTag3); |
| session3->device_type = SyncedSession::TYPE_OTHER; |
| ASSERT_EQ(3U, GetTracker()->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()); |
| |
| GetTracker()->PutWindowInSession(kTag, 0); // Create a window. |
| GetTracker()->PutTabInWindow(kTag, 0, 2); // No longer unmapped. |
| ASSERT_EQ(3U, GetTracker()->num_synced_tabs(kTag)); // Has not changed. |
| |
| const sessions::SessionTab* tab_ptr; |
| ASSERT_TRUE(GetTracker()->LookupSessionTab(kTag, 0, &tab_ptr)); |
| ASSERT_EQ(tab_ptr, tabs1[0]); |
| ASSERT_TRUE(GetTracker()->LookupSessionTab(kTag, 2, &tab_ptr)); |
| ASSERT_EQ(tab_ptr, tabs1[2]); |
| ASSERT_FALSE(GetTracker()->LookupSessionTab(kTag, 3, &tab_ptr)); |
| ASSERT_FALSE(tab_ptr); |
| |
| std::vector<const sessions::SessionWindow*> windows; |
| ASSERT_TRUE(GetTracker()->LookupSessionWindows(kTag, &windows)); |
| ASSERT_EQ(1U, windows.size()); |
| ASSERT_TRUE(GetTracker()->LookupSessionWindows(kTag2, &windows)); |
| ASSERT_EQ(0U, windows.size()); |
| |
| // The sessions don't have valid tabs, lookup should not succeed. |
| std::vector<const SyncedSession*> sessions; |
| ASSERT_FALSE(GetTracker()->LookupAllForeignSessions( |
| &sessions, SyncedSessionTracker::PRESENTABLE)); |
| ASSERT_TRUE(GetTracker()->LookupAllForeignSessions( |
| &sessions, SyncedSessionTracker::RAW)); |
| ASSERT_EQ(2U, sessions.size()); |
| |
| 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()); |
| ASSERT_TRUE(VerifyTabIntegrity(kTag)); |
| } |
| |
| TEST_F(SyncedSessionTrackerTest, ManyGetTabs) { |
| ASSERT_TRUE(GetTracker()->Empty()); |
| const int kMaxSessions = 10; |
| const int kMaxTabs = 1000; |
| const int kMaxAttempts = 10000; |
| for (int j = 0; j < kMaxSessions; ++j) { |
| std::string tag = base::StringPrintf("tag%d", j); |
| for (int i = 0; i < kMaxAttempts; ++i) { |
| // 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(tag, rand_tab_num + 1); |
| ASSERT_TRUE(tab); |
| } |
| } |
| } |
| |
| TEST_F(SyncedSessionTrackerTest, LookupForeignTabNodeIds) { |
| std::set<int> result; |
| |
| GetTracker()->OnTabNodeSeen(kTag, 1); |
| GetTracker()->OnTabNodeSeen(kTag, 2); |
| GetTracker()->LookupForeignTabNodeIds(kTag, &result); |
| EXPECT_EQ(2U, result.size()); |
| EXPECT_FALSE(result.end() == result.find(1)); |
| EXPECT_FALSE(result.end() == result.find(2)); |
| GetTracker()->LookupForeignTabNodeIds(kTag2, &result); |
| EXPECT_TRUE(result.empty()); |
| |
| GetTracker()->PutWindowInSession(kTag, 0); |
| GetTracker()->PutTabInWindow(kTag, 0, 3); |
| GetTracker()->LookupForeignTabNodeIds(kTag, &result); |
| EXPECT_EQ(2U, result.size()); |
| |
| GetTracker()->OnTabNodeSeen(kTag, 3); |
| GetTracker()->LookupForeignTabNodeIds(kTag, &result); |
| EXPECT_EQ(3U, result.size()); |
| EXPECT_FALSE(result.end() == result.find(3)); |
| |
| GetTracker()->OnTabNodeSeen(kTag2, 21); |
| GetTracker()->OnTabNodeSeen(kTag2, 22); |
| GetTracker()->LookupForeignTabNodeIds(kTag2, &result); |
| EXPECT_EQ(2U, result.size()); |
| EXPECT_FALSE(result.end() == result.find(21)); |
| EXPECT_FALSE(result.end() == result.find(22)); |
| GetTracker()->LookupForeignTabNodeIds(kTag, &result); |
| EXPECT_EQ(3U, result.size()); |
| EXPECT_FALSE(result.end() == result.find(1)); |
| EXPECT_FALSE(result.end() == result.find(2)); |
| |
| GetTracker()->LookupForeignTabNodeIds(kTag3, &result); |
| EXPECT_TRUE(result.empty()); |
| GetTracker()->PutWindowInSession(kTag3, 1); |
| GetTracker()->PutTabInWindow(kTag3, 1, 5); |
| GetTracker()->LookupForeignTabNodeIds(kTag3, &result); |
| EXPECT_TRUE(result.empty()); |
| EXPECT_FALSE(GetTracker()->DeleteForeignSession(kTag3)); |
| GetTracker()->LookupForeignTabNodeIds(kTag3, &result); |
| EXPECT_TRUE(result.empty()); |
| |
| EXPECT_FALSE(GetTracker()->DeleteForeignSession(kTag)); |
| GetTracker()->LookupForeignTabNodeIds(kTag, &result); |
| EXPECT_TRUE(result.empty()); |
| GetTracker()->LookupForeignTabNodeIds(kTag2, &result); |
| EXPECT_EQ(2U, result.size()); |
| EXPECT_FALSE(result.end() == result.find(21)); |
| EXPECT_FALSE(result.end() == result.find(22)); |
| |
| GetTracker()->OnTabNodeSeen(kTag2, 21); |
| GetTracker()->OnTabNodeSeen(kTag2, 23); |
| GetTracker()->LookupForeignTabNodeIds(kTag2, &result); |
| EXPECT_EQ(3U, result.size()); |
| EXPECT_FALSE(result.end() == result.find(21)); |
| EXPECT_FALSE(result.end() == result.find(22)); |
| EXPECT_FALSE(result.end() == result.find(23)); |
| |
| EXPECT_FALSE(GetTracker()->DeleteForeignSession(kTag2)); |
| GetTracker()->LookupForeignTabNodeIds(kTag2, &result); |
| EXPECT_TRUE(result.empty()); |
| } |
| |
| TEST_F(SyncedSessionTrackerTest, SessionTracking) { |
| ASSERT_TRUE(GetTracker()->Empty()); |
| |
| // Create some session information that is stale. |
| SyncedSession* session1 = GetTracker()->GetSession(kTag); |
| GetTracker()->PutWindowInSession(kTag, 0); |
| GetTracker()->PutTabInWindow(kTag, 0, 0); |
| GetTracker()->PutTabInWindow(kTag, 0, 1); |
| GetTracker()->GetTab(kTag, 2)->window_id.set_id(0); // Will be unmapped. |
| GetTracker()->GetTab(kTag, 3)->window_id.set_id(0); // Will be unmapped. |
| GetTracker()->PutWindowInSession(kTag, 1); |
| GetTracker()->PutTabInWindow(kTag, 1, 4); |
| GetTracker()->PutTabInWindow(kTag, 1, 5); |
| ASSERT_EQ(2U, session1->windows.size()); |
| ASSERT_EQ(2U, session1->windows[0]->wrapped_window.tabs.size()); |
| ASSERT_EQ(2U, session1->windows[1]->wrapped_window.tabs.size()); |
| ASSERT_EQ(6U, GetTracker()->num_synced_tabs(kTag)); |
| |
| // Create a session that should not be affected. |
| SyncedSession* session2 = GetTracker()->GetSession(kTag2); |
| GetTracker()->PutWindowInSession(kTag2, 2); |
| GetTracker()->PutTabInWindow(kTag2, 2, 1); |
| ASSERT_EQ(1U, session2->windows.size()); |
| ASSERT_EQ(1U, session2->windows[2]->wrapped_window.tabs.size()); |
| ASSERT_EQ(1U, GetTracker()->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 |
| // first window (including its one remaining tab), and opening a new tab |
| // on the remaining window. |
| |
| // New tab, arrived before meta node so unmapped. |
| GetTracker()->GetTab(kTag, 6); |
| GetTracker()->ResetSessionTracking(kTag); |
| GetTracker()->PutWindowInSession(kTag, 0); |
| GetTracker()->PutTabInWindow(kTag, 0, 0); |
| // Tab 1 is closed. |
| GetTracker()->PutTabInWindow(kTag, 0, 2); // No longer unmapped. |
| // Tab 3 was unmapped and does not get used. |
| GetTracker()->PutTabInWindow(kTag, 0, 4); // Moved from window 1. |
| // Window 1 was closed, along with tab 5. |
| GetTracker()->PutTabInWindow(kTag, 0, 6); // No longer unmapped. |
| // Session 2 should not be affected. |
| GetTracker()->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[0]->wrapped_window.tabs.size()); |
| ASSERT_EQ(1U, session2->windows.size()); |
| ASSERT_EQ(1U, session2->windows[2]->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_TRUE(VerifyTabIntegrity(kTag)); |
| |
| // All memory should be properly deallocated by destructor for the |
| // SyncedSessionTracker. |
| } |
| |
| TEST_F(SyncedSessionTrackerTest, DeleteForeignTab) { |
| int tab_node_id_1 = 1; |
| int tab_node_id_2 = 2; |
| std::set<int> result; |
| |
| GetTracker()->OnTabNodeSeen(kTag, tab_node_id_1); |
| GetTracker()->OnTabNodeSeen(kTag, tab_node_id_2); |
| |
| GetTracker()->LookupForeignTabNodeIds(kTag, &result); |
| EXPECT_EQ(2U, result.size()); |
| EXPECT_TRUE(result.find(tab_node_id_1) != result.end()); |
| EXPECT_TRUE(result.find(tab_node_id_2) != result.end()); |
| |
| GetTracker()->DeleteForeignTab(kTag, tab_node_id_1); |
| GetTracker()->LookupForeignTabNodeIds(kTag, &result); |
| EXPECT_EQ(1U, result.size()); |
| EXPECT_TRUE(result.find(tab_node_id_2) != result.end()); |
| |
| 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; |
| |
| GetTracker()->SetLocalSessionTag(kTag); |
| |
| // Start with two restored tab nodes. |
| GetTracker()->ReassociateLocalTab(kTabNode1, kTab1); |
| GetTracker()->ReassociateLocalTab(kTabNode2, kTab2); |
| EXPECT_TRUE(GetTabNodePool()->Empty()); |
| EXPECT_FALSE(GetTabNodePool()->Full()); |
| EXPECT_EQ(2U, GetTabNodePool()->Capacity()); |
| |
| // 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()); |
| EXPECT_TRUE(GetTabNodePool()->Full()); |
| |
| // 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_TRUE(GetTracker()->GetTabNodeFromLocalTabId(kTab1, &tab_node_id)); |
| GetTracker()->CleanupLocalTabs(&free_node_ids); |
| EXPECT_TRUE(free_node_ids.empty()); |
| |
| // TabNodePool should have one free tab node and one used. |
| EXPECT_EQ(2U, GetTabNodePool()->Capacity()); |
| EXPECT_FALSE(GetTabNodePool()->Empty()); |
| EXPECT_FALSE(GetTabNodePool()->Full()); |
| |
| // Simulate a tab opening, which should use the last free tab node. |
| EXPECT_TRUE(GetTracker()->GetTabNodeFromLocalTabId(kTab2, &tab_node_id)); |
| EXPECT_TRUE(GetTabNodePool()->Empty()); |
| |
| // Simulate another tab opening, which should create a new associated tab |
| // node. |
| EXPECT_FALSE(GetTracker()->GetTabNodeFromLocalTabId(kTab3, &tab_node_id)); |
| EXPECT_EQ(kTabNode3, tab_node_id); |
| EXPECT_EQ(3U, GetTabNodePool()->Capacity()); |
| EXPECT_TRUE(GetTabNodePool()->Empty()); |
| |
| // Fetching the same tab should return the same tab node id. |
| EXPECT_TRUE(GetTracker()->GetTabNodeFromLocalTabId(kTab3, &tab_node_id)); |
| EXPECT_EQ(kTabNode3, tab_node_id); |
| EXPECT_TRUE(GetTabNodePool()->Empty()); |
| |
| // 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()); |
| EXPECT_TRUE(GetTabNodePool()->Full()); |
| EXPECT_FALSE(GetTabNodePool()->Empty()); |
| ASSERT_TRUE(VerifyTabIntegrity(kTag)); |
| } |
| |
| TEST_F(SyncedSessionTrackerTest, ReassociateTabMapped) { |
| 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 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); |
| 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()); |
| |
| // Then reassociate with a new tab id. |
| GetTracker()->ReassociateLocalTab(kTabNode1, kTab2); |
| ASSERT_TRUE(VerifyTabIntegrity(kTag)); |
| EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1)); |
| EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2)); |
| EXPECT_FALSE(GetTracker()->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); |
| ASSERT_TRUE(VerifyTabIntegrity(kTag)); |
| EXPECT_TRUE(free_node_ids.empty()); |
| 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(session->tab_node_ids.size(), |
| session->tab_node_ids.count(kTabNode1)); |
| ASSERT_EQ(1U, GetTabNodePool()->Capacity()); |
| ASSERT_TRUE(VerifyTabIntegrity(kTag)); |
| } |
| |
| TEST_F(SyncedSessionTrackerTest, ReassociateTabMappedTwice) { |
| 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 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); |
| ASSERT_TRUE(VerifyTabIntegrity(kTag)); |
| EXPECT_TRUE(free_node_ids.empty()); |
| 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()); |
| EXPECT_EQ(GetTracker()->GetTab(kTag, kTab1), |
| session->windows[kWindow1]->wrapped_window.tabs[0].get()); |
| |
| // Then reassociate with a new tab id. |
| GetTracker()->ReassociateLocalTab(kTabNode1, kTab2); |
| ASSERT_TRUE(VerifyTabIntegrity(kTag)); |
| EXPECT_TRUE(GetTracker()->IsLocalTabNodeAssociated(kTabNode1)); |
| EXPECT_FALSE(GetTracker()->IsTabUnmappedForTesting(kTab2)); |
| EXPECT_FALSE(GetTracker()->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. |
| const sessions::SessionTab* tab_ptr = nullptr; |
| EXPECT_FALSE(GetTracker()->LookupSessionTab(kTag, kTab1, &tab_ptr)); |
| |
| // 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); |
| ASSERT_TRUE(VerifyTabIntegrity(kTag)); |
| EXPECT_TRUE(free_node_ids.empty()); |
| EXPECT_FALSE(GetTracker()->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), |
| session->windows[kWindow1]->wrapped_window.tabs[1].get()); |
| EXPECT_EQ(session->tab_node_ids.size(), |
| session->tab_node_ids.count(kTabNode1)); |
| EXPECT_EQ(1U, GetTabNodePool()->Capacity()); |
| |
| // Attempting to access the original tab will create a new SessionTab object. |
| EXPECT_NE(GetTracker()->GetTab(kTag, kTab1), |
| 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) { |
| std::set<int> free_node_ids; |
| |
| // First create the old tab in an unmapped state. |
| 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, but reassociated with a new tab id. |
| GetTracker()->ResetSessionTracking(kTag); |
| 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)); |
| |
| // Now that it's been mapped, it should be accessible both via the |
| // GetSession as well as GetTab. |
| SyncedSession* session = GetTracker()->GetSession(kTag); |
| 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(kTabNode1)); |
| ASSERT_EQ(1U, GetTabNodePool()->Capacity()); |
| ASSERT_TRUE(VerifyTabIntegrity(kTag)); |
| } |
| |
| 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(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(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(kTabNode1, kTab2); |
| ASSERT_TRUE(VerifyTabIntegrity(kTag)); |
| EXPECT_TRUE(free_node_ids.empty()); |
| 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 GetTab. |
| SyncedSession* session = GetTracker()->GetSession(kTag); |
| 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(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 |