Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4217)

Unified Diff: chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc

Issue 11298004: alternate ntp: add "Recent Tabs" submenu to wrench menu (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: addressed scott's comments Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc
diff --git a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..08e611695054701543c16b1a6a56b4b6991b5ee2
--- /dev/null
+++ b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model_unittest.cc
@@ -0,0 +1,417 @@
+// Copyright (c) 2012 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 "chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h"
+
+#include "base/rand_util.h"
+#include "base/string_number_conversions.h"
+#include "base/stringprintf.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/sessions/session_types.h"
+#include "chrome/browser/sessions/session_types_test_helper.h"
+#include "chrome/browser/sync/glue/session_model_associator.h"
+#include "chrome/browser/sync/glue/synced_session.h"
+#include "chrome/browser/sync/profile_sync_service_mock.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/test/base/browser_with_test_window_test.h"
+#include "chrome/test/base/menu_model_test.h"
+#include "chrome/test/base/testing_profile.h"
+#include "grit/generated_resources.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kBaseSessionTag[] = "session_tag";
+const char kBaseSessionName[] = "session_name";
+const char kBaseTabUrl[] = "http://foo/?";
+const char kTabTitleFormat[] = "session=%d;window=%d;tab=%d";
+
+struct TabTime {
+ TabTime(SessionID::id_type session_id,
+ SessionID::id_type window_id,
+ int tab_idx,
+ base::Time timestamp)
+ : session_id(session_id),
+ window_id(window_id),
+ tab_idx(tab_idx),
+ timestamp(timestamp) {}
+
+ SessionID::id_type session_id;
+ SessionID::id_type window_id;
+ int tab_idx;
+ base::Time timestamp;
+};
+
+bool SortTabTimesByRecency(const TabTime& t1, const TabTime& t2) {
+ return t1.timestamp > t2.timestamp;
+}
+
+std::string ToSessionTag(SessionID::id_type session_id) {
+ return std::string(kBaseSessionTag + base::IntToString(session_id));
+}
+
+std::string ToSessionName(SessionID::id_type session_id) {
+ return std::string(kBaseSessionName + base::IntToString(session_id));
+}
+
+std::string ToTabTitle(SessionID::id_type session_id,
+ SessionID::id_type window_id,
+ int tab_idx) {
+ return base::StringPrintf(kTabTitleFormat, session_id, window_id, tab_idx);
+}
+
+std::string ToTabUrl(SessionID::id_type session_id,
+ SessionID::id_type window_id,
+ int tab_idx) {
+ return std::string(kBaseTabUrl + ToTabTitle(session_id, window_id, tab_idx));
+}
+
+// Copies parts of MenuModelTest::Delegate and combines them with the
+// RecentTabsSubMenuModel since RecentTabsSubMenuModel is a
+// SimpleMenuModel::Delegate and not just derived from SimpleMenuModel.
+class TestRecentTabsSubMenuModel : public RecentTabsSubMenuModel {
+ public:
+ TestRecentTabsSubMenuModel(ui::AcceleratorProvider* provider,
+ Browser* browser,
+ bool can_restore_tab)
+ : RecentTabsSubMenuModel(provider, browser),
+ can_restore_tab_(can_restore_tab),
+ execute_count_(0),
+ enable_count_(0) {
+ }
+
+ TestRecentTabsSubMenuModel(ui::AcceleratorProvider* provider,
+ Browser* browser,
+ browser_sync::SessionModelAssociator* associator,
+ bool can_restore_tab)
+ : RecentTabsSubMenuModel(provider, browser, associator, true),
+ can_restore_tab_(can_restore_tab),
+ execute_count_(0),
+ enable_count_(0) {
+ }
+
+ // Testing overrides to ui::SimpleMenuModel::Delegate:
+ virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
+ bool val = command_id == IDC_RESTORE_TAB ? can_restore_tab_ :
+ RecentTabsSubMenuModel::IsCommandIdEnabled(command_id);
+ if (val)
+ ++enable_count_;
+ return val;
+ }
+
+ virtual void ExecuteCommand(int command_id) OVERRIDE {
+ ++execute_count_;
+ }
+
+ bool can_restore_tab_;
+ int execute_count_;
+ int mutable enable_count_; // Mutable because IsCommandIdEnabledAt is const.
+};
+
+class TestSessionModelAssociator{
+ public:
+ TestSessionModelAssociator()
+ : sync_service_(&profile_),
+ model_associator_(&sync_service_, true) {}
+
+ void InitWindowPb(int num_tabs, SessionID::id_type* tab_id,
+ sync_pb::SessionWindow* window_pb) {
+ for (int i = 0; i < num_tabs; ++i)
+ window_pb->add_tab((*tab_id)++);
+ window_pb->set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED);
+ window_pb->set_selected_tab_index(0);
+ }
+
+ void CreateSession(SessionID::id_type session_id) {
+ browser_sync::SyncedSession* session = GetSession(session_id);
+ session->device_type = browser_sync::SyncedSession::TYPE_CHROMEOS;
+ session->session_name = ToSessionName(session_id);
+ }
+
+ void PutWindowInSessionAndTracker(SessionID::id_type session_id,
+ SessionID::id_type window_id,
+ const sync_pb::SessionWindow& window_pb) {
akalin 2012/11/10 00:08:40 actually, can't you just use the public function A
kuan 2012/11/10 17:07:04 i tried implementing building the SessionSpecifics
kuan 2012/11/10 19:34:46 Done.
+ tracker().PutWindowInSession(ToSessionTag(session_id), window_id);
+ browser_sync::SyncedSession* session = GetSession(session_id);
+ SessionWindow* window = session->windows[window_id];
+ browser_sync::SessionModelAssociator::
+ PopulateSessionWindowFromSpecificsForTest(
+ ToSessionTag(session_id), window_pb, base::Time(), window,
+ &tracker());
+ ASSERT_EQ(static_cast<size_t>(window_pb.tab_size()), window->tabs.size());
+ }
+
+ void MakeTabSyncable(SessionID::id_type session_id,
+ SessionID::id_type window_id,
+ int tab_idx,
+ base::Time timestamp) {
+ browser_sync::SyncedSession* session = GetSession(session_id);
akalin 2012/11/10 00:08:40 can you do this using public functions? Maybe use
kuan 2012/11/10 19:34:46 Done.
+ SessionTab* tab = session->windows[window_id]->tabs[tab_idx];
+ tab->navigations.push_back(SessionTypesTestHelper::CreateNavigation(
+ ToTabUrl(session_id, window_id, tab_idx),
+ ToTabTitle(session_id, window_id, tab_idx)));
+ tab->current_navigation_index = 0;
+ tab->timestamp = timestamp;
+ session->modified_time = timestamp;
+ }
+
+ browser_sync::SyncedSession* GetSession(SessionID::id_type session_id) {
+ return tracker().GetSession(ToSessionTag(session_id));
+ }
+
+ browser_sync::SyncedSessionTracker& tracker() {
+ return model_associator_.GetSyncedSessionTrackerForTest();
+ }
+
+ browser_sync::SessionModelAssociator& model_associator() {
+ return model_associator_;
+ }
+
+ private:
+ TestingProfile profile_;
+ testing::NiceMock<ProfileSyncServiceMock> sync_service_;
+ browser_sync::SessionModelAssociator model_associator_;
+};
+
+} // namespace
+
+class RecentTabsSubMenuModelTest : public BrowserWithTestWindowTest,
+ public ui::AcceleratorProvider {
+ public:
+ // Don't handle accelerators.
+ virtual bool GetAcceleratorForCommandId(
+ int command_id,
+ ui::Accelerator* accelerator) OVERRIDE {
+ return false;
+ }
+};
+
+TEST_F(RecentTabsSubMenuModelTest, NoTabs) {
+ TestRecentTabsSubMenuModel model(this, browser(), false);
+
+ // Expected menu:
+ // Menu index Menu items
+ // --------------------------------------
+ // 0 Reopen closed tab
+ // 1 <separator>
+ // 2 No tabs from other Devices
+
+ int num_items = model.GetItemCount();
+ EXPECT_EQ(3, num_items);
+ EXPECT_FALSE(model.IsEnabledAt(0));
+ EXPECT_FALSE(model.IsEnabledAt(2));
+ EXPECT_EQ(0, model.enable_count_);
+}
+
+TEST_F(RecentTabsSubMenuModelTest, ReopenClosedTab) {
+ TestRecentTabsSubMenuModel model(this, browser(), true);
+ // Menu contents are identical to NoTabs test.
+ int num_items = model.GetItemCount();
+ EXPECT_EQ(3, num_items);
+ EXPECT_TRUE(model.IsEnabledAt(0));
+ model.ActivatedAt(0);
+ EXPECT_FALSE(model.IsEnabledAt(2));
+ EXPECT_EQ(1, model.enable_count_);
+ EXPECT_EQ(1, model.execute_count_);
+}
+
+TEST_F(RecentTabsSubMenuModelTest, OtherDevices) {
+ TestSessionModelAssociator associator;
+ SessionID::id_type id = 0;
+ // Tabs are populated in decreasing timestamp.
+ base::Time now = base::Time::Now();
+
+ // Create 1st session : 1 window, 3 tabs
+ SessionID::id_type session_id = id++;
+ associator.CreateSession(session_id);
+ SessionID::id_type window_id = id++;
+ sync_pb::SessionWindow window_pb;
+ associator.InitWindowPb(3, &id, &window_pb);
+ associator.PutWindowInSessionAndTracker(session_id, window_id, window_pb);
+ base::Time timestamp = now - base::TimeDelta::FromMinutes(window_id * 10);
+ associator.MakeTabSyncable(session_id, window_id, 0, timestamp);
+ associator.MakeTabSyncable(session_id, window_id, 1,
+ timestamp - base::TimeDelta::FromMinutes(1));
+ associator.MakeTabSyncable(session_id, window_id, 2,
+ timestamp - base::TimeDelta::FromMinutes(2));
+ ASSERT_EQ(3U, associator.tracker().num_synced_tabs(ToSessionTag(session_id)));
akalin 2012/11/10 00:08:40 can you get the num synced tabs using GetForeignSe
kuan 2012/11/10 19:34:46 Done.
+
+ // Create 2nd session : 2 windows, 1 tab in 1st window, 2 tabs in 2nd window
+ session_id = id++;
+ associator.CreateSession(session_id);
+ window_id = id++;
+ sync_pb::SessionWindow window_pb0;
+ associator.InitWindowPb(1, &id, &window_pb0);
+ associator.PutWindowInSessionAndTracker(session_id, window_id, window_pb0);
+ timestamp = now - base::TimeDelta::FromMinutes(window_id * 10);
+ associator.MakeTabSyncable(session_id, window_id, 0, timestamp);
+ ASSERT_EQ(1U, associator.tracker().num_synced_tabs(ToSessionTag(session_id)));
+ window_id = id++;
+ sync_pb::SessionWindow window_pb1;
+ associator.InitWindowPb(2, &id, &window_pb1);
+ associator.PutWindowInSessionAndTracker(session_id, window_id, window_pb1);
+ timestamp = now - base::TimeDelta::FromMinutes(window_id * 10);
+ associator.MakeTabSyncable(session_id, window_id, 0, timestamp);
+ associator.MakeTabSyncable(session_id, window_id, 1,
+ timestamp - base::TimeDelta::FromMinutes(1));
+ ASSERT_EQ(3U, associator.tracker().num_synced_tabs(ToSessionTag(session_id)));
+
+ ASSERT_EQ(2U, associator.tracker().num_synced_sessions());
akalin 2012/11/10 00:08:40 GetAllForeignSessions()?
kuan 2012/11/10 19:34:46 Done.
+
+ TestRecentTabsSubMenuModel model(this, browser(),
+ &associator.model_associator(), true);
+
+ // Expected menu:
+ // - first inserted tab is most recent and hence is top
+ // Menu index Menu items
+ // --------------------------------------
+ // 0 Reopen closed tab
+ // 1 <separator>
+ // 2 <section header for 1st session>
+ // 3-5 <3 tabs of the only window of session 0>
+ // 6 <separator>
+ // 7 <section header for 2nd session>
+ // 8 <the only tab of window 0 of session 1>
+ // 9-10 <2 tabs of window 1 of session 2>
+
+ int num_items = model.GetItemCount();
+ EXPECT_EQ(11, num_items);
+ model.ActivatedAt(0);
+ EXPECT_TRUE(model.IsEnabledAt(0));
+ model.ActivatedAt(3);
+ EXPECT_TRUE(model.IsEnabledAt(3));
+ model.ActivatedAt(4);
+ EXPECT_TRUE(model.IsEnabledAt(4));
+ model.ActivatedAt(5);
+ EXPECT_TRUE(model.IsEnabledAt(5));
+ model.ActivatedAt(8);
+ EXPECT_TRUE(model.IsEnabledAt(8));
+ model.ActivatedAt(9);
+ EXPECT_TRUE(model.IsEnabledAt(9));
+ model.ActivatedAt(10);
+ EXPECT_TRUE(model.IsEnabledAt(10));
+ EXPECT_EQ(7, model.enable_count_);
+ EXPECT_EQ(7, model.execute_count_);
+}
+
+TEST_F(RecentTabsSubMenuModelTest, MaxSessionsAndRecency) {
+ TestSessionModelAssociator associator;
+ SessionID::id_type id = 0;
+ base::Time now = base::Time::Now();
+
+ // Create 4 sessions : each session has 1 window with 1 tab each .
+ std::vector<TabTime> tab_times;
+ for (int i = 0; i < 4; ++i) {
+ SessionID::id_type session_id = id++;
+ associator.CreateSession(session_id);
+ SessionID::id_type window_id = id++;
+ sync_pb::SessionWindow window_pb;
+ associator.InitWindowPb(1, &id, &window_pb);
+ associator.PutWindowInSessionAndTracker(session_id, window_id, window_pb);
+ base::Time timestamp = now +
+ base::TimeDelta::FromMinutes(base::RandUint64());
+ associator.MakeTabSyncable(session_id, window_id, 0, timestamp);
+ tab_times.push_back(TabTime(session_id, window_id, 0, timestamp));
+ ASSERT_EQ(1U,
+ associator.tracker().num_synced_tabs(ToSessionTag(session_id)));
+ }
+ ASSERT_EQ(4U, associator.tracker().num_synced_sessions());
+
+ TestRecentTabsSubMenuModel model(this, browser(),
+ &associator.model_associator(), true);
+
+ // Expected menu:
+ // - max sessions is 3, so only 3 most-recent sessions will show
+ // Menu index Menu items
+ // --------------------------------------
+ // 0 Reopen closed tab
+ // 1 <separator>
+ // 2 <section header for 1st session>
+ // 3 <the only tab of the only window of session 3>
+ // 4 <separator>
+ // 5 <section header for 2nd session>
+ // 6 <the only tab of the only window of session 2>
+ // 7 <separator>
+ // 8 <section header for 3rd session>
+ // 9 <the only tab of the only window of session 1>
+
+ int num_items = model.GetItemCount();
+ EXPECT_EQ(10, num_items);
+
+ sort(tab_times.begin(), tab_times.end(), SortTabTimesByRecency);
+ EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[0].session_id,
+ tab_times[0].window_id,
+ tab_times[0].tab_idx)),
+ model.GetLabelAt(3));
+ EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[1].session_id,
+ tab_times[1].window_id,
+ tab_times[1].tab_idx)),
+ model.GetLabelAt(6));
+ EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[2].session_id,
+ tab_times[2].window_id,
+ tab_times[2].tab_idx)),
+ model.GetLabelAt(9));
+}
+
+TEST_F(RecentTabsSubMenuModelTest, MaxTabsPerSessionAndRecency) {
+ TestSessionModelAssociator associator;
+ SessionID::id_type id = 0;
+ base::Time now = base::Time::Now();
+
+ // Create a session: 2 windows with 5 tabs each.
+ std::vector<TabTime> tab_times;
+ SessionID::id_type session_id = id++;
+ associator.CreateSession(session_id);
+ for (int j = 0; j < 2; ++j) {
+ SessionID::id_type window_id = id++;
+ sync_pb::SessionWindow window_pb;
+ associator.InitWindowPb(5, &id, &window_pb);
+ associator.PutWindowInSessionAndTracker(session_id, window_id, window_pb);
+ for (int k = 0; k < 5; ++k) {
+ base::Time timestamp(now +
+ base::TimeDelta::FromMinutes(base::RandUint64()));
+ associator.MakeTabSyncable(session_id, window_id, k, timestamp);
+ tab_times.push_back(TabTime(session_id, window_id, k, timestamp));
+ }
+ }
+ ASSERT_EQ(10U,
+ associator.tracker().num_synced_tabs(ToSessionTag(session_id)));
+ ASSERT_EQ(1U, associator.tracker().num_synced_sessions());
+
+ TestRecentTabsSubMenuModel model(this, browser(),
+ &associator.model_associator(), true);
+
+ // Expected menu:
+ // - max tabs per session is 4, so only 4 most-recent tabs will show,
+ // independent of which window they came from
+ // Menu index Menu items
+ // --------------------------------------
+ // 0 Reopen closed tab
+ // 1 <separator>
+ // 2 <section header for session>
+ // 3-6 <4 most-recent tabs of session>
+
+ int num_items = model.GetItemCount();
+ EXPECT_EQ(7, num_items);
+
+ sort(tab_times.begin(), tab_times.end(), SortTabTimesByRecency);
+ EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[0].session_id,
+ tab_times[0].window_id,
+ tab_times[0].tab_idx)),
+ model.GetLabelAt(3));
+ EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[1].session_id,
+ tab_times[1].window_id,
+ tab_times[1].tab_idx)),
+ model.GetLabelAt(4));
+ EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[2].session_id,
+ tab_times[2].window_id,
+ tab_times[2].tab_idx)),
+ model.GetLabelAt(5));
+ EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[3].session_id,
+ tab_times[3].window_id,
+ tab_times[3].tab_idx)),
+ model.GetLabelAt(6));
+}

Powered by Google App Engine
This is Rietveld 408576698