Index: ios/chrome/browser/ui/tab_switcher/tab_switcher_model_unittest.mm |
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_model_unittest.mm b/ios/chrome/browser/ui/tab_switcher/tab_switcher_model_unittest.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..08e5726d5d98630c075e0e801565d256edf56b1d |
--- /dev/null |
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_model_unittest.mm |
@@ -0,0 +1,434 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "ios/chrome/browser/ui/tab_switcher/tab_switcher_model.h" |
+ |
+#include "base/mac/scoped_nsobject.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h" |
+#include "ios/chrome/browser/ui/ntp/recent_tabs/synced_sessions.h" |
+#include "ios/chrome/browser/ui/tab_switcher/session_changes.h" |
+#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_model_private.h" |
+#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_utils.h" |
+#include "testing/platform_test.h" |
+#import "third_party/ocmock/OCMock/OCMock.h" |
+ |
+namespace { |
+ |
+// A lightweight SessionChanges. |
+struct LightSessionChanges { |
+ std::vector<int> updates; |
+ std::vector<int> deletions; |
+ std::vector<int> insertions; |
+}; |
+ |
+// A lightweight DistantTab. |
+class LightDT { |
+ public: |
+ LightDT(std::string const& urlSuffix, std::string const& title = "") |
+ : url("http://www.foo.com/" + urlSuffix), title(title) {} |
+ std::string url; |
+ std::string title; |
+}; |
+ |
+// A lightweight DistantSession. |
+struct LightDS { |
+ std::string tag; |
+ std::vector<LightDT> distantTabs; |
+}; |
+ |
+// A lightweight SyncedSessions. |
+using LightSS = std::vector<LightDS>; |
+ |
+} // namespace |
+ |
+// Helper class to test calls to the TabSwitcherModelDelegate. |
+@interface DelegateTester : NSObject<TabSwitcherModelDelegate> |
+@end |
+ |
+@implementation DelegateTester { |
+ base::scoped_nsobject<NSArray> _expectedSessionRemoved; |
+ base::scoped_nsobject<NSArray> _expectedSessionInserted; |
+ std::set<std::string> _expectedTagsOfTheSessionsNeedingUpdates; |
+} |
+ |
+- (void)expectSessionsRemoved:(NSArray*)expectedIndexes { |
+ _expectedSessionRemoved.reset([expectedIndexes retain]); |
+} |
+ |
+- (void)expectSessionsInserted:(NSArray*)expectedIndexes { |
+ _expectedSessionInserted.reset([expectedIndexes retain]); |
+} |
+ |
+- (void)expectSessionMayNeedUpdate:(std::set<std::string> const&)tags { |
+ _expectedTagsOfTheSessionsNeedingUpdates = tags; |
+} |
+ |
+- (void)verify { |
+ EXPECT_EQ(0UL, [_expectedSessionRemoved count]); |
+ EXPECT_EQ(0UL, [_expectedSessionInserted count]); |
+ EXPECT_EQ(0UL, _expectedTagsOfTheSessionsNeedingUpdates.size()); |
+} |
+ |
+#pragma mark - TabSwitcherModelDelegate |
+ |
+- (void)distantSessionsRemovedAtSortedIndexes:(NSArray*)removedIndexes |
+ insertedAtSortedIndexes:(NSArray*)insertedIndexes { |
+ EXPECT_TRUE(removedIndexes == _expectedSessionRemoved.get() || |
+ [removedIndexes isEqualToArray:_expectedSessionRemoved.get()]); |
+ EXPECT_TRUE(insertedIndexes == _expectedSessionInserted.get() || |
+ [insertedIndexes isEqualToArray:_expectedSessionInserted.get()]); |
+ _expectedSessionRemoved.reset(); |
+ _expectedSessionInserted.reset(); |
+} |
+ |
+- (void)distantSessionMayNeedUpdate:(std::string const&)tag { |
+ EXPECT_EQ(1UL, _expectedTagsOfTheSessionsNeedingUpdates.erase(tag)); |
+} |
+ |
+- (void)localSessionMayNeedUpdate:(ios_internal::SessionType)type { |
+ NOTREACHED(); |
+} |
+ |
+- (void)signInPanelChangedTo:(TabSwitcherSignInPanelsType)panelType { |
+ NOTREACHED(); |
+} |
+- (CGSize)sizeForItemAtIndex:(NSUInteger)index |
+ inSession:(ios_internal::SessionType)session { |
+ return CGSizeZero; |
+} |
+@end |
+ |
+namespace { |
+ |
+class TabSwitcherModelTest : public PlatformTest { |
+ protected: |
+ void SetUp() override { delegate_.reset([[DelegateTester alloc] init]); } |
+ |
+ void AddSessionToSessions(synced_sessions::SyncedSessions& sessions, |
+ std::string const& session_tag, |
+ std::vector<size_t> const& tab_ids) { |
+ std::vector<LightDT> light_distant_tabs; |
+ for (size_t tab_id : tab_ids) { |
+ light_distant_tabs.push_back(LightDT(std::to_string(tab_id), "")); |
+ } |
+ AddDetailedSessionToSessions(sessions, session_tag, light_distant_tabs); |
+ } |
+ |
+ std::unique_ptr<synced_sessions::SyncedSessions> syncedSessionsFromTestData( |
+ LightSS const& lightSyncedSessions) { |
+ auto syncedSessions = base::MakeUnique<synced_sessions::SyncedSessions>(); |
+ for (auto& lightdistantSession : lightSyncedSessions) { |
+ AddDetailedSessionToSessions(*syncedSessions, lightdistantSession.tag, |
+ lightdistantSession.distantTabs); |
+ } |
+ return syncedSessions; |
+ } |
+ |
+ void AddDetailedSessionToSessions( |
+ synced_sessions::SyncedSessions& sessions, |
+ std::string const& session_tag, |
+ std::vector<LightDT> const& light_distant_tabs) { |
+ // Create a new DistantSession and initialize it with |sessionTag| and |
+ // |tabTags|. |
+ auto distant_session = base::MakeUnique<synced_sessions::DistantSession>(); |
+ distant_session->tag = session_tag; |
+ |
+ for (auto const& light_distant_tab : light_distant_tabs) { |
+ auto temp_tab = base::MakeUnique<synced_sessions::DistantTab>(); |
+ temp_tab->virtual_url = GURL(light_distant_tab.url); |
+ temp_tab->title = base::ASCIIToUTF16(light_distant_tab.title); |
+ distant_session->tabs.push_back(std::move(temp_tab)); |
+ } |
+ |
+ sessions.AddDistantSessionForTest(std::move(distant_session)); |
+ } |
+ base::scoped_nsobject<DelegateTester> delegate_; |
+ base::scoped_nsobject<TabSwitcherModel> model_; |
+}; |
+ |
+TEST_F(TabSwitcherModelTest, TestNoDiffs) { |
+ // Test with 2 empty sessions. |
+ synced_sessions::SyncedSessions old_sessions_1; |
+ synced_sessions::SyncedSessions new_sessions_1; |
+ [TabSwitcherModel notifyDelegate:delegate_ |
+ aboutChangeFrom:old_sessions_1 |
+ to:new_sessions_1]; |
+ [delegate_ verify]; |
+ |
+ // Test with 2 identical sessions. |
+ synced_sessions::SyncedSessions old_sessions_2; |
+ synced_sessions::SyncedSessions new_sessions_2; |
+ AddSessionToSessions(old_sessions_2, "Foo", {0, 1}); |
+ AddSessionToSessions(new_sessions_2, "Foo", {0, 1}); |
+ [delegate_ expectSessionMayNeedUpdate:{"Foo"}]; |
+ |
+ [TabSwitcherModel notifyDelegate:delegate_ |
+ aboutChangeFrom:old_sessions_2 |
+ to:new_sessions_2]; |
+ [delegate_ verify]; |
+} |
+ |
+TEST_F(TabSwitcherModelTest, TestSessionDiffs) { |
+ // Test with 1 session inserted. |
+ synced_sessions::SyncedSessions old_sessions_1; |
+ synced_sessions::SyncedSessions new_sessions_1; |
+ AddSessionToSessions(new_sessions_1, "Foo", {0}); |
+ [delegate_ expectSessionsInserted:@[ @0 ]]; |
+ [TabSwitcherModel notifyDelegate:delegate_ |
+ aboutChangeFrom:old_sessions_1 |
+ to:new_sessions_1]; |
+ [delegate_ verify]; |
+ |
+ // Test with 2 sessions inserted. |
+ synced_sessions::SyncedSessions old_sessions_2; |
+ synced_sessions::SyncedSessions new_sessions_2; |
+ AddSessionToSessions(old_sessions_2, "Bar", {0}); |
+ AddSessionToSessions(new_sessions_2, "Foo", {0}); |
+ AddSessionToSessions(new_sessions_2, "Bar", {0}); |
+ AddSessionToSessions(new_sessions_2, "Qux", {0}); |
+ [delegate_ expectSessionsInserted:@[ @0, @2 ]]; |
+ [delegate_ expectSessionMayNeedUpdate:{"Bar"}]; |
+ [TabSwitcherModel notifyDelegate:delegate_ |
+ aboutChangeFrom:old_sessions_2 |
+ to:new_sessions_2]; |
+ [delegate_ verify]; |
+ |
+ // Test with 2 sessions removed. |
+ synced_sessions::SyncedSessions old_sessions_3; |
+ synced_sessions::SyncedSessions new_sessions_3; |
+ AddSessionToSessions(old_sessions_3, "Foo", {0}); |
+ AddSessionToSessions(old_sessions_3, "Bar", {0}); |
+ AddSessionToSessions(old_sessions_3, "Qux", {0}); |
+ AddSessionToSessions(new_sessions_3, "Bar", {0}); |
+ [delegate_ expectSessionsRemoved:@[ @0, @2 ]]; |
+ [delegate_ expectSessionMayNeedUpdate:{"Bar"}]; |
+ [TabSwitcherModel notifyDelegate:delegate_ |
+ aboutChangeFrom:old_sessions_3 |
+ to:new_sessions_3]; |
+ [delegate_ verify]; |
+ |
+ // Test with 2 sessions inserted, 2 removed. |
+ synced_sessions::SyncedSessions old_sessions_4; |
+ synced_sessions::SyncedSessions new_sessions_4; |
+ AddSessionToSessions(old_sessions_4, "Deleted1", {0}); |
+ AddSessionToSessions(old_sessions_4, "a", {0}); |
+ AddSessionToSessions(old_sessions_4, "Delete2", {0}); |
+ |
+ AddSessionToSessions(new_sessions_4, "b", {0}); |
+ AddSessionToSessions(new_sessions_4, "a", {0}); |
+ AddSessionToSessions(new_sessions_4, "c", {0}); |
+ [delegate_ expectSessionsRemoved:@[ @0, @2 ]]; |
+ [delegate_ expectSessionsInserted:@[ @0, @2 ]]; |
+ [delegate_ expectSessionMayNeedUpdate:{"a"}]; |
+ |
+ [TabSwitcherModel notifyDelegate:delegate_ |
+ aboutChangeFrom:old_sessions_4 |
+ to:new_sessions_4]; |
+ [delegate_ verify]; |
+} |
+ |
+TEST_F(TabSwitcherModelTest, TestTabsSimpleDiffs) { |
+ // Test with 2 tabs added. |
+ synced_sessions::SyncedSessions old_sessions_1; |
+ synced_sessions::SyncedSessions new_sessions_1; |
+ AddSessionToSessions(old_sessions_1, "Bar", {100}); |
+ AddSessionToSessions(old_sessions_1, "Foo", {100}); |
+ AddSessionToSessions(new_sessions_1, "Bar", {100}); |
+ AddSessionToSessions(new_sessions_1, "Foo", {99, 100, 101}); |
+ [delegate_ expectSessionMayNeedUpdate:{"Bar", "Foo"}]; |
+ |
+ [TabSwitcherModel notifyDelegate:delegate_ |
+ aboutChangeFrom:old_sessions_1 |
+ to:new_sessions_1]; |
+ [delegate_ verify]; |
+ |
+ // Test with 2 tabs removed. |
+ synced_sessions::SyncedSessions old_sessions_2; |
+ synced_sessions::SyncedSessions new_sessions_2; |
+ AddSessionToSessions(old_sessions_2, "Bar", {0}); |
+ AddSessionToSessions(old_sessions_2, "Foo", {100, 101, 102}); |
+ AddSessionToSessions(new_sessions_2, "Bar", {0}); |
+ AddSessionToSessions(new_sessions_2, "Foo", {100}); |
+ [delegate_ expectSessionMayNeedUpdate:{"Bar", "Foo"}]; |
+ |
+ [TabSwitcherModel notifyDelegate:delegate_ |
+ aboutChangeFrom:old_sessions_2 |
+ to:new_sessions_2]; |
+ [delegate_ verify]; |
+ |
+ // Test with 2 tabs updated. |
+ synced_sessions::SyncedSessions old_sessions_3; |
+ synced_sessions::SyncedSessions new_sessions_3; |
+ std::vector<LightDT> old_tabs_3; |
+ old_tabs_3.push_back(LightDT("1", "1")); |
+ old_tabs_3.push_back(LightDT("2", "2")); |
+ old_tabs_3.push_back(LightDT("3", "3")); |
+ std::vector<LightDT> new_tabs_3; |
+ new_tabs_3.push_back(LightDT("1", "1 bis")); |
+ new_tabs_3.push_back(LightDT("2", "2")); |
+ new_tabs_3.push_back(LightDT("a", "3")); |
+ AddDetailedSessionToSessions(old_sessions_3, "Foo", old_tabs_3); |
+ AddDetailedSessionToSessions(new_sessions_3, "Foo", new_tabs_3); |
+ [delegate_ expectSessionMayNeedUpdate:{"Foo"}]; |
+ [TabSwitcherModel notifyDelegate:delegate_ |
+ aboutChangeFrom:old_sessions_3 |
+ to:new_sessions_3]; |
+ [delegate_ verify]; |
+} |
+ |
+TEST_F(TabSwitcherModelTest, TestTabsDeletionInsertions) { |
+ synced_sessions::SyncedSessions old_sessions; |
+ synced_sessions::SyncedSessions new_sessions; |
+ std::vector<LightDT> old_tabs; |
+ old_tabs.push_back(LightDT("0", "0")); |
+ old_tabs.push_back(LightDT("1", "1")); |
+ old_tabs.push_back(LightDT("2", "2")); |
+ old_tabs.push_back(LightDT("3", "3")); |
+ std::vector<LightDT> new_tabs; |
+ new_tabs.push_back(LightDT("1", "1")); |
+ new_tabs.push_back(LightDT("2", "2")); |
+ new_tabs.push_back(LightDT("3", "3")); |
+ new_tabs.push_back(LightDT("0", "0")); |
+ AddDetailedSessionToSessions(old_sessions, "Foo", old_tabs); |
+ AddDetailedSessionToSessions(new_sessions, "Foo", new_tabs); |
+ [delegate_ expectSessionMayNeedUpdate:{"Foo"}]; |
+ [TabSwitcherModel notifyDelegate:delegate_ |
+ aboutChangeFrom:old_sessions |
+ to:new_sessions]; |
+ [delegate_ verify]; |
+} |
+ |
+TEST_F(TabSwitcherModelTest, TestTabsWithInterleavedDiffs) { |
+ synced_sessions::SyncedSessions old_sessions; |
+ synced_sessions::SyncedSessions new_sessions; |
+ std::vector<LightDT> old_tabs; |
+ old_tabs.push_back(LightDT("0", "0")); |
+ old_tabs.push_back(LightDT("1", "1")); // gets deleted |
+ old_tabs.push_back(LightDT("2", "2")); |
+ old_tabs.push_back(LightDT("3", "3")); // gets updated |
+ old_tabs.push_back(LightDT("4", "4")); |
+ old_tabs.push_back(LightDT("6", "6")); |
+ old_tabs.push_back(LightDT("7", "7")); // gets updated |
+ std::vector<LightDT> new_tabs; |
+ new_tabs.push_back(LightDT("0", "0")); |
+ new_tabs.push_back(LightDT("2", "2")); |
+ new_tabs.push_back(LightDT("3", "3 bis")); |
+ new_tabs.push_back(LightDT("4", "4")); |
+ new_tabs.push_back(LightDT("5", "5")); // is inserted |
+ new_tabs.push_back(LightDT("6", "6")); |
+ new_tabs.push_back(LightDT("7", "7 bis")); |
+ AddDetailedSessionToSessions(old_sessions, "Foo", old_tabs); |
+ AddDetailedSessionToSessions(new_sessions, "Foo", new_tabs); |
+ [delegate_ expectSessionMayNeedUpdate:{"Foo"}]; |
+ [TabSwitcherModel notifyDelegate:delegate_ |
+ aboutChangeFrom:old_sessions |
+ to:new_sessions]; |
+ [delegate_ verify]; |
+} |
+ |
+// Tests that the reordering of sessions does not result in calls to the |
+// delegate. |
+TEST_F(TabSwitcherModelTest, TestReorderingOfSessions) { |
+ std::vector<LightDS> old_sessions_data = { |
+ {"A", {LightDT("a"), LightDT("b")}}, |
+ {"B", {LightDT("a"), LightDT("b")}}, |
+ {"C", {LightDT("a"), LightDT("b")}}, |
+ }; |
+ std::vector<LightDS> new_sessions_data = { |
+ {"C", {LightDT("a"), LightDT("b")}}, |
+ {"A", {LightDT("a"), LightDT("b")}}, |
+ {"B", {LightDT("a"), LightDT("b")}}, |
+ }; |
+ auto old_sessions = syncedSessionsFromTestData(old_sessions_data); |
+ auto new_sessions = syncedSessionsFromTestData(new_sessions_data); |
+ [delegate_ expectSessionMayNeedUpdate:{"A", "B", "C"}]; |
+ [TabSwitcherModel notifyDelegate:delegate_ |
+ aboutChangeFrom:*old_sessions |
+ to:*new_sessions]; |
+ [delegate_ verify]; |
+} |
+ |
+// Tests that the reordering of sessions does not result in wrong deletion |
+// indexes. |
+TEST_F(TabSwitcherModelTest, TestReorderingOfSessionsWithDeletion) { |
+ std::vector<LightDS> old_sessions_data = { |
+ {"A", {LightDT("a"), LightDT("b"), LightDT("c")}}, |
+ {"B", {LightDT("a"), LightDT("b")}}, // deleted |
+ {"C", {LightDT("a"), LightDT("b")}}, |
+ {"D", {LightDT("a"), LightDT("b")}}, // deleted |
+ }; |
+ std::vector<LightDS> new_sessions_data = { |
+ {"C", {LightDT("a"), LightDT("b")}}, |
+ {"A", {LightDT("a"), LightDT("b"), LightDT("c")}}, |
+ }; |
+ auto old_sessions = syncedSessionsFromTestData(old_sessions_data); |
+ auto new_sessions = syncedSessionsFromTestData(new_sessions_data); |
+ [delegate_ expectSessionsRemoved:@[ @1, @3 ]]; |
+ [delegate_ expectSessionMayNeedUpdate:{"A", "C"}]; |
+ [TabSwitcherModel notifyDelegate:delegate_ |
+ aboutChangeFrom:*old_sessions |
+ to:*new_sessions]; |
+ [delegate_ verify]; |
+} |
+ |
+// Tests that the reordering of sessions does not result in wrong insertion |
+// indexes. |
+TEST_F(TabSwitcherModelTest, TestReorderingOfSessionsWithInsertion) { |
+ std::vector<LightDS> old_sessions_data = { |
+ {"A", {LightDT("a"), LightDT("b")}}, |
+ {"B", {LightDT("a"), LightDT("b")}}, |
+ {"C", {LightDT("a"), LightDT("b")}}, |
+ }; |
+ std::vector<LightDS> new_sessions_data = { |
+ {"B", {LightDT("a"), LightDT("b")}}, |
+ {"D", {LightDT("a"), LightDT("b")}}, // inserted |
+ {"C", {LightDT("a"), LightDT("b")}}, |
+ {"A", {LightDT("a"), LightDT("b")}}, |
+ {"E", {LightDT("a"), LightDT("b")}}, // inserted |
+ }; |
+ auto old_sessions = syncedSessionsFromTestData(old_sessions_data); |
+ auto new_sessions = syncedSessionsFromTestData(new_sessions_data); |
+ [delegate_ expectSessionsInserted:@[ @1, @4 ]]; |
+ [delegate_ expectSessionMayNeedUpdate:{"A", "B", "C"}]; |
+ [TabSwitcherModel notifyDelegate:delegate_ |
+ aboutChangeFrom:*old_sessions |
+ to:*new_sessions]; |
+ [delegate_ verify]; |
+} |
+ |
+TEST_F(TabSwitcherModelTest, TestTabsWithSessionAndTabChanges) { |
+ std::vector<LightDS> old_sessions_data = { |
+ {"0", {LightDT("a"), LightDT("b")}}, |
+ {"1", {LightDT("a"), LightDT("b")}}, // session deleted |
+ {"2", {LightDT("a"), LightDT("b"), LightDT("c")}}, |
+ {"3", {LightDT("b")}}, |
+ {"4", {LightDT("a"), LightDT("b"), LightDT("c")}}, // tab deleted |
+ {"5", {LightDT("a"), LightDT("b")}}, |
+ }; |
+ |
+ std::vector<LightDS> new_sessions_data = { |
+ {"A", {LightDT("a")}}, // session inserted |
+ {"0", {LightDT("a"), LightDT("b")}}, |
+ {"2", {LightDT("A"), LightDT("b"), LightDT("C")}}, // tabs updated |
+ {"3", {LightDT("a"), LightDT("b")}}, // tab inserted |
+ {"4", {LightDT("b")}}, |
+ {"5", {LightDT("a"), LightDT("b")}}, |
+ }; |
+ |
+ auto old_sessions = syncedSessionsFromTestData(old_sessions_data); |
+ auto new_sessions = syncedSessionsFromTestData(new_sessions_data); |
+ |
+ [delegate_ expectSessionMayNeedUpdate:{"0", "2", "3", "4", "5"}]; |
+ [delegate_ expectSessionsRemoved:@[ @1 ]]; |
+ [delegate_ expectSessionsInserted:@[ @0 ]]; |
+ |
+ [TabSwitcherModel notifyDelegate:delegate_ |
+ aboutChangeFrom:*old_sessions |
+ to:*new_sessions]; |
+ [delegate_ verify]; |
+} |
+ |
+} // namespace |