OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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 "ios/chrome/browser/ui/tab_switcher/tab_switcher_model.h" |
| 6 |
| 7 #include "base/mac/scoped_nsobject.h" |
| 8 #include "base/memory/ptr_util.h" |
| 9 #include "base/strings/utf_string_conversions.h" |
| 10 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h" |
| 11 #include "ios/chrome/browser/ui/ntp/recent_tabs/synced_sessions.h" |
| 12 #include "ios/chrome/browser/ui/tab_switcher/session_changes.h" |
| 13 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_model_private.h" |
| 14 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_utils.h" |
| 15 #include "testing/platform_test.h" |
| 16 #import "third_party/ocmock/OCMock/OCMock.h" |
| 17 |
| 18 namespace { |
| 19 |
| 20 // A lightweight SessionChanges. |
| 21 struct LightSessionChanges { |
| 22 std::vector<int> updates; |
| 23 std::vector<int> deletions; |
| 24 std::vector<int> insertions; |
| 25 }; |
| 26 |
| 27 // A lightweight DistantTab. |
| 28 class LightDT { |
| 29 public: |
| 30 LightDT(std::string const& urlSuffix, std::string const& title = "") |
| 31 : url("http://www.foo.com/" + urlSuffix), title(title) {} |
| 32 std::string url; |
| 33 std::string title; |
| 34 }; |
| 35 |
| 36 // A lightweight DistantSession. |
| 37 struct LightDS { |
| 38 std::string tag; |
| 39 std::vector<LightDT> distantTabs; |
| 40 }; |
| 41 |
| 42 // A lightweight SyncedSessions. |
| 43 using LightSS = std::vector<LightDS>; |
| 44 |
| 45 } // namespace |
| 46 |
| 47 // Helper class to test calls to the TabSwitcherModelDelegate. |
| 48 @interface DelegateTester : NSObject<TabSwitcherModelDelegate> |
| 49 @end |
| 50 |
| 51 @implementation DelegateTester { |
| 52 base::scoped_nsobject<NSArray> _expectedSessionRemoved; |
| 53 base::scoped_nsobject<NSArray> _expectedSessionInserted; |
| 54 std::set<std::string> _expectedTagsOfTheSessionsNeedingUpdates; |
| 55 } |
| 56 |
| 57 - (void)expectSessionsRemoved:(NSArray*)expectedIndexes { |
| 58 _expectedSessionRemoved.reset([expectedIndexes retain]); |
| 59 } |
| 60 |
| 61 - (void)expectSessionsInserted:(NSArray*)expectedIndexes { |
| 62 _expectedSessionInserted.reset([expectedIndexes retain]); |
| 63 } |
| 64 |
| 65 - (void)expectSessionMayNeedUpdate:(std::set<std::string> const&)tags { |
| 66 _expectedTagsOfTheSessionsNeedingUpdates = tags; |
| 67 } |
| 68 |
| 69 - (void)verify { |
| 70 EXPECT_EQ(0UL, [_expectedSessionRemoved count]); |
| 71 EXPECT_EQ(0UL, [_expectedSessionInserted count]); |
| 72 EXPECT_EQ(0UL, _expectedTagsOfTheSessionsNeedingUpdates.size()); |
| 73 } |
| 74 |
| 75 #pragma mark - TabSwitcherModelDelegate |
| 76 |
| 77 - (void)distantSessionsRemovedAtSortedIndexes:(NSArray*)removedIndexes |
| 78 insertedAtSortedIndexes:(NSArray*)insertedIndexes { |
| 79 EXPECT_TRUE(removedIndexes == _expectedSessionRemoved.get() || |
| 80 [removedIndexes isEqualToArray:_expectedSessionRemoved.get()]); |
| 81 EXPECT_TRUE(insertedIndexes == _expectedSessionInserted.get() || |
| 82 [insertedIndexes isEqualToArray:_expectedSessionInserted.get()]); |
| 83 _expectedSessionRemoved.reset(); |
| 84 _expectedSessionInserted.reset(); |
| 85 } |
| 86 |
| 87 - (void)distantSessionMayNeedUpdate:(std::string const&)tag { |
| 88 EXPECT_EQ(1UL, _expectedTagsOfTheSessionsNeedingUpdates.erase(tag)); |
| 89 } |
| 90 |
| 91 - (void)localSessionMayNeedUpdate:(ios_internal::SessionType)type { |
| 92 NOTREACHED(); |
| 93 } |
| 94 |
| 95 - (void)signInPanelChangedTo:(TabSwitcherSignInPanelsType)panelType { |
| 96 NOTREACHED(); |
| 97 } |
| 98 - (CGSize)sizeForItemAtIndex:(NSUInteger)index |
| 99 inSession:(ios_internal::SessionType)session { |
| 100 return CGSizeZero; |
| 101 } |
| 102 @end |
| 103 |
| 104 namespace { |
| 105 |
| 106 class TabSwitcherModelTest : public PlatformTest { |
| 107 protected: |
| 108 void SetUp() override { delegate_.reset([[DelegateTester alloc] init]); } |
| 109 |
| 110 void AddSessionToSessions(synced_sessions::SyncedSessions& sessions, |
| 111 std::string const& session_tag, |
| 112 std::vector<size_t> const& tab_ids) { |
| 113 std::vector<LightDT> light_distant_tabs; |
| 114 for (size_t tab_id : tab_ids) { |
| 115 light_distant_tabs.push_back(LightDT(std::to_string(tab_id), "")); |
| 116 } |
| 117 AddDetailedSessionToSessions(sessions, session_tag, light_distant_tabs); |
| 118 } |
| 119 |
| 120 std::unique_ptr<synced_sessions::SyncedSessions> syncedSessionsFromTestData( |
| 121 LightSS const& lightSyncedSessions) { |
| 122 auto syncedSessions = base::MakeUnique<synced_sessions::SyncedSessions>(); |
| 123 for (auto& lightdistantSession : lightSyncedSessions) { |
| 124 AddDetailedSessionToSessions(*syncedSessions, lightdistantSession.tag, |
| 125 lightdistantSession.distantTabs); |
| 126 } |
| 127 return syncedSessions; |
| 128 } |
| 129 |
| 130 void AddDetailedSessionToSessions( |
| 131 synced_sessions::SyncedSessions& sessions, |
| 132 std::string const& session_tag, |
| 133 std::vector<LightDT> const& light_distant_tabs) { |
| 134 // Create a new DistantSession and initialize it with |sessionTag| and |
| 135 // |tabTags|. |
| 136 auto distant_session = base::MakeUnique<synced_sessions::DistantSession>(); |
| 137 distant_session->tag = session_tag; |
| 138 |
| 139 for (auto const& light_distant_tab : light_distant_tabs) { |
| 140 auto temp_tab = base::MakeUnique<synced_sessions::DistantTab>(); |
| 141 temp_tab->virtual_url = GURL(light_distant_tab.url); |
| 142 temp_tab->title = base::ASCIIToUTF16(light_distant_tab.title); |
| 143 distant_session->tabs.push_back(std::move(temp_tab)); |
| 144 } |
| 145 |
| 146 sessions.AddDistantSessionForTest(std::move(distant_session)); |
| 147 } |
| 148 base::scoped_nsobject<DelegateTester> delegate_; |
| 149 base::scoped_nsobject<TabSwitcherModel> model_; |
| 150 }; |
| 151 |
| 152 TEST_F(TabSwitcherModelTest, TestNoDiffs) { |
| 153 // Test with 2 empty sessions. |
| 154 synced_sessions::SyncedSessions old_sessions_1; |
| 155 synced_sessions::SyncedSessions new_sessions_1; |
| 156 [TabSwitcherModel notifyDelegate:delegate_ |
| 157 aboutChangeFrom:old_sessions_1 |
| 158 to:new_sessions_1]; |
| 159 [delegate_ verify]; |
| 160 |
| 161 // Test with 2 identical sessions. |
| 162 synced_sessions::SyncedSessions old_sessions_2; |
| 163 synced_sessions::SyncedSessions new_sessions_2; |
| 164 AddSessionToSessions(old_sessions_2, "Foo", {0, 1}); |
| 165 AddSessionToSessions(new_sessions_2, "Foo", {0, 1}); |
| 166 [delegate_ expectSessionMayNeedUpdate:{"Foo"}]; |
| 167 |
| 168 [TabSwitcherModel notifyDelegate:delegate_ |
| 169 aboutChangeFrom:old_sessions_2 |
| 170 to:new_sessions_2]; |
| 171 [delegate_ verify]; |
| 172 } |
| 173 |
| 174 TEST_F(TabSwitcherModelTest, TestSessionDiffs) { |
| 175 // Test with 1 session inserted. |
| 176 synced_sessions::SyncedSessions old_sessions_1; |
| 177 synced_sessions::SyncedSessions new_sessions_1; |
| 178 AddSessionToSessions(new_sessions_1, "Foo", {0}); |
| 179 [delegate_ expectSessionsInserted:@[ @0 ]]; |
| 180 [TabSwitcherModel notifyDelegate:delegate_ |
| 181 aboutChangeFrom:old_sessions_1 |
| 182 to:new_sessions_1]; |
| 183 [delegate_ verify]; |
| 184 |
| 185 // Test with 2 sessions inserted. |
| 186 synced_sessions::SyncedSessions old_sessions_2; |
| 187 synced_sessions::SyncedSessions new_sessions_2; |
| 188 AddSessionToSessions(old_sessions_2, "Bar", {0}); |
| 189 AddSessionToSessions(new_sessions_2, "Foo", {0}); |
| 190 AddSessionToSessions(new_sessions_2, "Bar", {0}); |
| 191 AddSessionToSessions(new_sessions_2, "Qux", {0}); |
| 192 [delegate_ expectSessionsInserted:@[ @0, @2 ]]; |
| 193 [delegate_ expectSessionMayNeedUpdate:{"Bar"}]; |
| 194 [TabSwitcherModel notifyDelegate:delegate_ |
| 195 aboutChangeFrom:old_sessions_2 |
| 196 to:new_sessions_2]; |
| 197 [delegate_ verify]; |
| 198 |
| 199 // Test with 2 sessions removed. |
| 200 synced_sessions::SyncedSessions old_sessions_3; |
| 201 synced_sessions::SyncedSessions new_sessions_3; |
| 202 AddSessionToSessions(old_sessions_3, "Foo", {0}); |
| 203 AddSessionToSessions(old_sessions_3, "Bar", {0}); |
| 204 AddSessionToSessions(old_sessions_3, "Qux", {0}); |
| 205 AddSessionToSessions(new_sessions_3, "Bar", {0}); |
| 206 [delegate_ expectSessionsRemoved:@[ @0, @2 ]]; |
| 207 [delegate_ expectSessionMayNeedUpdate:{"Bar"}]; |
| 208 [TabSwitcherModel notifyDelegate:delegate_ |
| 209 aboutChangeFrom:old_sessions_3 |
| 210 to:new_sessions_3]; |
| 211 [delegate_ verify]; |
| 212 |
| 213 // Test with 2 sessions inserted, 2 removed. |
| 214 synced_sessions::SyncedSessions old_sessions_4; |
| 215 synced_sessions::SyncedSessions new_sessions_4; |
| 216 AddSessionToSessions(old_sessions_4, "Deleted1", {0}); |
| 217 AddSessionToSessions(old_sessions_4, "a", {0}); |
| 218 AddSessionToSessions(old_sessions_4, "Delete2", {0}); |
| 219 |
| 220 AddSessionToSessions(new_sessions_4, "b", {0}); |
| 221 AddSessionToSessions(new_sessions_4, "a", {0}); |
| 222 AddSessionToSessions(new_sessions_4, "c", {0}); |
| 223 [delegate_ expectSessionsRemoved:@[ @0, @2 ]]; |
| 224 [delegate_ expectSessionsInserted:@[ @0, @2 ]]; |
| 225 [delegate_ expectSessionMayNeedUpdate:{"a"}]; |
| 226 |
| 227 [TabSwitcherModel notifyDelegate:delegate_ |
| 228 aboutChangeFrom:old_sessions_4 |
| 229 to:new_sessions_4]; |
| 230 [delegate_ verify]; |
| 231 } |
| 232 |
| 233 TEST_F(TabSwitcherModelTest, TestTabsSimpleDiffs) { |
| 234 // Test with 2 tabs added. |
| 235 synced_sessions::SyncedSessions old_sessions_1; |
| 236 synced_sessions::SyncedSessions new_sessions_1; |
| 237 AddSessionToSessions(old_sessions_1, "Bar", {100}); |
| 238 AddSessionToSessions(old_sessions_1, "Foo", {100}); |
| 239 AddSessionToSessions(new_sessions_1, "Bar", {100}); |
| 240 AddSessionToSessions(new_sessions_1, "Foo", {99, 100, 101}); |
| 241 [delegate_ expectSessionMayNeedUpdate:{"Bar", "Foo"}]; |
| 242 |
| 243 [TabSwitcherModel notifyDelegate:delegate_ |
| 244 aboutChangeFrom:old_sessions_1 |
| 245 to:new_sessions_1]; |
| 246 [delegate_ verify]; |
| 247 |
| 248 // Test with 2 tabs removed. |
| 249 synced_sessions::SyncedSessions old_sessions_2; |
| 250 synced_sessions::SyncedSessions new_sessions_2; |
| 251 AddSessionToSessions(old_sessions_2, "Bar", {0}); |
| 252 AddSessionToSessions(old_sessions_2, "Foo", {100, 101, 102}); |
| 253 AddSessionToSessions(new_sessions_2, "Bar", {0}); |
| 254 AddSessionToSessions(new_sessions_2, "Foo", {100}); |
| 255 [delegate_ expectSessionMayNeedUpdate:{"Bar", "Foo"}]; |
| 256 |
| 257 [TabSwitcherModel notifyDelegate:delegate_ |
| 258 aboutChangeFrom:old_sessions_2 |
| 259 to:new_sessions_2]; |
| 260 [delegate_ verify]; |
| 261 |
| 262 // Test with 2 tabs updated. |
| 263 synced_sessions::SyncedSessions old_sessions_3; |
| 264 synced_sessions::SyncedSessions new_sessions_3; |
| 265 std::vector<LightDT> old_tabs_3; |
| 266 old_tabs_3.push_back(LightDT("1", "1")); |
| 267 old_tabs_3.push_back(LightDT("2", "2")); |
| 268 old_tabs_3.push_back(LightDT("3", "3")); |
| 269 std::vector<LightDT> new_tabs_3; |
| 270 new_tabs_3.push_back(LightDT("1", "1 bis")); |
| 271 new_tabs_3.push_back(LightDT("2", "2")); |
| 272 new_tabs_3.push_back(LightDT("a", "3")); |
| 273 AddDetailedSessionToSessions(old_sessions_3, "Foo", old_tabs_3); |
| 274 AddDetailedSessionToSessions(new_sessions_3, "Foo", new_tabs_3); |
| 275 [delegate_ expectSessionMayNeedUpdate:{"Foo"}]; |
| 276 [TabSwitcherModel notifyDelegate:delegate_ |
| 277 aboutChangeFrom:old_sessions_3 |
| 278 to:new_sessions_3]; |
| 279 [delegate_ verify]; |
| 280 } |
| 281 |
| 282 TEST_F(TabSwitcherModelTest, TestTabsDeletionInsertions) { |
| 283 synced_sessions::SyncedSessions old_sessions; |
| 284 synced_sessions::SyncedSessions new_sessions; |
| 285 std::vector<LightDT> old_tabs; |
| 286 old_tabs.push_back(LightDT("0", "0")); |
| 287 old_tabs.push_back(LightDT("1", "1")); |
| 288 old_tabs.push_back(LightDT("2", "2")); |
| 289 old_tabs.push_back(LightDT("3", "3")); |
| 290 std::vector<LightDT> new_tabs; |
| 291 new_tabs.push_back(LightDT("1", "1")); |
| 292 new_tabs.push_back(LightDT("2", "2")); |
| 293 new_tabs.push_back(LightDT("3", "3")); |
| 294 new_tabs.push_back(LightDT("0", "0")); |
| 295 AddDetailedSessionToSessions(old_sessions, "Foo", old_tabs); |
| 296 AddDetailedSessionToSessions(new_sessions, "Foo", new_tabs); |
| 297 [delegate_ expectSessionMayNeedUpdate:{"Foo"}]; |
| 298 [TabSwitcherModel notifyDelegate:delegate_ |
| 299 aboutChangeFrom:old_sessions |
| 300 to:new_sessions]; |
| 301 [delegate_ verify]; |
| 302 } |
| 303 |
| 304 TEST_F(TabSwitcherModelTest, TestTabsWithInterleavedDiffs) { |
| 305 synced_sessions::SyncedSessions old_sessions; |
| 306 synced_sessions::SyncedSessions new_sessions; |
| 307 std::vector<LightDT> old_tabs; |
| 308 old_tabs.push_back(LightDT("0", "0")); |
| 309 old_tabs.push_back(LightDT("1", "1")); // gets deleted |
| 310 old_tabs.push_back(LightDT("2", "2")); |
| 311 old_tabs.push_back(LightDT("3", "3")); // gets updated |
| 312 old_tabs.push_back(LightDT("4", "4")); |
| 313 old_tabs.push_back(LightDT("6", "6")); |
| 314 old_tabs.push_back(LightDT("7", "7")); // gets updated |
| 315 std::vector<LightDT> new_tabs; |
| 316 new_tabs.push_back(LightDT("0", "0")); |
| 317 new_tabs.push_back(LightDT("2", "2")); |
| 318 new_tabs.push_back(LightDT("3", "3 bis")); |
| 319 new_tabs.push_back(LightDT("4", "4")); |
| 320 new_tabs.push_back(LightDT("5", "5")); // is inserted |
| 321 new_tabs.push_back(LightDT("6", "6")); |
| 322 new_tabs.push_back(LightDT("7", "7 bis")); |
| 323 AddDetailedSessionToSessions(old_sessions, "Foo", old_tabs); |
| 324 AddDetailedSessionToSessions(new_sessions, "Foo", new_tabs); |
| 325 [delegate_ expectSessionMayNeedUpdate:{"Foo"}]; |
| 326 [TabSwitcherModel notifyDelegate:delegate_ |
| 327 aboutChangeFrom:old_sessions |
| 328 to:new_sessions]; |
| 329 [delegate_ verify]; |
| 330 } |
| 331 |
| 332 // Tests that the reordering of sessions does not result in calls to the |
| 333 // delegate. |
| 334 TEST_F(TabSwitcherModelTest, TestReorderingOfSessions) { |
| 335 std::vector<LightDS> old_sessions_data = { |
| 336 {"A", {LightDT("a"), LightDT("b")}}, |
| 337 {"B", {LightDT("a"), LightDT("b")}}, |
| 338 {"C", {LightDT("a"), LightDT("b")}}, |
| 339 }; |
| 340 std::vector<LightDS> new_sessions_data = { |
| 341 {"C", {LightDT("a"), LightDT("b")}}, |
| 342 {"A", {LightDT("a"), LightDT("b")}}, |
| 343 {"B", {LightDT("a"), LightDT("b")}}, |
| 344 }; |
| 345 auto old_sessions = syncedSessionsFromTestData(old_sessions_data); |
| 346 auto new_sessions = syncedSessionsFromTestData(new_sessions_data); |
| 347 [delegate_ expectSessionMayNeedUpdate:{"A", "B", "C"}]; |
| 348 [TabSwitcherModel notifyDelegate:delegate_ |
| 349 aboutChangeFrom:*old_sessions |
| 350 to:*new_sessions]; |
| 351 [delegate_ verify]; |
| 352 } |
| 353 |
| 354 // Tests that the reordering of sessions does not result in wrong deletion |
| 355 // indexes. |
| 356 TEST_F(TabSwitcherModelTest, TestReorderingOfSessionsWithDeletion) { |
| 357 std::vector<LightDS> old_sessions_data = { |
| 358 {"A", {LightDT("a"), LightDT("b"), LightDT("c")}}, |
| 359 {"B", {LightDT("a"), LightDT("b")}}, // deleted |
| 360 {"C", {LightDT("a"), LightDT("b")}}, |
| 361 {"D", {LightDT("a"), LightDT("b")}}, // deleted |
| 362 }; |
| 363 std::vector<LightDS> new_sessions_data = { |
| 364 {"C", {LightDT("a"), LightDT("b")}}, |
| 365 {"A", {LightDT("a"), LightDT("b"), LightDT("c")}}, |
| 366 }; |
| 367 auto old_sessions = syncedSessionsFromTestData(old_sessions_data); |
| 368 auto new_sessions = syncedSessionsFromTestData(new_sessions_data); |
| 369 [delegate_ expectSessionsRemoved:@[ @1, @3 ]]; |
| 370 [delegate_ expectSessionMayNeedUpdate:{"A", "C"}]; |
| 371 [TabSwitcherModel notifyDelegate:delegate_ |
| 372 aboutChangeFrom:*old_sessions |
| 373 to:*new_sessions]; |
| 374 [delegate_ verify]; |
| 375 } |
| 376 |
| 377 // Tests that the reordering of sessions does not result in wrong insertion |
| 378 // indexes. |
| 379 TEST_F(TabSwitcherModelTest, TestReorderingOfSessionsWithInsertion) { |
| 380 std::vector<LightDS> old_sessions_data = { |
| 381 {"A", {LightDT("a"), LightDT("b")}}, |
| 382 {"B", {LightDT("a"), LightDT("b")}}, |
| 383 {"C", {LightDT("a"), LightDT("b")}}, |
| 384 }; |
| 385 std::vector<LightDS> new_sessions_data = { |
| 386 {"B", {LightDT("a"), LightDT("b")}}, |
| 387 {"D", {LightDT("a"), LightDT("b")}}, // inserted |
| 388 {"C", {LightDT("a"), LightDT("b")}}, |
| 389 {"A", {LightDT("a"), LightDT("b")}}, |
| 390 {"E", {LightDT("a"), LightDT("b")}}, // inserted |
| 391 }; |
| 392 auto old_sessions = syncedSessionsFromTestData(old_sessions_data); |
| 393 auto new_sessions = syncedSessionsFromTestData(new_sessions_data); |
| 394 [delegate_ expectSessionsInserted:@[ @1, @4 ]]; |
| 395 [delegate_ expectSessionMayNeedUpdate:{"A", "B", "C"}]; |
| 396 [TabSwitcherModel notifyDelegate:delegate_ |
| 397 aboutChangeFrom:*old_sessions |
| 398 to:*new_sessions]; |
| 399 [delegate_ verify]; |
| 400 } |
| 401 |
| 402 TEST_F(TabSwitcherModelTest, TestTabsWithSessionAndTabChanges) { |
| 403 std::vector<LightDS> old_sessions_data = { |
| 404 {"0", {LightDT("a"), LightDT("b")}}, |
| 405 {"1", {LightDT("a"), LightDT("b")}}, // session deleted |
| 406 {"2", {LightDT("a"), LightDT("b"), LightDT("c")}}, |
| 407 {"3", {LightDT("b")}}, |
| 408 {"4", {LightDT("a"), LightDT("b"), LightDT("c")}}, // tab deleted |
| 409 {"5", {LightDT("a"), LightDT("b")}}, |
| 410 }; |
| 411 |
| 412 std::vector<LightDS> new_sessions_data = { |
| 413 {"A", {LightDT("a")}}, // session inserted |
| 414 {"0", {LightDT("a"), LightDT("b")}}, |
| 415 {"2", {LightDT("A"), LightDT("b"), LightDT("C")}}, // tabs updated |
| 416 {"3", {LightDT("a"), LightDT("b")}}, // tab inserted |
| 417 {"4", {LightDT("b")}}, |
| 418 {"5", {LightDT("a"), LightDT("b")}}, |
| 419 }; |
| 420 |
| 421 auto old_sessions = syncedSessionsFromTestData(old_sessions_data); |
| 422 auto new_sessions = syncedSessionsFromTestData(new_sessions_data); |
| 423 |
| 424 [delegate_ expectSessionMayNeedUpdate:{"0", "2", "3", "4", "5"}]; |
| 425 [delegate_ expectSessionsRemoved:@[ @1 ]]; |
| 426 [delegate_ expectSessionsInserted:@[ @0 ]]; |
| 427 |
| 428 [TabSwitcherModel notifyDelegate:delegate_ |
| 429 aboutChangeFrom:*old_sessions |
| 430 to:*new_sessions]; |
| 431 [delegate_ verify]; |
| 432 } |
| 433 |
| 434 } // namespace |
OLD | NEW |