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