1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <string> 6#include <vector> 7 8#include "base/memory/scoped_ptr.h" 9#include "base/rand_util.h" 10#include "base/strings/stringprintf.h" 11#include "base/strings/utf_string_conversions.h" 12#include "chrome/browser/sessions/session_types.h" 13#include "chrome/browser/sync/glue/synced_session_tracker.h" 14#include "components/sessions/serialized_navigation_entry_test_helper.h" 15#include "testing/gtest/include/gtest/gtest.h" 16 17namespace browser_sync { 18 19typedef testing::Test SyncedSessionTrackerTest; 20 21TEST_F(SyncedSessionTrackerTest, GetSession) { 22 SyncedSessionTracker tracker; 23 SyncedSession* session1 = tracker.GetSession("tag"); 24 SyncedSession* session2 = tracker.GetSession("tag2"); 25 ASSERT_EQ(session1, tracker.GetSession("tag")); 26 ASSERT_NE(session1, session2); 27 // Should clean up memory on its own. 28} 29 30TEST_F(SyncedSessionTrackerTest, GetTabUnmapped) { 31 SyncedSessionTracker tracker; 32 SessionTab* tab = tracker.GetTab("tag", 0, 0); 33 ASSERT_EQ(tab, tracker.GetTab("tag", 0, 0)); 34 // Should clean up memory on its own. 35} 36 37TEST_F(SyncedSessionTrackerTest, PutWindowInSession) { 38 SyncedSessionTracker tracker; 39 tracker.PutWindowInSession("tag", 0); 40 SyncedSession* session = tracker.GetSession("tag"); 41 ASSERT_EQ(1U, session->windows.size()); 42 // Should clean up memory on its own. 43} 44 45TEST_F(SyncedSessionTrackerTest, PutTabInWindow) { 46 SyncedSessionTracker tracker; 47 tracker.PutWindowInSession("tag", 10); 48 tracker.PutTabInWindow("tag", 10, 15, 0); // win id 10, tab id 15, tab ind 0. 49 SyncedSession* session = tracker.GetSession("tag"); 50 ASSERT_EQ(1U, session->windows.size()); 51 ASSERT_EQ(1U, session->windows[10]->tabs.size()); 52 ASSERT_EQ(tracker.GetTab("tag", 15, 1), session->windows[10]->tabs[0]); 53 // Should clean up memory on its own. 54} 55 56TEST_F(SyncedSessionTrackerTest, LookupAllForeignSessions) { 57 SyncedSessionTracker tracker; 58 std::vector<const SyncedSession*> sessions; 59 ASSERT_FALSE(tracker.LookupAllForeignSessions(&sessions)); 60 tracker.GetSession("tag1"); 61 tracker.GetSession("tag2"); 62 tracker.PutWindowInSession("tag1", 0); 63 tracker.PutTabInWindow("tag1", 0, 15, 0); 64 SessionTab* tab = tracker.GetTab("tag1", 15, 1); 65 ASSERT_TRUE(tab); 66 tab->navigations.push_back( 67 sessions::SerializedNavigationEntryTestHelper::CreateNavigation( 68 "bla://valid_url", "title")); 69 ASSERT_TRUE(tracker.LookupAllForeignSessions(&sessions)); 70 // Only the session with a valid window and tab gets returned. 71 ASSERT_EQ(1U, sessions.size()); 72 ASSERT_EQ("tag1", sessions[0]->session_tag); 73} 74 75TEST_F(SyncedSessionTrackerTest, LookupSessionWindows) { 76 SyncedSessionTracker tracker; 77 std::vector<const SessionWindow*> windows; 78 ASSERT_FALSE(tracker.LookupSessionWindows("tag1", &windows)); 79 tracker.GetSession("tag1"); 80 tracker.PutWindowInSession("tag1", 0); 81 tracker.PutWindowInSession("tag1", 2); 82 tracker.GetSession("tag2"); 83 tracker.PutWindowInSession("tag2", 0); 84 tracker.PutWindowInSession("tag2", 2); 85 ASSERT_TRUE(tracker.LookupSessionWindows("tag1", &windows)); 86 ASSERT_EQ(2U, windows.size()); // Only windows from tag1 session. 87 ASSERT_NE((SessionWindow*)NULL, windows[0]); 88 ASSERT_NE((SessionWindow*)NULL, windows[1]); 89 ASSERT_NE(windows[1], windows[0]); 90} 91 92TEST_F(SyncedSessionTrackerTest, LookupSessionTab) { 93 SyncedSessionTracker tracker; 94 const SessionTab* tab; 95 ASSERT_FALSE(tracker.LookupSessionTab("tag1", 5, &tab)); 96 tracker.GetSession("tag1"); 97 tracker.PutWindowInSession("tag1", 0); 98 tracker.PutTabInWindow("tag1", 0, 5, 0); 99 ASSERT_TRUE(tracker.LookupSessionTab("tag1", 5, &tab)); 100 ASSERT_NE((SessionTab*)NULL, tab); 101} 102 103TEST_F(SyncedSessionTrackerTest, Complex) { 104 const std::string tag1 = "tag"; 105 const std::string tag2 = "tag2"; 106 const std::string tag3 = "tag3"; 107 SyncedSessionTracker tracker; 108 std::vector<SessionTab*> tabs1, tabs2; 109 SessionTab* temp_tab; 110 ASSERT_TRUE(tracker.Empty()); 111 ASSERT_EQ(0U, tracker.num_synced_sessions()); 112 ASSERT_EQ(0U, tracker.num_synced_tabs(tag1)); 113 tabs1.push_back(tracker.GetTab(tag1, 0, 0)); 114 tabs1.push_back(tracker.GetTab(tag1, 1, 1)); 115 tabs1.push_back(tracker.GetTab(tag1, 2, 2)); 116 ASSERT_EQ(3U, tracker.num_synced_tabs(tag1)); 117 ASSERT_EQ(0U, tracker.num_synced_sessions()); 118 temp_tab = tracker.GetTab(tag1, 0, 0); // Already created. 119 ASSERT_EQ(3U, tracker.num_synced_tabs(tag1)); 120 ASSERT_EQ(0U, tracker.num_synced_sessions()); 121 ASSERT_EQ(tabs1[0], temp_tab); 122 tabs2.push_back(tracker.GetTab(tag2, 0, 0)); 123 ASSERT_EQ(1U, tracker.num_synced_tabs(tag2)); 124 ASSERT_EQ(0U, tracker.num_synced_sessions()); 125 ASSERT_FALSE(tracker.DeleteSession(tag3)); 126 127 SyncedSession* session = tracker.GetSession(tag1); 128 SyncedSession* session2 = tracker.GetSession(tag2); 129 SyncedSession* session3 = tracker.GetSession(tag3); 130 ASSERT_EQ(3U, tracker.num_synced_sessions()); 131 132 ASSERT_TRUE(session); 133 ASSERT_TRUE(session2); 134 ASSERT_TRUE(session3); 135 ASSERT_NE(session, session2); 136 ASSERT_NE(session2, session3); 137 ASSERT_TRUE(tracker.DeleteSession(tag3)); 138 ASSERT_EQ(2U, tracker.num_synced_sessions()); 139 140 tracker.PutWindowInSession(tag1, 0); // Create a window. 141 tracker.PutTabInWindow(tag1, 0, 2, 0); // No longer unmapped. 142 ASSERT_EQ(3U, tracker.num_synced_tabs(tag1)); // Has not changed. 143 144 const SessionTab *tab_ptr; 145 ASSERT_TRUE(tracker.LookupSessionTab(tag1, 0, &tab_ptr)); 146 ASSERT_EQ(tab_ptr, tabs1[0]); 147 ASSERT_TRUE(tracker.LookupSessionTab(tag1, 2, &tab_ptr)); 148 ASSERT_EQ(tab_ptr, tabs1[2]); 149 ASSERT_FALSE(tracker.LookupSessionTab(tag1, 3, &tab_ptr)); 150 ASSERT_EQ(static_cast<const SessionTab*>(NULL), tab_ptr); 151 152 std::vector<const SessionWindow*> windows; 153 ASSERT_TRUE(tracker.LookupSessionWindows(tag1, &windows)); 154 ASSERT_EQ(1U, windows.size()); 155 ASSERT_TRUE(tracker.LookupSessionWindows(tag2, &windows)); 156 ASSERT_EQ(0U, windows.size()); 157 158 // The sessions don't have valid tabs, lookup should not succeed. 159 std::vector<const SyncedSession*> sessions; 160 ASSERT_FALSE(tracker.LookupAllForeignSessions(&sessions)); 161 162 tracker.Clear(); 163 ASSERT_EQ(0U, tracker.num_synced_tabs(tag1)); 164 ASSERT_EQ(0U, tracker.num_synced_tabs(tag2)); 165 ASSERT_EQ(0U, tracker.num_synced_sessions()); 166} 167 168TEST_F(SyncedSessionTrackerTest, ManyGetTabs) { 169 SyncedSessionTracker tracker; 170 ASSERT_TRUE(tracker.Empty()); 171 const int kMaxSessions = 10; 172 const int kMaxTabs = 1000; 173 const int kMaxAttempts = 10000; 174 for (int j=0; j<kMaxSessions; ++j) { 175 std::string tag = base::StringPrintf("tag%d", j); 176 for (int i=0; i<kMaxAttempts; ++i) { 177 // More attempts than tabs means we'll sometimes get the same tabs, 178 // sometimes have to allocate new tabs. 179 int rand_tab_num = base::RandInt(0, kMaxTabs); 180 SessionTab* tab = tracker.GetTab(tag, rand_tab_num, rand_tab_num + 1); 181 ASSERT_TRUE(tab); 182 } 183 } 184} 185 186TEST_F(SyncedSessionTrackerTest, LookupTabNodeIds) { 187 SyncedSessionTracker tracker; 188 std::set<int> result; 189 std::string tag1 = "session1"; 190 std::string tag2 = "session2"; 191 std::string tag3 = "session3"; 192 193 tracker.GetTab(tag1, 1, 1); 194 tracker.GetTab(tag1, 2, 2); 195 EXPECT_TRUE(tracker.LookupTabNodeIds(tag1, &result)); 196 EXPECT_EQ(2U, result.size()); 197 EXPECT_FALSE(result.end() == result.find(1)); 198 EXPECT_FALSE(result.end() == result.find(2)); 199 EXPECT_FALSE(tracker.LookupTabNodeIds(tag2, &result)); 200 201 tracker.PutWindowInSession(tag1, 0); 202 tracker.PutTabInWindow(tag1, 0, 3, 0); 203 EXPECT_TRUE(tracker.LookupTabNodeIds(tag1, &result)); 204 EXPECT_EQ(2U, result.size()); 205 206 tracker.GetTab(tag1, 3, 3); 207 EXPECT_TRUE(tracker.LookupTabNodeIds(tag1, &result)); 208 EXPECT_EQ(3U, result.size()); 209 EXPECT_FALSE(result.end() == result.find(3)); 210 211 tracker.GetTab(tag2, 1, 21); 212 tracker.GetTab(tag2, 2, 22); 213 EXPECT_TRUE(tracker.LookupTabNodeIds(tag2, &result)); 214 EXPECT_EQ(2U, result.size()); 215 EXPECT_FALSE(result.end() == result.find(21)); 216 EXPECT_FALSE(result.end() == result.find(22)); 217 EXPECT_TRUE(tracker.LookupTabNodeIds(tag1, &result)); 218 EXPECT_EQ(3U, result.size()); 219 EXPECT_FALSE(result.end() == result.find(1)); 220 EXPECT_FALSE(result.end() == result.find(2)); 221 222 EXPECT_FALSE(tracker.LookupTabNodeIds(tag3, &result)); 223 tracker.PutWindowInSession(tag3, 1); 224 tracker.PutTabInWindow(tag3, 1, 5, 0); 225 EXPECT_TRUE(tracker.LookupTabNodeIds(tag3, &result)); 226 EXPECT_TRUE(result.empty()); 227 EXPECT_TRUE(tracker.DeleteSession(tag3)); 228 EXPECT_FALSE(tracker.LookupTabNodeIds(tag3, &result)); 229 230 EXPECT_TRUE(tracker.DeleteSession(tag1)); 231 EXPECT_FALSE(tracker.LookupTabNodeIds(tag1, &result)); 232 EXPECT_TRUE(tracker.LookupTabNodeIds(tag2, &result)); 233 EXPECT_EQ(2U, result.size()); 234 EXPECT_FALSE(result.end() == result.find(21)); 235 EXPECT_FALSE(result.end() == result.find(22)); 236 EXPECT_TRUE(tracker.DeleteSession(tag2)); 237 EXPECT_FALSE(tracker.LookupTabNodeIds(tag2, &result)); 238} 239 240TEST_F(SyncedSessionTrackerTest, SessionTracking) { 241 SyncedSessionTracker tracker; 242 ASSERT_TRUE(tracker.Empty()); 243 std::string tag1 = "tag1"; 244 std::string tag2 = "tag2"; 245 246 // Create some session information that is stale. 247 SyncedSession* session1= tracker.GetSession(tag1); 248 tracker.PutWindowInSession(tag1, 0); 249 tracker.PutTabInWindow(tag1, 0, 0, 0); 250 tracker.PutTabInWindow(tag1, 0, 1, 1); 251 tracker.GetTab(tag1, 2, 3U)->window_id.set_id(0); // Will be unmapped. 252 tracker.GetTab(tag1, 3, 4U)->window_id.set_id(0); // Will be unmapped. 253 tracker.PutWindowInSession(tag1, 1); 254 tracker.PutTabInWindow(tag1, 1, 4, 0); 255 tracker.PutTabInWindow(tag1, 1, 5, 1); 256 ASSERT_EQ(2U, session1->windows.size()); 257 ASSERT_EQ(2U, session1->windows[0]->tabs.size()); 258 ASSERT_EQ(2U, session1->windows[1]->tabs.size()); 259 ASSERT_EQ(6U, tracker.num_synced_tabs(tag1)); 260 261 // Create a session that should not be affected. 262 SyncedSession* session2 = tracker.GetSession(tag2); 263 tracker.PutWindowInSession(tag2, 2); 264 tracker.PutTabInWindow(tag2, 2, 1, 0); 265 ASSERT_EQ(1U, session2->windows.size()); 266 ASSERT_EQ(1U, session2->windows[2]->tabs.size()); 267 ASSERT_EQ(1U, tracker.num_synced_tabs(tag2)); 268 269 // Reset tracking and get the current windows/tabs. 270 // We simulate moving a tab from one window to another, then closing the 271 // first window (including its one remaining tab), and opening a new tab 272 // on the remaining window. 273 274 // New tab, arrived before meta node so unmapped. 275 tracker.GetTab(tag1, 6, 7U); 276 tracker.ResetSessionTracking(tag1); 277 tracker.PutWindowInSession(tag1, 0); 278 tracker.PutTabInWindow(tag1, 0, 0, 0); 279 // Tab 1 is closed. 280 tracker.PutTabInWindow(tag1, 0, 2, 1); // No longer unmapped. 281 // Tab 3 was unmapped and does not get used. 282 tracker.PutTabInWindow(tag1, 0, 4, 2); // Moved from window 1. 283 // Window 1 was closed, along with tab 5. 284 tracker.PutTabInWindow(tag1, 0, 6, 3); // No longer unmapped. 285 // Session 2 should not be affected. 286 tracker.CleanupSession(tag1); 287 288 // Verify that only those parts of the session not owned have been removed. 289 ASSERT_EQ(1U, session1->windows.size()); 290 ASSERT_EQ(4U, session1->windows[0]->tabs.size()); 291 ASSERT_EQ(1U, session2->windows.size()); 292 ASSERT_EQ(1U, session2->windows[2]->tabs.size()); 293 ASSERT_EQ(2U, tracker.num_synced_sessions()); 294 ASSERT_EQ(4U, tracker.num_synced_tabs(tag1)); 295 ASSERT_EQ(1U, tracker.num_synced_tabs(tag2)); 296 297 // All memory should be properly deallocated by destructor for the 298 // SyncedSessionTracker. 299} 300 301} // namespace browser_sync 302