synced_session_tracker_unittest.cc 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075
  1. // Copyright 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. #include "components/sync_sessions/synced_session_tracker.h"
  5. #include "base/rand_util.h"
  6. #include "base/strings/stringprintf.h"
  7. #include "base/test/mock_callback.h"
  8. #include "components/sessions/core/serialized_navigation_entry_test_helper.h"
  9. #include "components/sync/protocol/sync_enums.pb.h"
  10. #include "components/sync_sessions/mock_sync_sessions_client.h"
  11. #include "components/sync_sessions/test_matchers.h"
  12. #include "testing/gtest/include/gtest/gtest.h"
  13. using testing::_;
  14. using testing::AssertionFailure;
  15. using testing::AssertionResult;
  16. using testing::AssertionSuccess;
  17. using testing::ElementsAre;
  18. using testing::IsEmpty;
  19. using testing::IsNull;
  20. using testing::Ne;
  21. using testing::NotNull;
  22. using testing::Pointee;
  23. using testing::Return;
  24. namespace sync_sessions {
  25. namespace {
  26. const char kValidUrl[] = "http://www.example.com";
  27. const char kSessionName[] = "sessionname";
  28. const sync_pb::SyncEnums::DeviceType kDeviceType =
  29. sync_pb::SyncEnums_DeviceType_TYPE_PHONE;
  30. const char kTag[] = "tag";
  31. const char kTag2[] = "tag2";
  32. const char kTag3[] = "tag3";
  33. const int kTabNode1 = 0;
  34. const int kTabNode2 = 1;
  35. const int kTabNode3 = 2;
  36. const int kTabNode4 = 3;
  37. const int kTabNode5 = 4;
  38. const SessionID kWindow1 = SessionID::FromSerializedValue(1);
  39. const SessionID kWindow2 = SessionID::FromSerializedValue(2);
  40. const SessionID kWindow3 = SessionID::FromSerializedValue(3);
  41. const SessionID kTab1 = SessionID::FromSerializedValue(15);
  42. const SessionID kTab2 = SessionID::FromSerializedValue(25);
  43. const SessionID kTab3 = SessionID::FromSerializedValue(35);
  44. const SessionID kTab4 = SessionID::FromSerializedValue(45);
  45. const SessionID kTab5 = SessionID::FromSerializedValue(55);
  46. const SessionID kTab6 = SessionID::FromSerializedValue(65);
  47. const SessionID kTab7 = SessionID::FromSerializedValue(75);
  48. MATCHER_P(HasSessionTag, expected_tag, "") {
  49. return arg->session_tag == expected_tag;
  50. }
  51. } // namespace
  52. class SyncedSessionTrackerTest : public testing::Test {
  53. public:
  54. SyncedSessionTrackerTest() : tracker_(&sessions_client_) {}
  55. ~SyncedSessionTrackerTest() override = default;
  56. TabNodePool* GetLocalTabNodePool() {
  57. return &tracker_.LookupTrackedSession(tracker_.local_session_tag_)
  58. ->tab_node_pool;
  59. }
  60. // Returns whether |tab_node_id| refers to a valid tab node that is associated
  61. // with a tab.
  62. bool IsLocalTabNodeAssociated(int tab_node_id) const {
  63. return tracker_
  64. .LookupTabIdFromTabNodeId(tracker_.local_session_tag_, tab_node_id)
  65. .is_valid();
  66. }
  67. // Verify that each tab within a session is allocated one SessionTab object,
  68. // and that that tab object is owned either by the Session itself or the
  69. // |unmapped_tabs_| tab holder.
  70. AssertionResult VerifyTabIntegrity(const std::string& session_tag) {
  71. const SyncedSessionTracker::TrackedSession* session =
  72. tracker_.LookupTrackedSession(session_tag);
  73. if (!session) {
  74. return AssertionFailure()
  75. << "Not tracked session with tag " << session_tag;
  76. }
  77. // First get all the tabs associated with this session.
  78. int total_tab_count = session->synced_tab_map.size();
  79. // Now traverse the SyncedSession tree to verify the mapped tabs all match
  80. // up.
  81. int mapped_tab_count = 0;
  82. for (auto& [window_id, window] : session->synced_session.windows) {
  83. mapped_tab_count += window->wrapped_window.tabs.size();
  84. for (auto& tab : window->wrapped_window.tabs) {
  85. const auto tab_map_it = session->synced_tab_map.find(tab->tab_id);
  86. if (tab_map_it == session->synced_tab_map.end()) {
  87. return AssertionFailure() << "Tab ID " << tab->tab_id.id()
  88. << " has no corresponding synced tab entry";
  89. }
  90. if (tab_map_it->second != tab.get()) {
  91. return AssertionFailure()
  92. << "Mapped tab " << tab->tab_id.id()
  93. << " does not match synced tab map " << tab->tab_id.id();
  94. }
  95. }
  96. }
  97. // Wrap up by verifying all unmapped tabs are tracked.
  98. int unmapped_tab_count = session->unmapped_tabs.size();
  99. for (const auto& [id, tab] : session->unmapped_tabs) {
  100. if (id != tab->tab_id) {
  101. return AssertionFailure() << "Unmapped tab " << tab->tab_id.id()
  102. << " associated with wrong tab " << id;
  103. }
  104. const auto tab_map_it = session->synced_tab_map.find(tab->tab_id);
  105. if (tab_map_it == session->synced_tab_map.end()) {
  106. return AssertionFailure() << "Unmapped tab " << tab->tab_id
  107. << " has no corresponding synced tab entry";
  108. }
  109. if (tab_map_it->second != tab.get()) {
  110. return AssertionFailure()
  111. << "Unmapped tab " << tab->tab_id.id()
  112. << " does not match synced tab map " << tab_map_it->second;
  113. }
  114. }
  115. return mapped_tab_count + unmapped_tab_count == total_tab_count
  116. ? AssertionSuccess()
  117. : AssertionFailure()
  118. << " Tab count mismatch. Total: " << total_tab_count
  119. << ". Mapped + Unmapped: " << mapped_tab_count << " + "
  120. << unmapped_tab_count;
  121. }
  122. protected:
  123. testing::NiceMock<MockSyncSessionsClient> sessions_client_;
  124. testing::NiceMock<
  125. base::MockCallback<base::RepeatingCallback<bool(int /*tab_node_id*/)>>>
  126. is_tab_node_unsynced_cb_;
  127. SyncedSessionTracker tracker_;
  128. };
  129. TEST_F(SyncedSessionTrackerTest, GetSession) {
  130. SyncedSession* session1 = tracker_.GetSession(kTag);
  131. SyncedSession* session2 = tracker_.GetSession(kTag2);
  132. ASSERT_EQ(session1, tracker_.LookupSession(kTag));
  133. ASSERT_EQ(session1, tracker_.GetSession(kTag));
  134. ASSERT_NE(session1, session2);
  135. // Should clean up memory on its own.
  136. }
  137. TEST_F(SyncedSessionTrackerTest, GetTabUnmapped) {
  138. sessions::SessionTab* tab = tracker_.GetTab(kTag, kTab1);
  139. ASSERT_EQ(tab, tracker_.GetTab(kTag, kTab1));
  140. // Should clean up memory on its own.
  141. }
  142. TEST_F(SyncedSessionTrackerTest, PutWindowInSession) {
  143. tracker_.PutWindowInSession(kTag, kWindow1);
  144. const SyncedSession* session = tracker_.LookupSession(kTag);
  145. ASSERT_EQ(1U, session->windows.size());
  146. // Doing it again should have no effect.
  147. tracker_.PutWindowInSession(kTag, kWindow1);
  148. ASSERT_EQ(1U, session->windows.size());
  149. // Should clean up memory on its own.
  150. }
  151. TEST_F(SyncedSessionTrackerTest, PutTabInWindow) {
  152. tracker_.PutWindowInSession(kTag, kWindow1);
  153. tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
  154. const SyncedSession* session = tracker_.LookupSession(kTag);
  155. ASSERT_EQ(1U, session->windows.size());
  156. ASSERT_EQ(1U, session->windows.at(kWindow1)->wrapped_window.tabs.size());
  157. ASSERT_EQ(tracker_.GetTab(kTag, kTab1),
  158. session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
  159. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  160. // Should clean up memory on its own.
  161. }
  162. TEST_F(SyncedSessionTrackerTest, LookupAllSessions) {
  163. EXPECT_THAT(tracker_.LookupAllSessions(SyncedSessionTracker::PRESENTABLE),
  164. IsEmpty());
  165. tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
  166. tracker_.PutWindowInSession(kTag, kWindow1);
  167. tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
  168. EXPECT_THAT(tracker_.LookupAllSessions(SyncedSessionTracker::RAW),
  169. ElementsAre(HasSessionTag(kTag)));
  170. EXPECT_THAT(tracker_.LookupAllSessions(SyncedSessionTracker::PRESENTABLE),
  171. IsEmpty());
  172. sessions::SessionTab* tab = tracker_.GetTab(kTag, kTab1);
  173. ASSERT_TRUE(tab);
  174. tab->navigations.push_back(
  175. sessions::SerializedNavigationEntryTestHelper::CreateNavigationForTest());
  176. EXPECT_THAT(tracker_.LookupAllSessions(SyncedSessionTracker::PRESENTABLE),
  177. ElementsAre(HasSessionTag(kTag)));
  178. tracker_.GetSession(kTag2);
  179. tracker_.PutWindowInSession(kTag2, kWindow1);
  180. tracker_.PutTabInWindow(kTag2, kWindow1, kTab2);
  181. sessions::SessionTab* tab2 = tracker_.GetTab(kTag2, kTab2);
  182. ASSERT_TRUE(tab2);
  183. tab2->navigations.push_back(
  184. sessions::SerializedNavigationEntryTestHelper::CreateNavigationForTest());
  185. EXPECT_THAT(tracker_.LookupAllSessions(SyncedSessionTracker::PRESENTABLE),
  186. ElementsAre(HasSessionTag(kTag), HasSessionTag(kTag2)));
  187. }
  188. TEST_F(SyncedSessionTrackerTest, LookupAllForeignSessions) {
  189. const char kInvalidUrl[] = "invalid.url";
  190. ON_CALL(sessions_client_, ShouldSyncURL(GURL(kInvalidUrl)))
  191. .WillByDefault(testing::Return(false));
  192. EXPECT_THAT(
  193. tracker_.LookupAllForeignSessions(SyncedSessionTracker::PRESENTABLE),
  194. IsEmpty());
  195. tracker_.GetSession(kTag);
  196. tracker_.PutWindowInSession(kTag, kWindow1);
  197. tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
  198. sessions::SessionTab* tab = tracker_.GetTab(kTag, kTab1);
  199. ASSERT_TRUE(tab);
  200. tab->navigations.push_back(
  201. sessions::SerializedNavigationEntryTestHelper::CreateNavigationForTest());
  202. tab->navigations.back().set_virtual_url(GURL(kValidUrl));
  203. tracker_.GetSession(kTag2);
  204. tracker_.GetSession(kTag3);
  205. tracker_.PutWindowInSession(kTag3, kWindow1);
  206. tracker_.PutTabInWindow(kTag3, kWindow1, kTab1);
  207. tab = tracker_.GetTab(kTag3, kTab1);
  208. ASSERT_TRUE(tab);
  209. tab->navigations.push_back(
  210. sessions::SerializedNavigationEntryTestHelper::CreateNavigationForTest());
  211. tab->navigations.back().set_virtual_url(GURL(kInvalidUrl));
  212. // Only the session with a valid window and tab gets returned.
  213. EXPECT_THAT(
  214. tracker_.LookupAllForeignSessions(SyncedSessionTracker::PRESENTABLE),
  215. ElementsAre(HasSessionTag(kTag)));
  216. EXPECT_THAT(tracker_.LookupAllForeignSessions(SyncedSessionTracker::RAW),
  217. ElementsAre(HasSessionTag(kTag), HasSessionTag(kTag2),
  218. HasSessionTag(kTag3)));
  219. // Annotate kTag as local session.
  220. ON_CALL(sessions_client_, IsRecentLocalCacheGuid(kTag))
  221. .WillByDefault(Return(true));
  222. EXPECT_THAT(
  223. tracker_.LookupAllForeignSessions(SyncedSessionTracker::PRESENTABLE),
  224. IsEmpty());
  225. EXPECT_THAT(tracker_.LookupAllForeignSessions(SyncedSessionTracker::RAW),
  226. ElementsAre(HasSessionTag(kTag2), HasSessionTag(kTag3)));
  227. }
  228. TEST_F(SyncedSessionTrackerTest, LookupSessionWindows) {
  229. std::vector<const sessions::SessionWindow*> windows;
  230. ASSERT_FALSE(tracker_.LookupSessionWindows(kTag, &windows));
  231. tracker_.GetSession(kTag);
  232. tracker_.PutWindowInSession(kTag, kWindow1);
  233. tracker_.PutWindowInSession(kTag, kWindow2);
  234. tracker_.GetSession(kTag2);
  235. tracker_.PutWindowInSession(kTag2, kWindow1);
  236. tracker_.PutWindowInSession(kTag2, kWindow2);
  237. ASSERT_TRUE(tracker_.LookupSessionWindows(kTag, &windows));
  238. ASSERT_EQ(2U, windows.size()); // Only windows from kTag session.
  239. ASSERT_NE((sessions::SessionWindow*)nullptr, windows[0]);
  240. ASSERT_NE((sessions::SessionWindow*)nullptr, windows[1]);
  241. ASSERT_NE(windows[1], windows[0]);
  242. }
  243. TEST_F(SyncedSessionTrackerTest, LookupSessionTab) {
  244. ASSERT_THAT(tracker_.LookupSessionTab(kTag, SessionID::InvalidValue()),
  245. IsNull());
  246. ASSERT_THAT(tracker_.LookupSessionTab(kTag, kTab1), IsNull());
  247. tracker_.GetSession(kTag);
  248. tracker_.PutWindowInSession(kTag, kWindow1);
  249. tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
  250. ASSERT_THAT(tracker_.LookupSessionTab(kTag, kTab1), NotNull());
  251. }
  252. TEST_F(SyncedSessionTrackerTest, Complex) {
  253. std::vector<sessions::SessionTab*> tabs1, tabs2;
  254. sessions::SessionTab* temp_tab;
  255. ASSERT_TRUE(tracker_.Empty());
  256. ASSERT_EQ(0U, tracker_.num_synced_sessions());
  257. ASSERT_EQ(0U, tracker_.num_synced_tabs(kTag));
  258. tabs1.push_back(tracker_.GetTab(kTag, kTab1));
  259. tabs1.push_back(tracker_.GetTab(kTag, kTab2));
  260. tabs1.push_back(tracker_.GetTab(kTag, kTab3));
  261. ASSERT_EQ(3U, tracker_.num_synced_tabs(kTag));
  262. ASSERT_EQ(1U, tracker_.num_synced_sessions());
  263. temp_tab = tracker_.GetTab(kTag, kTab1); // Already created.
  264. ASSERT_EQ(3U, tracker_.num_synced_tabs(kTag));
  265. ASSERT_EQ(1U, tracker_.num_synced_sessions());
  266. ASSERT_EQ(tabs1[0], temp_tab);
  267. tabs2.push_back(tracker_.GetTab(kTag2, kTab1));
  268. ASSERT_EQ(1U, tracker_.num_synced_tabs(kTag2));
  269. ASSERT_EQ(2U, tracker_.num_synced_sessions());
  270. ASSERT_FALSE(tracker_.DeleteForeignSession(kTag3));
  271. SyncedSession* session = tracker_.GetSession(kTag);
  272. ASSERT_EQ(2U, tracker_.num_synced_sessions());
  273. SyncedSession* session2 = tracker_.GetSession(kTag2);
  274. ASSERT_EQ(2U, tracker_.num_synced_sessions());
  275. SyncedSession* session3 = tracker_.GetSession(kTag3);
  276. session3->device_type = sync_pb::SyncEnums_DeviceType_TYPE_LINUX;
  277. ASSERT_EQ(3U, tracker_.num_synced_sessions());
  278. ASSERT_TRUE(session);
  279. ASSERT_TRUE(session2);
  280. ASSERT_TRUE(session3);
  281. ASSERT_NE(session, session2);
  282. ASSERT_NE(session2, session3);
  283. ASSERT_TRUE(tracker_.DeleteForeignSession(kTag3));
  284. ASSERT_EQ(2U, tracker_.num_synced_sessions());
  285. tracker_.PutWindowInSession(kTag, kWindow1); // Create a window.
  286. tracker_.PutTabInWindow(kTag, kWindow1, kTab3); // No longer unmapped.
  287. ASSERT_EQ(3U, tracker_.num_synced_tabs(kTag)); // Has not changed.
  288. ASSERT_EQ(tabs1[0], tracker_.LookupSessionTab(kTag, kTab1));
  289. ASSERT_EQ(tabs1[2], tracker_.LookupSessionTab(kTag, kTab3));
  290. ASSERT_THAT(tracker_.LookupSessionTab(kTag, kTab4), IsNull());
  291. std::vector<const sessions::SessionWindow*> windows;
  292. ASSERT_TRUE(tracker_.LookupSessionWindows(kTag, &windows));
  293. ASSERT_EQ(1U, windows.size());
  294. ASSERT_TRUE(tracker_.LookupSessionWindows(kTag2, &windows));
  295. ASSERT_EQ(0U, windows.size());
  296. // The sessions don't have valid tabs, lookup should not succeed.
  297. std::vector<const SyncedSession*> sessions;
  298. EXPECT_THAT(
  299. tracker_.LookupAllForeignSessions(SyncedSessionTracker::PRESENTABLE),
  300. IsEmpty());
  301. EXPECT_THAT(tracker_.LookupAllForeignSessions(SyncedSessionTracker::RAW),
  302. ElementsAre(HasSessionTag(kTag), HasSessionTag(kTag2)));
  303. tracker_.Clear();
  304. ASSERT_EQ(0U, tracker_.num_synced_tabs(kTag));
  305. ASSERT_EQ(0U, tracker_.num_synced_tabs(kTag2));
  306. ASSERT_EQ(0U, tracker_.num_synced_sessions());
  307. }
  308. TEST_F(SyncedSessionTrackerTest, ManyGetTabs) {
  309. ASSERT_TRUE(tracker_.Empty());
  310. const int kMaxSessions = 10;
  311. const int kMaxTabs = 1000;
  312. const int kMaxAttempts = 10000;
  313. for (int j = 0; j < kMaxSessions; ++j) {
  314. std::string tag = base::StringPrintf("tag%d", j);
  315. for (int i = 0; i < kMaxAttempts; ++i) {
  316. // More attempts than tabs means we'll sometimes get the same tabs,
  317. // sometimes have to allocate new tabs.
  318. int rand_tab_num = base::RandInt(0, kMaxTabs);
  319. sessions::SessionTab* tab = tracker_.GetTab(
  320. tag, SessionID::FromSerializedValue(rand_tab_num + 1));
  321. ASSERT_TRUE(tab);
  322. }
  323. }
  324. }
  325. TEST_F(SyncedSessionTrackerTest, LookupTabNodeIds) {
  326. tracker_.OnTabNodeSeen(kTag, 1, kTab1);
  327. tracker_.OnTabNodeSeen(kTag, 2, kTab2);
  328. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag), ElementsAre(1, 2));
  329. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag2), IsEmpty());
  330. tracker_.PutWindowInSession(kTag, kWindow1);
  331. tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
  332. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag), ElementsAre(1, 2));
  333. tracker_.OnTabNodeSeen(kTag, 3, kTab3);
  334. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag), ElementsAre(1, 2, 3));
  335. tracker_.OnTabNodeSeen(kTag2, 21, kTab4);
  336. tracker_.OnTabNodeSeen(kTag2, 22, kTab5);
  337. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag2), ElementsAre(21, 22));
  338. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag), ElementsAre(1, 2, 3));
  339. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag3), IsEmpty());
  340. tracker_.PutWindowInSession(kTag3, kWindow2);
  341. tracker_.PutTabInWindow(kTag3, kWindow2, kTab2);
  342. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag3), IsEmpty());
  343. EXPECT_FALSE(tracker_.DeleteForeignSession(kTag3));
  344. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag3), IsEmpty());
  345. EXPECT_FALSE(tracker_.DeleteForeignSession(kTag));
  346. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag), IsEmpty());
  347. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag2), ElementsAre(21, 22));
  348. tracker_.OnTabNodeSeen(kTag2, 21, kTab6);
  349. tracker_.OnTabNodeSeen(kTag2, 23, kTab7);
  350. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag2), ElementsAre(21, 22, 23));
  351. EXPECT_FALSE(tracker_.DeleteForeignSession(kTag2));
  352. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag2), IsEmpty());
  353. }
  354. TEST_F(SyncedSessionTrackerTest, SessionTracking) {
  355. ASSERT_TRUE(tracker_.Empty());
  356. // Create some session information that is stale.
  357. SyncedSession* session1 = tracker_.GetSession(kTag);
  358. tracker_.PutWindowInSession(kTag, kWindow1);
  359. tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
  360. tracker_.PutTabInWindow(kTag, kWindow1, kTab2);
  361. tracker_.GetTab(kTag, kTab3)->window_id =
  362. SessionID::FromSerializedValue(1); // Unmapped.
  363. tracker_.GetTab(kTag, kTab4)->window_id =
  364. SessionID::FromSerializedValue(1); // Unmapped.
  365. tracker_.PutWindowInSession(kTag, kWindow2);
  366. tracker_.PutTabInWindow(kTag, kWindow2, kTab5);
  367. tracker_.PutTabInWindow(kTag, kWindow2, kTab6);
  368. ASSERT_EQ(2U, session1->windows.size());
  369. ASSERT_EQ(2U, session1->windows[kWindow1]->wrapped_window.tabs.size());
  370. ASSERT_EQ(2U, session1->windows[kWindow2]->wrapped_window.tabs.size());
  371. ASSERT_EQ(6U, tracker_.num_synced_tabs(kTag));
  372. // Create a session that should not be affected.
  373. SyncedSession* session2 = tracker_.GetSession(kTag2);
  374. tracker_.PutWindowInSession(kTag2, kWindow3);
  375. tracker_.PutTabInWindow(kTag2, kWindow3, kTab2);
  376. ASSERT_EQ(1U, session2->windows.size());
  377. ASSERT_EQ(1U, session2->windows[kWindow3]->wrapped_window.tabs.size());
  378. ASSERT_EQ(1U, tracker_.num_synced_tabs(kTag2));
  379. // Reset tracking and get the current windows/tabs.
  380. // We simulate moving a tab from one window to another, then closing the
  381. // first window (including its one remaining tab), and opening a new tab
  382. // on the remaining window.
  383. // New tab, arrived before meta node so unmapped.
  384. tracker_.GetTab(kTag, kTab7);
  385. tracker_.ResetSessionTracking(kTag);
  386. tracker_.PutWindowInSession(kTag, kWindow1);
  387. tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
  388. // Tab 1 is closed.
  389. tracker_.PutTabInWindow(kTag, kWindow1, kTab3); // No longer unmapped.
  390. // Tab 3 was unmapped and does not get used.
  391. tracker_.PutTabInWindow(kTag, kWindow1, kTab5); // Moved from window 1.
  392. // Window 1 was closed, along with tab 5.
  393. tracker_.PutTabInWindow(kTag, kWindow1, kTab7); // No longer unmapped.
  394. // Session 2 should not be affected.
  395. tracker_.CleanupSession(kTag);
  396. // Verify that only those parts of the session not owned have been removed.
  397. ASSERT_EQ(1U, session1->windows.size());
  398. ASSERT_EQ(4U, session1->windows[kWindow1]->wrapped_window.tabs.size());
  399. ASSERT_EQ(1U, session2->windows.size());
  400. ASSERT_EQ(1U, session2->windows[kWindow3]->wrapped_window.tabs.size());
  401. ASSERT_EQ(2U, tracker_.num_synced_sessions());
  402. ASSERT_EQ(4U, tracker_.num_synced_tabs(kTag));
  403. ASSERT_EQ(1U, tracker_.num_synced_tabs(kTag2));
  404. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  405. // All memory should be properly deallocated by destructor for the
  406. // SyncedSessionTracker.
  407. }
  408. TEST_F(SyncedSessionTrackerTest, DeleteForeignTab) {
  409. int tab_node_id_1 = 1;
  410. int tab_node_id_2 = 2;
  411. std::set<int> result;
  412. tracker_.OnTabNodeSeen(kTag, tab_node_id_1, kTab1);
  413. tracker_.OnTabNodeSeen(kTag, tab_node_id_2, kTab2);
  414. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag),
  415. ElementsAre(tab_node_id_1, tab_node_id_2));
  416. tracker_.DeleteForeignTab(kTag, tab_node_id_1);
  417. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag), ElementsAre(tab_node_id_2));
  418. tracker_.DeleteForeignTab(kTag, tab_node_id_2);
  419. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag), IsEmpty());
  420. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  421. }
  422. TEST_F(SyncedSessionTrackerTest, CleanupLocalTabs) {
  423. tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
  424. // Start with four restored tab nodes, one of which is mapped (|kTab1|).
  425. tracker_.ReassociateLocalTab(kTabNode1, kTab1);
  426. tracker_.ReassociateLocalTab(kTabNode2, kTab2);
  427. tracker_.ReassociateLocalTab(kTabNode3, kTab3);
  428. tracker_.ReassociateLocalTab(kTabNode4, kTab4);
  429. tracker_.PutWindowInSession(kTag, kWindow1);
  430. tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
  431. // Modification times are very recent except for one tab, |kTab3|, which is
  432. // old, and hence is expected to be recycled immediately, even if it's
  433. // unsynced.
  434. tracker_.GetTab(kTag, kTab1)->timestamp = base::Time::Now();
  435. tracker_.GetTab(kTag, kTab2)->timestamp = base::Time::Now();
  436. tracker_.GetTab(kTag, kTab3)->timestamp = base::Time::Now() - base::Days(100);
  437. tracker_.GetTab(kTag, kTab4)->timestamp = base::Time::Now();
  438. // Among the unmapped (closed) ones, |kTab2| and |kTab3| are unsynced.
  439. // However, |kTab3| is old so it's irrelevant whether it's unsynced and the
  440. // callback shouldn't even run.
  441. EXPECT_CALL(is_tab_node_unsynced_cb_, Run(kTabNode2)).WillOnce(Return(true));
  442. EXPECT_CALL(is_tab_node_unsynced_cb_, Run(kTabNode3)).Times(0);
  443. EXPECT_CALL(is_tab_node_unsynced_cb_, Run(kTabNode4)).WillOnce(Return(false));
  444. // During cleanup, only two tabs should be freed:
  445. // - |kTabNode3| because of its age, although it's unsynced.
  446. // - |kTabNode4| because it's synced.
  447. EXPECT_THAT(tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()),
  448. ElementsAre(kTabNode3, kTabNode4));
  449. ASSERT_EQ(kTabNode1, tracker_.LookupTabNodeFromTabId(kTag, kTab1));
  450. EXPECT_EQ(kTabNode2, tracker_.LookupTabNodeFromTabId(kTag, kTab2));
  451. EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
  452. tracker_.LookupTabNodeFromTabId(kTag, kTab3));
  453. EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
  454. tracker_.LookupTabNodeFromTabId(kTag, kTab4));
  455. // |kTabNode2| now becomes synced (commit succeeded), which means it should be
  456. // freed during cleanup.
  457. EXPECT_CALL(is_tab_node_unsynced_cb_, Run(kTabNode2)).WillOnce(Return(false));
  458. EXPECT_THAT(tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()),
  459. ElementsAre(kTabNode2));
  460. EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
  461. tracker_.LookupTabNodeFromTabId(kTag, kTab2));
  462. EXPECT_EQ(kTabNode2, tracker_.AssociateLocalTabWithFreeTabNode(kTab5));
  463. }
  464. TEST_F(SyncedSessionTrackerTest, ReassociateTabMapped) {
  465. // First create the tab normally.
  466. tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
  467. EXPECT_FALSE(IsLocalTabNodeAssociated(kTabNode1));
  468. tracker_.ReassociateLocalTab(kTabNode1, kTab1);
  469. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  470. EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
  471. EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab1));
  472. // Map it to a window with the same tab id as it was created with.
  473. tracker_.ResetSessionTracking(kTag);
  474. tracker_.PutWindowInSession(kTag, kWindow1);
  475. tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
  476. tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get());
  477. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  478. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
  479. const SyncedSession* session = tracker_.LookupSession(kTag);
  480. ASSERT_EQ(1U, session->windows.size());
  481. ASSERT_EQ(1U, session->windows.at(kWindow1)->wrapped_window.tabs.size());
  482. ASSERT_EQ(tracker_.GetTab(kTag, kTab1),
  483. session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
  484. // Then reassociate with a new tab id.
  485. tracker_.ReassociateLocalTab(kTabNode1, kTab2);
  486. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  487. EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
  488. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
  489. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
  490. // Reset tracking, and put the new tab id into the window.
  491. tracker_.ResetSessionTracking(kTag);
  492. EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab2));
  493. tracker_.PutWindowInSession(kTag, kWindow1);
  494. tracker_.PutTabInWindow(kTag, kWindow1, kTab2);
  495. EXPECT_TRUE(
  496. tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
  497. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  498. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
  499. // Now that it's been mapped, it should be accessible both via the
  500. // GetSession as well as the GetTab.
  501. ASSERT_EQ(tracker_.GetTab(kTag, kTab2),
  502. session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
  503. ASSERT_EQ(tracker_.LookupTabNodeIds(kTag).size(),
  504. tracker_.LookupTabNodeIds(kTag).count(kTabNode1));
  505. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  506. }
  507. TEST_F(SyncedSessionTrackerTest, ReassociateTabMappedTwice) {
  508. // First create the tab normally.
  509. tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
  510. EXPECT_FALSE(IsLocalTabNodeAssociated(kTabNode1));
  511. tracker_.ReassociateLocalTab(kTabNode1, kTab1);
  512. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  513. EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
  514. EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab1));
  515. // Map it to a window with the same tab id as it was created with.
  516. tracker_.ResetSessionTracking(kTag);
  517. tracker_.PutWindowInSession(kTag, kWindow1);
  518. tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
  519. EXPECT_TRUE(
  520. tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
  521. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  522. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
  523. const SyncedSession* session = tracker_.LookupSession(kTag);
  524. ASSERT_EQ(1U, session->windows.size());
  525. ASSERT_EQ(1U, session->windows.at(kWindow1)->wrapped_window.tabs.size());
  526. EXPECT_EQ(tracker_.GetTab(kTag, kTab1),
  527. session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
  528. // Then reassociate with a new tab id.
  529. tracker_.ReassociateLocalTab(kTabNode1, kTab2);
  530. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  531. EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
  532. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
  533. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
  534. // Tab 1 should no longer be associated with any SessionTab object. At this
  535. // point there's no need to verify it's unmapped state.
  536. EXPECT_THAT(tracker_.LookupSessionTab(kTag, kTab1), IsNull());
  537. // Reset tracking and add back both the old tab and the new tab (both of which
  538. // refer to the same tab node id).
  539. tracker_.ResetSessionTracking(kTag);
  540. EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab2));
  541. tracker_.PutWindowInSession(kTag, kWindow1);
  542. tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
  543. tracker_.PutTabInWindow(kTag, kWindow1, kTab2);
  544. EXPECT_TRUE(
  545. tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
  546. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  547. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
  548. // Now that it's been mapped, it should be accessible both via the
  549. // GetSession as well as the GetTab.
  550. EXPECT_EQ(tracker_.GetTab(kTag, kTab2),
  551. session->windows.at(kWindow1)->wrapped_window.tabs[1].get());
  552. EXPECT_EQ(tracker_.LookupTabNodeIds(kTag).size(),
  553. tracker_.LookupTabNodeIds(kTag).count(kTabNode1));
  554. // Attempting to access the original tab will create a new SessionTab object.
  555. EXPECT_NE(tracker_.GetTab(kTag, kTab1), tracker_.GetTab(kTag, kTab2));
  556. EXPECT_EQ(TabNodePool::kInvalidTabNodeID,
  557. tracker_.LookupTabNodeFromTabId(kTag, kTab1));
  558. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  559. }
  560. TEST_F(SyncedSessionTrackerTest, ReassociateTabUnmapped) {
  561. // First create the old tab in an unmapped state.
  562. tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
  563. EXPECT_FALSE(IsLocalTabNodeAssociated(kTabNode1));
  564. tracker_.ReassociateLocalTab(kTabNode1, kTab1);
  565. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  566. EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
  567. EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab1));
  568. // Map it to a window, but reassociated with a new tab id.
  569. tracker_.ResetSessionTracking(kTag);
  570. tracker_.ReassociateLocalTab(kTabNode1, kTab2);
  571. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  572. EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
  573. EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab2));
  574. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
  575. tracker_.PutWindowInSession(kTag, kWindow1);
  576. tracker_.PutTabInWindow(kTag, kWindow1, kTab2);
  577. EXPECT_TRUE(
  578. tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
  579. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  580. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
  581. // Now that it's been mapped, it should be accessible both via the
  582. // GetSession as well as GetTab.
  583. const SyncedSession* session = tracker_.LookupSession(kTag);
  584. ASSERT_EQ(tracker_.GetTab(kTag, kTab2),
  585. session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
  586. ASSERT_EQ(tracker_.LookupTabNodeIds(kTag).size(),
  587. tracker_.LookupTabNodeIds(kTag).count(kTabNode1));
  588. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  589. }
  590. TEST_F(SyncedSessionTrackerTest, ReassociateTabOldUnmappedNewMapped) {
  591. // First create the old tab in an unmapped state.
  592. tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
  593. EXPECT_FALSE(IsLocalTabNodeAssociated(kTabNode1));
  594. tracker_.ReassociateLocalTab(kTabNode1, kTab1);
  595. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  596. EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
  597. EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab1));
  598. // Map an unseen tab to a window, then reassociate the existing tab to the
  599. // mapped tab id.
  600. tracker_.ResetSessionTracking(kTag);
  601. EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
  602. tracker_.PutWindowInSession(kTag, kWindow1);
  603. tracker_.PutTabInWindow(kTag, kWindow1, kTab2);
  604. tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get());
  605. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  606. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
  607. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
  608. tracker_.ReassociateLocalTab(kTabNode1, kTab2);
  609. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  610. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
  611. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
  612. // Now that it's been mapped, it should be accessible both via the
  613. // GetSession as well as GetTab.
  614. const SyncedSession* session = tracker_.LookupSession(kTag);
  615. ASSERT_EQ(tracker_.GetTab(kTag, kTab2),
  616. session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
  617. ASSERT_EQ(tracker_.LookupTabNodeIds(kTag).size(),
  618. tracker_.LookupTabNodeIds(kTag).count(kTabNode1));
  619. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  620. }
  621. TEST_F(SyncedSessionTrackerTest, ReassociateTabSameTabId) {
  622. // First create the tab normally.
  623. tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
  624. EXPECT_FALSE(IsLocalTabNodeAssociated(kTabNode1));
  625. tracker_.ReassociateLocalTab(kTabNode1, kTab1);
  626. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  627. EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
  628. EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab1));
  629. // Map it to a window.
  630. tracker_.ResetSessionTracking(kTag);
  631. tracker_.PutWindowInSession(kTag, kWindow1);
  632. tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
  633. EXPECT_TRUE(
  634. tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
  635. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  636. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
  637. const SyncedSession* session = tracker_.LookupSession(kTag);
  638. ASSERT_EQ(1U, session->windows.size());
  639. ASSERT_EQ(1U, session->windows.at(kWindow1)->wrapped_window.tabs.size());
  640. ASSERT_EQ(tracker_.GetTab(kTag, kTab1),
  641. session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
  642. // Reassociate, using the same tab id.
  643. tracker_.ReassociateLocalTab(kTabNode1, kTab1);
  644. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  645. EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
  646. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
  647. // Reset tracking, and put the tab id back into the same window.
  648. tracker_.ResetSessionTracking(kTag);
  649. EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab1));
  650. tracker_.PutWindowInSession(kTag, kWindow1);
  651. tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
  652. EXPECT_TRUE(
  653. tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get()).empty());
  654. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  655. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
  656. // Now that it's been mapped, it should be accessible both via the
  657. // GetSession as well as the GetTab.
  658. ASSERT_EQ(tracker_.GetTab(kTag, kTab1),
  659. session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
  660. ASSERT_EQ(tracker_.LookupTabNodeIds(kTag).size(),
  661. tracker_.LookupTabNodeIds(kTag).count(kTabNode1));
  662. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  663. }
  664. TEST_F(SyncedSessionTrackerTest, ReassociateTabOldMappedNewUnmapped) {
  665. // First create an unmapped tab.
  666. tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
  667. EXPECT_FALSE(IsLocalTabNodeAssociated(kTabNode1));
  668. tracker_.ReassociateLocalTab(kTabNode1, kTab1);
  669. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  670. EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
  671. EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab1));
  672. // Now, map the first one, deleting the second one.
  673. tracker_.ResetSessionTracking(kTag);
  674. tracker_.PutWindowInSession(kTag, kWindow1);
  675. tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
  676. tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get());
  677. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  678. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
  679. const SyncedSession* session = tracker_.LookupSession(kTag);
  680. ASSERT_EQ(1U, session->windows.size());
  681. ASSERT_EQ(1U, session->windows.at(kWindow1)->wrapped_window.tabs.size());
  682. ASSERT_EQ(tracker_.GetTab(kTag, kTab1),
  683. session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
  684. // Create a second unmapped tab.
  685. tracker_.ReassociateLocalTab(kTabNode2, kTab2);
  686. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  687. EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode2));
  688. EXPECT_TRUE(tracker_.IsTabUnmappedForTesting(kTab2));
  689. // Reassociate the second tab with node of the first tab.
  690. tracker_.ReassociateLocalTab(kTabNode1, kTab2);
  691. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  692. EXPECT_TRUE(IsLocalTabNodeAssociated(kTabNode1));
  693. EXPECT_FALSE(IsLocalTabNodeAssociated(kTabNode2));
  694. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
  695. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
  696. // Now map the new one.
  697. tracker_.ResetSessionTracking(kTag);
  698. tracker_.PutWindowInSession(kTag, kWindow1);
  699. tracker_.PutTabInWindow(kTag, kWindow1, kTab2);
  700. tracker_.CleanupLocalTabs(is_tab_node_unsynced_cb_.Get());
  701. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  702. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab1));
  703. EXPECT_FALSE(tracker_.IsTabUnmappedForTesting(kTab2));
  704. // Now that it's been mapped, it should be accessible both via the
  705. // GetSession as well as the GetTab.
  706. ASSERT_EQ(tracker_.GetTab(kTag, kTab2),
  707. session->windows.at(kWindow1)->wrapped_window.tabs[0].get());
  708. ASSERT_TRUE(VerifyTabIntegrity(kTag));
  709. }
  710. TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithHeader) {
  711. sync_pb::SessionSpecifics header;
  712. header.set_session_tag(kTag);
  713. header.mutable_header()->add_window()->set_window_id(kWindow1.id());
  714. header.mutable_header()->mutable_window(0)->add_tab(kTab1.id());
  715. header.mutable_header()->mutable_window(0)->add_tab(kTab2.id());
  716. UpdateTrackerWithSpecifics(header, base::Time::Now(), &tracker_);
  717. EXPECT_THAT(
  718. tracker_.LookupSession(kTag),
  719. MatchesSyncedSession(kTag, {{kWindow1.id(), {kTab1.id(), kTab2.id()}}}));
  720. EXPECT_THAT(tracker_.LookupSessionTab(kTag, kTab1), NotNull());
  721. EXPECT_THAT(tracker_.LookupSessionTab(kTag, kTab2), NotNull());
  722. }
  723. TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithIdenticalHeader) {
  724. sync_pb::SessionSpecifics header;
  725. header.set_session_tag(kTag);
  726. header.mutable_header()->add_window()->set_window_id(kWindow1.id());
  727. header.mutable_header()->mutable_window(0)->add_tab(kTab1.id());
  728. header.mutable_header()->mutable_window(0)->add_tab(kTab2.id());
  729. UpdateTrackerWithSpecifics(header, base::Time::Now(), &tracker_);
  730. ASSERT_THAT(
  731. tracker_.LookupSession(kTag),
  732. MatchesSyncedSession(kTag, {{kWindow1.id(), {kTab1.id(), kTab2.id()}}}));
  733. // Update with an exact header entity.
  734. UpdateTrackerWithSpecifics(header, base::Time::Now(), &tracker_);
  735. EXPECT_THAT(
  736. tracker_.LookupSession(kTag),
  737. MatchesSyncedSession(kTag, {{kWindow1.id(), {kTab1.id(), kTab2.id()}}}));
  738. EXPECT_THAT(tracker_.LookupSessionTab(kTag, kTab1), NotNull());
  739. }
  740. // Verifies that an invalid header (with duplicated tab IDs) is discarded.
  741. TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithHeaderWithDuplicateTabIds) {
  742. sync_pb::SessionSpecifics header;
  743. header.set_session_tag(kTag);
  744. header.mutable_header()->add_window()->set_window_id(kWindow1.id());
  745. header.mutable_header()->mutable_window(0)->add_tab(kTab1.id());
  746. header.mutable_header()->mutable_window(0)->add_tab(kTab1.id());
  747. UpdateTrackerWithSpecifics(header, base::Time::Now(), &tracker_);
  748. EXPECT_THAT(tracker_.LookupSession(kTag),
  749. MatchesSyncedSession(kTag, /*window_id_to_tabs=*/{}));
  750. }
  751. // Verifies that an invalid tab (with invalid ID) is discarded.
  752. TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithInvalidTab) {
  753. const int kInvalidTabId = -1;
  754. sync_pb::SessionSpecifics tab;
  755. tab.set_session_tag(kTag);
  756. tab.mutable_tab()->set_tab_id(kInvalidTabId);
  757. UpdateTrackerWithSpecifics(tab, base::Time::Now(), &tracker_);
  758. EXPECT_THAT(tracker_.LookupSessionTab(
  759. kTag, SessionID::FromSerializedValue(kInvalidTabId)),
  760. IsNull());
  761. }
  762. // Verifies that an invalid header (with duplicated window IDs) is discarded.
  763. TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithHeaderWithDuplicateWindowId) {
  764. sync_pb::SessionSpecifics header;
  765. header.set_session_tag(kTag);
  766. header.mutable_header()->add_window()->set_window_id(kWindow1.id());
  767. header.mutable_header()->add_window()->set_window_id(kWindow1.id());
  768. header.mutable_header()->mutable_window(0)->add_tab(kTab1.id());
  769. header.mutable_header()->mutable_window(1)->add_tab(kTab2.id());
  770. UpdateTrackerWithSpecifics(header, base::Time::Now(), &tracker_);
  771. EXPECT_THAT(tracker_.LookupSession(kTag),
  772. MatchesSyncedSession(kTag, /*window_id_to_tabs=*/{}));
  773. }
  774. // Verifies that an invalid header with duplicated window IDs is ignored. It
  775. // specifically tests feeding the same input twice to
  776. // UpdateTrackerWithSpecifics(), as a regression test for crbug.com/803205 to
  777. // verify that it at least doesn't crash.
  778. TEST_F(SyncedSessionTrackerTest,
  779. UpdateTrackerWithHeaderWithDuplicateWindowIdTwice) {
  780. sync_pb::SessionSpecifics header;
  781. header.set_session_tag(kTag);
  782. header.mutable_header()->add_window()->set_window_id(kWindow1.id());
  783. header.mutable_header()->mutable_window(0)->add_tab(kTab1.id());
  784. header.mutable_header()->add_window()->set_window_id(kWindow1.id());
  785. header.mutable_header()->mutable_window(1)->add_tab(kTab2.id());
  786. UpdateTrackerWithSpecifics(header, base::Time::Now(), &tracker_);
  787. UpdateTrackerWithSpecifics(header, base::Time::Now(), &tracker_);
  788. EXPECT_THAT(tracker_.LookupSession(kTag),
  789. MatchesSyncedSession(kTag, /*window_id_to_tabs=*/{}));
  790. }
  791. TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithTab) {
  792. sync_pb::SessionSpecifics tab;
  793. tab.set_session_tag(kTag);
  794. tab.set_tab_node_id(kTabNode1);
  795. tab.mutable_tab()->set_window_id(kWindow1.id());
  796. tab.mutable_tab()->set_tab_id(kTab1.id());
  797. UpdateTrackerWithSpecifics(tab, base::Time::Now(), &tracker_);
  798. const sessions::SessionTab* tracked_tab =
  799. tracker_.LookupSessionTab(kTag, kTab1);
  800. ASSERT_THAT(tracked_tab, NotNull());
  801. EXPECT_EQ(kWindow1, tracked_tab->window_id);
  802. EXPECT_EQ(false, tracked_tab->pinned);
  803. EXPECT_THAT(tracker_.LookupSession(kTag),
  804. MatchesSyncedSession(kTag, /*window_id_to_tabs*/ {}));
  805. // Overwrite some attribute.
  806. tab.mutable_tab()->set_pinned(true);
  807. UpdateTrackerWithSpecifics(tab, base::Time::Now(), &tracker_);
  808. tracked_tab = tracker_.LookupSessionTab(kTag, kTab1);
  809. ASSERT_THAT(tracked_tab, NotNull());
  810. EXPECT_EQ(kWindow1, tracked_tab->window_id);
  811. EXPECT_EQ(true, tracked_tab->pinned);
  812. EXPECT_THAT(tracker_.LookupSession(kTag),
  813. MatchesSyncedSession(kTag, /*window_id_to_tabs*/ {}));
  814. }
  815. TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithTabThenHeader) {
  816. sync_pb::SessionSpecifics tab;
  817. tab.set_session_tag(kTag);
  818. tab.set_tab_node_id(kTabNode1);
  819. tab.mutable_tab()->set_window_id(kWindow1.id());
  820. tab.mutable_tab()->set_tab_id(kTab1.id());
  821. UpdateTrackerWithSpecifics(tab, base::Time::Now(), &tracker_);
  822. EXPECT_THAT(tracker_.LookupSession(kTag),
  823. MatchesSyncedSession(kTag, /*window_id_to_tabs*/ {}));
  824. sync_pb::SessionSpecifics header;
  825. header.set_session_tag(kTag);
  826. header.mutable_header()->add_window()->set_window_id(kWindow1.id());
  827. header.mutable_header()->mutable_window(0)->add_tab(kTab1.id());
  828. UpdateTrackerWithSpecifics(header, base::Time::Now(), &tracker_);
  829. const sessions::SessionTab* tracked_tab =
  830. tracker_.LookupSessionTab(kTag, kTab1);
  831. ASSERT_THAT(tracked_tab, NotNull());
  832. EXPECT_EQ(kWindow1, tracked_tab->window_id);
  833. EXPECT_THAT(tracker_.LookupSession(kTag),
  834. MatchesSyncedSession(
  835. kTag, {{kWindow1.id(), std::vector<int>{kTab1.id()}}}));
  836. }
  837. TEST_F(SyncedSessionTrackerTest, UpdateTrackerWithTwoTabsSameId) {
  838. sync_pb::SessionSpecifics tab1;
  839. tab1.set_session_tag(kTag);
  840. tab1.set_tab_node_id(kTabNode1);
  841. tab1.mutable_tab()->set_window_id(kWindow1.id());
  842. tab1.mutable_tab()->set_tab_id(kTab1.id());
  843. tab1.mutable_tab()->set_pinned(false);
  844. UpdateTrackerWithSpecifics(tab1, base::Time::Now(), &tracker_);
  845. sync_pb::SessionSpecifics tab2;
  846. tab2.set_session_tag(kTag);
  847. tab2.set_tab_node_id(kTabNode2);
  848. tab2.mutable_tab()->set_window_id(kWindow1.id());
  849. tab2.mutable_tab()->set_tab_id(kTab1.id());
  850. tab2.mutable_tab()->set_pinned(true);
  851. UpdateTrackerWithSpecifics(tab2, base::Time::Now(), &tracker_);
  852. EXPECT_THAT(tracker_.LookupSession(kTag),
  853. MatchesSyncedSession(kTag, /*window_id_to_tabs*/ {}));
  854. EXPECT_THAT(tracker_.LookupTabNodeIds(kTag),
  855. ElementsAre(kTabNode1, kTabNode2));
  856. const sessions::SessionTab* tracked_tab =
  857. tracker_.LookupSessionTab(kTag, kTab1);
  858. ASSERT_THAT(tracked_tab, NotNull());
  859. EXPECT_EQ(kWindow1, tracked_tab->window_id);
  860. EXPECT_EQ(true, tracked_tab->pinned);
  861. }
  862. TEST_F(SyncedSessionTrackerTest, SerializeTrackerToSpecifics) {
  863. tracker_.InitLocalSession(kTag, kSessionName, kDeviceType);
  864. tracker_.PutWindowInSession(kTag, kWindow1);
  865. tracker_.GetSession(kTag)->windows[kWindow1]->window_type =
  866. sync_pb::SyncEnums_BrowserType_TYPE_TABBED;
  867. tracker_.PutTabInWindow(kTag, kWindow1, kTab1);
  868. tracker_.PutTabInWindow(kTag, kWindow1, kTab2);
  869. // Unmapped tab.
  870. tracker_.GetTab(kTag, kTab3);
  871. // |kTabNode4| will be unassociated, because |kTab1| is associated twice.
  872. tracker_.ReassociateLocalTab(kTabNode4, kTab1);
  873. tracker_.ReassociateLocalTab(kTabNode1, kTab1);
  874. // Regular associations.
  875. tracker_.ReassociateLocalTab(kTabNode2, kTab2);
  876. tracker_.ReassociateLocalTab(kTabNode3, kTab3);
  877. base::MockCallback<base::RepeatingCallback<void(
  878. const std::string& session_name, sync_pb::SessionSpecifics* specifics)>>
  879. callback;
  880. EXPECT_CALL(callback, Run(kSessionName,
  881. Pointee(MatchesHeader(kTag, {kWindow1.id()},
  882. {kTab1.id(), kTab2.id()}))));
  883. EXPECT_CALL(callback, Run(kSessionName,
  884. Pointee(MatchesTab(kTag, kWindow1.id(), kTab1.id(),
  885. kTabNode1, /*urls=*/_))));
  886. EXPECT_CALL(callback, Run(kSessionName,
  887. Pointee(MatchesTab(kTag, kWindow1.id(), kTab2.id(),
  888. kTabNode2, /*urls=*/_))));
  889. EXPECT_CALL(
  890. callback,
  891. Run(kSessionName, Pointee(MatchesTab(kTag, Ne(kWindow1.id()), kTab3.id(),
  892. kTabNode3, /*urls=*/_))));
  893. SerializeTrackerToSpecifics(tracker_, callback.Get());
  894. EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(&callback));
  895. // Serialize the header only.
  896. EXPECT_CALL(callback, Run(kSessionName,
  897. Pointee(MatchesHeader(kTag, {kWindow1.id()},
  898. {kTab1.id(), kTab2.id()}))));
  899. SerializePartialTrackerToSpecifics(
  900. tracker_, {{kTag, {TabNodePool::kInvalidTabNodeID}}}, callback.Get());
  901. EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(&callback));
  902. // Serialize a known and associated tab.
  903. EXPECT_CALL(callback, Run(kSessionName,
  904. Pointee(MatchesTab(kTag, kWindow1.id(), kTab1.id(),
  905. kTabNode1, /*urls=*/_))));
  906. SerializePartialTrackerToSpecifics(tracker_, {{kTag, {kTabNode1}}},
  907. callback.Get());
  908. EXPECT_TRUE(testing::Mock::VerifyAndClearExpectations(&callback));
  909. // Attempt to serialize unknown entities.
  910. EXPECT_CALL(callback, Run).Times(0);
  911. SerializePartialTrackerToSpecifics(tracker_, {{kTag, {kTabNode5}}},
  912. callback.Get());
  913. SerializePartialTrackerToSpecifics(
  914. tracker_, {{kTag2, {TabNodePool::kInvalidTabNodeID, kTabNode1}}},
  915. callback.Get());
  916. }
  917. TEST_F(SyncedSessionTrackerTest, SerializeTrackerToSpecificsWithEmptyHeader) {
  918. sync_pb::SessionSpecifics tab;
  919. tab.set_session_tag(kTag);
  920. tab.set_tab_node_id(kTabNode1);
  921. tab.mutable_tab()->set_window_id(kWindow1.id());
  922. tab.mutable_tab()->set_tab_id(kTab1.id());
  923. UpdateTrackerWithSpecifics(tab, base::Time::Now(), &tracker_);
  924. sync_pb::SessionSpecifics header;
  925. header.set_session_tag(kTag);
  926. header.mutable_header()->set_client_name(kSessionName);
  927. UpdateTrackerWithSpecifics(header, base::Time::Now(), &tracker_);
  928. base::MockCallback<base::RepeatingCallback<void(
  929. const std::string& session_name, sync_pb::SessionSpecifics* specifics)>>
  930. callback;
  931. EXPECT_CALL(callback,
  932. Run(kSessionName, Pointee(MatchesHeader(kTag, /*window_ids=*/{},
  933. /*tab_ids=*/{}))));
  934. EXPECT_CALL(
  935. callback,
  936. Run(kSessionName, Pointee(MatchesTab(kTag, /*window_id=*/0, kTab1.id(),
  937. kTabNode1, /*urls=*/_))));
  938. SerializeTrackerToSpecifics(tracker_, callback.Get());
  939. }
  940. } // namespace sync_sessions