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

Side by Side 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: rebased to resolve conflicts 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h"
6
7 #include "base/rand_util.h"
8 #include "base/string_number_conversions.h"
9 #include "base/stringprintf.h"
10 #include "chrome/app/chrome_command_ids.h"
11 #include "chrome/browser/sessions/session_types.h"
12 #include "chrome/browser/sessions/session_types_test_helper.h"
13 #include "chrome/browser/sync/glue/session_model_associator.h"
14 #include "chrome/browser/sync/glue/synced_session.h"
15 #include "chrome/browser/sync/profile_sync_service_mock.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/browser_tabstrip.h"
18 #include "chrome/test/base/browser_with_test_window_test.h"
19 #include "chrome/test/base/menu_model_test.h"
20 #include "chrome/test/base/testing_profile.h"
21 #include "grit/generated_resources.h"
22 #include "sync/protocol/session_specifics.pb.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 namespace {
27
28 const char kBaseSessionTag[] = "session_tag";
29 const char kBaseSessionName[] = "session_name";
30 const char kBaseTabUrl[] = "http://foo/?";
31 const char kTabTitleFormat[] = "session=%d;window=%d;tab=%d";
32
33 struct TabTime {
34 TabTime(SessionID::id_type session_id,
35 SessionID::id_type window_id,
36 SessionID::id_type tab_id,
37 const base::Time& timestamp)
38 : session_id(session_id),
39 window_id(window_id),
40 tab_id(tab_id),
41 timestamp(timestamp) {}
42
43 SessionID::id_type session_id;
44 SessionID::id_type window_id;
45 SessionID::id_type tab_id;
46 base::Time timestamp;
47 };
48
49 bool SortTabTimesByRecency(const TabTime& t1, const TabTime& t2) {
50 return t1.timestamp > t2.timestamp;
51 }
52
53 std::string ToSessionTag(SessionID::id_type session_id) {
54 return std::string(kBaseSessionTag + base::IntToString(session_id));
55 }
56
57 std::string ToSessionName(SessionID::id_type session_id) {
58 return std::string(kBaseSessionName + base::IntToString(session_id));
59 }
60
61 std::string ToTabTitle(SessionID::id_type session_id,
62 SessionID::id_type window_id,
63 SessionID::id_type tab_id) {
64 return base::StringPrintf(kTabTitleFormat, session_id, window_id, tab_id);
65 }
66
67 std::string ToTabUrl(SessionID::id_type session_id,
68 SessionID::id_type window_id,
69 SessionID::id_type tab_id) {
70 return std::string(kBaseTabUrl + ToTabTitle(session_id, window_id, tab_id));
71 }
72
73 void BuildSessionSpecifics(SessionID::id_type session_id,
74 sync_pb::SessionSpecifics* meta) {
75 meta->set_session_tag(ToSessionTag(session_id));
76 sync_pb::SessionHeader* header = meta->mutable_header();
77 header->set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_CROS);
78 header->set_client_name(ToSessionName(session_id));
79 }
80
81 void AddWindowSpecifics(int window_id, int num_tabs, int* tab_id,
82 std::vector<SessionID::id_type>* tab_ids,
83 sync_pb::SessionSpecifics* meta) {
84 sync_pb::SessionHeader* header = meta->mutable_header();
85 sync_pb::SessionWindow* window = header->add_window();
86 window->set_window_id(window_id);
87 window->set_selected_tab_index(0);
88 window->set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED);
89 for (int i = 0; i < num_tabs; ++i) {
90 SessionID::id_type id = (*tab_id)++;
91 window->add_tab(id);
92 tab_ids->push_back(id);
93 }
94 }
95
96 void BuildTabsSpecifics(SessionID::id_type session_id,
97 SessionID::id_type window_id,
98 const std::vector<SessionID::id_type>& tab_ids,
99 std::vector<sync_pb::SessionSpecifics>* tab_bases) {
100 tab_bases->resize(tab_ids.size());
101 for (size_t i = 0; i < tab_ids.size(); ++i) {
102 SessionID::id_type tab_id = tab_ids[i];
103 sync_pb::SessionSpecifics& tab_base = (*tab_bases)[i];
104 tab_base.set_session_tag(ToSessionTag(session_id));
105 sync_pb::SessionTab* tab = tab_base.mutable_tab();
106 tab->set_window_id(window_id);
107 tab->set_tab_id(tab_id);
108 tab->set_tab_visual_index(1);
109 tab->set_current_navigation_index(0);
110 tab->set_pinned(true);
111 tab->set_extension_app_id("app_id");
112 sync_pb::TabNavigation* navigation = tab->add_navigation();
113 navigation->set_virtual_url(ToTabUrl(session_id, window_id, tab_id));
114 navigation->set_referrer("referrer");
115 navigation->set_title(ToTabTitle(session_id, window_id, tab_id));
116 navigation->set_page_transition(sync_pb::SyncEnums_PageTransition_TYPED);
117 }
118 }
119
120 // This copies parts of MenuModelTest::Delegate and combines them with the
121 // RecentTabsSubMenuModel since RecentTabsSubMenuModel is a
122 // SimpleMenuModel::Delegate and not just derived from SimpleMenuModel.
123 class TestRecentTabsSubMenuModel : public RecentTabsSubMenuModel {
124 public:
125 TestRecentTabsSubMenuModel(ui::AcceleratorProvider* provider,
126 Browser* browser,
127 browser_sync::SessionModelAssociator* associator,
128 bool can_restore_tab)
129 : RecentTabsSubMenuModel(provider, browser, associator),
130 can_restore_tab_(can_restore_tab),
131 execute_count_(0),
132 enable_count_(0) {
133 }
134
135 // Testing overrides to ui::SimpleMenuModel::Delegate:
136 virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
137 bool val = command_id == IDC_RESTORE_TAB ? can_restore_tab_ :
138 RecentTabsSubMenuModel::IsCommandIdEnabled(command_id);
139 if (val)
140 ++enable_count_;
141 return val;
142 }
143
144 virtual void ExecuteCommand(int command_id) OVERRIDE {
145 ++execute_count_;
146 }
147
148 bool can_restore_tab_;
149 int execute_count_;
150 int mutable enable_count_; // Mutable because IsCommandIdEnabledAt is const.
151 };
152
153 } // namespace
154
155 class RecentTabsSubMenuModelTest : public BrowserWithTestWindowTest,
156 public ui::AcceleratorProvider {
157 public:
158 RecentTabsSubMenuModelTest()
159 : sync_service_(&testing_profile_),
160 associator_(&sync_service_, true) {
161 associator_.SetCurrentMachineTagForTesting("RecentTabsSubMenuModelTest");
162 }
163
164 // Don't handle accelerators.
165 virtual bool GetAcceleratorForCommandId(
166 int command_id,
167 ui::Accelerator* accelerator) OVERRIDE {
168 return false;
169 }
170
171 private:
172 TestingProfile testing_profile_;
173 testing::NiceMock<ProfileSyncServiceMock> sync_service_;
174
175 protected:
176 browser_sync::SessionModelAssociator associator_;
177 };
178
179 // Test disabled "Reopen closed tab" with no foreign tabs.
180 TEST_F(RecentTabsSubMenuModelTest, NoTabs) {
181 TestRecentTabsSubMenuModel model(this, browser(), NULL, false);
182
183 // Expected menu:
184 // Menu index Menu items
185 // --------------------------------------
186 // 0 Reopen closed tab
187 // 1 <separator>
188 // 2 No tabs from other Devices
189
190 int num_items = model.GetItemCount();
191 EXPECT_EQ(3, num_items);
192 EXPECT_FALSE(model.IsEnabledAt(0));
193 EXPECT_FALSE(model.IsEnabledAt(2));
194 EXPECT_EQ(0, model.enable_count_);
195 }
196
197 // Test enabled "Reopen closed tab" with no foreign tabs.
198 TEST_F(RecentTabsSubMenuModelTest, ReopenClosedTab) {
199 TestRecentTabsSubMenuModel model(this, browser(), NULL, true);
200 // Menu contents are identical to NoTabs test.
201 int num_items = model.GetItemCount();
202 EXPECT_EQ(3, num_items);
203 EXPECT_TRUE(model.IsEnabledAt(0));
204 model.ActivatedAt(0);
205 EXPECT_FALSE(model.IsEnabledAt(2));
206 EXPECT_EQ(1, model.enable_count_);
207 EXPECT_EQ(1, model.execute_count_);
208 }
209
210 // Test enabled "Reopen closed tab" with multiple sessions, multiple windows,
211 // and multiple enabled tabs from other devices.
212 TEST_F(RecentTabsSubMenuModelTest, OtherDevices) {
213 SessionID::id_type id = 0;
214 std::vector<SessionID::id_type> session_ids;
215 // Tabs are populated in decreasing timestamp.
216 base::Time now = base::Time::Now();
217
218 // Create 1st session : 1 window, 3 tabs
219 SessionID::id_type session_id = id++;
220 session_ids.push_back(session_id);
221 sync_pb::SessionSpecifics meta1;
222 BuildSessionSpecifics(session_id, &meta1);
223 SessionID::id_type window_id = id++;
224 std::vector<SessionID::id_type> tab_ids;
225 AddWindowSpecifics(window_id, 3, &id, &tab_ids, &meta1);
226 std::vector<sync_pb::SessionSpecifics> tabs;
227 BuildTabsSpecifics(session_id, window_id, tab_ids, &tabs);
228 base::Time timestamp = now - base::TimeDelta::FromMinutes(window_id * 10);
229 // First tab's timestamp is the most recent in session, so use it as session's
230 // modified time.
231 associator_.AssociateForeignSpecifics(meta1, timestamp);
232 std::vector<base::Time> tab_times;
233 for (size_t i = 0; i < tabs.size(); ++i)
234 tab_times.push_back(timestamp - base::TimeDelta::FromMinutes(i));
235 for (size_t i = 0; i < tabs.size(); ++i)
236 associator_.AssociateForeignSpecifics(tabs[i], tab_times[i]);
237
238 // Create 2nd session : 2 windows, 1 tab in 1st window, 2 tabs in 2nd window
239 session_id = id++;
240 session_ids.push_back(session_id);
241 sync_pb::SessionSpecifics meta2;
242 BuildSessionSpecifics(session_id, &meta2);
243 window_id = id++;
244 std::vector<SessionID::id_type> tab_ids1;
245 AddWindowSpecifics(window_id, 1, &id, &tab_ids1, &meta2);
246 std::vector<sync_pb::SessionSpecifics> tabs1;
247 BuildTabsSpecifics(session_id, window_id, tab_ids1, &tabs1);
248 timestamp = now - base::TimeDelta::FromMinutes(window_id * 10);
249 associator_.AssociateForeignSpecifics(meta2, timestamp);
250 std::vector<base::Time> tab_times1;
251 for (size_t i = 0; i < tabs1.size(); ++i)
252 tab_times1.push_back(timestamp - base::TimeDelta::FromMinutes(i));
253 for (size_t i = 0; i < tabs1.size(); ++i)
254 associator_.AssociateForeignSpecifics(tabs1[i], tab_times1[i]);
255 window_id = id++;
256 std::vector<SessionID::id_type> tab_ids2;
257 AddWindowSpecifics(window_id, 2, &id, &tab_ids2, &meta2);
258 std::vector<sync_pb::SessionSpecifics> tabs2;
259 BuildTabsSpecifics(session_id, window_id, tab_ids2, &tabs2);
260 timestamp = now - base::TimeDelta::FromMinutes(window_id * 10);
261 // First tab's timestamp is the most recent in session, so use it as session's
262 // modified time.
263 associator_.AssociateForeignSpecifics(meta2, timestamp);
264 std::vector<base::Time> tab_times2;
265 for (size_t i = 0; i < tabs2.size(); ++i)
266 tab_times2.push_back(timestamp - base::TimeDelta::FromMinutes(i));
267 for (size_t i = 0; i < tabs2.size(); ++i)
268 associator_.AssociateForeignSpecifics(tabs2[i], tab_times2[i]);
269
270 // Make sure data is populated correctly in SessionModelAssociator.
271 std::vector<const browser_sync::SyncedSession*> sessions;
272 ASSERT_TRUE(associator_.GetAllForeignSessions(&sessions));
273 ASSERT_EQ(2U, sessions.size());
274 std::vector<const SessionWindow*> windows;
275 ASSERT_TRUE(associator_.GetForeignSession(ToSessionTag(session_ids[0]),
276 &windows));
277 ASSERT_EQ(1U, windows.size());
278 ASSERT_EQ(3U, windows[0]->tabs.size());
279 windows.clear();
280 ASSERT_TRUE(associator_.GetForeignSession(ToSessionTag(session_ids[1]),
281 &windows));
282 ASSERT_EQ(2U, windows.size());
283 ASSERT_EQ(1U, windows[0]->tabs.size());
284 ASSERT_EQ(2U, windows[1]->tabs.size());
285
286 // Verify that data is populated correctly in RecentTabsSubMenuModel.
287 // Expected menu:
288 // - first inserted tab is most recent and hence is top
289 // Menu index Menu items
290 // --------------------------------------
291 // 0 Reopen closed tab
292 // 1 <separator>
293 // 2 <section header for 1st session>
294 // 3-5 <3 tabs of the only window of session 0>
295 // 6 <separator>
296 // 7 <section header for 2nd session>
297 // 8 <the only tab of window 0 of session 1>
298 // 9-10 <2 tabs of window 1 of session 2>
299
300 TestRecentTabsSubMenuModel model(this, browser(), &associator_, true);
301 int num_items = model.GetItemCount();
302 EXPECT_EQ(11, num_items);
303 model.ActivatedAt(0);
304 EXPECT_TRUE(model.IsEnabledAt(0));
305 model.ActivatedAt(3);
306 EXPECT_TRUE(model.IsEnabledAt(3));
307 model.ActivatedAt(4);
308 EXPECT_TRUE(model.IsEnabledAt(4));
309 model.ActivatedAt(5);
310 EXPECT_TRUE(model.IsEnabledAt(5));
311 model.ActivatedAt(8);
312 EXPECT_TRUE(model.IsEnabledAt(8));
313 model.ActivatedAt(9);
314 EXPECT_TRUE(model.IsEnabledAt(9));
315 model.ActivatedAt(10);
316 EXPECT_TRUE(model.IsEnabledAt(10));
317 EXPECT_EQ(7, model.enable_count_);
318 EXPECT_EQ(7, model.execute_count_);
319 }
320
321 TEST_F(RecentTabsSubMenuModelTest, MaxSessionsAndRecency) {
322 SessionID::id_type id = 0;
323 std::vector<SessionID::id_type> session_ids;
324 base::Time now = base::Time::Now();
325
326 // Create 4 sessions : each session has 1 window with 1 tab each.
327 std::vector<TabTime> tab_times;
328 for (int i = 0; i < 4; ++i) {
329 SessionID::id_type session_id = id++;
330 session_ids.push_back(session_id);
331 sync_pb::SessionSpecifics meta;
332 BuildSessionSpecifics(session_id, &meta);
333 SessionID::id_type window_id = id++;
334 std::vector<SessionID::id_type> tab_ids;
335 AddWindowSpecifics(window_id, 1, &id, &tab_ids, &meta);
336 std::vector<sync_pb::SessionSpecifics> tabs;
337 BuildTabsSpecifics(session_id, window_id, tab_ids, &tabs);
338 base::Time timestamp =
339 now + base::TimeDelta::FromMinutes(base::RandUint64());
340 tab_times.push_back(TabTime(session_id, window_id, tab_ids[0], timestamp));
341 // Use tab's timestamp as session's modified time.
342 associator_.AssociateForeignSpecifics(meta, timestamp);
343 associator_.AssociateForeignSpecifics(tabs[0], timestamp);
344 }
345
346 // Make sure data is populated correctly in SessionModelAssociator.
347 std::vector<const browser_sync::SyncedSession*> sessions;
348 ASSERT_TRUE(associator_.GetAllForeignSessions(&sessions));
349 ASSERT_EQ(4U, sessions.size());
350 std::vector<const SessionWindow*> windows;
351 for (size_t i = 0; i < sessions.size(); ++i) {
352 ASSERT_TRUE(associator_.GetForeignSession(ToSessionTag(session_ids[i]),
353 &windows));
354 ASSERT_EQ(1U, windows.size());
355 ASSERT_EQ(1U, windows[0]->tabs.size());
356 }
357
358 // Verify that data is populated correctly in RecentTabsSubMenuModel.
359 // Expected menu:
360 // - max sessions is 3, so only 3 most-recent sessions will show
361 // Menu index Menu items
362 // --------------------------------------
363 // 0 Reopen closed tab
364 // 1 <separator>
365 // 2 <section header for 1st session>
366 // 3 <the only tab of the only window of session 3>
367 // 4 <separator>
368 // 5 <section header for 2nd session>
369 // 6 <the only tab of the only window of session 2>
370 // 7 <separator>
371 // 8 <section header for 3rd session>
372 // 9 <the only tab of the only window of session 1>
373
374 TestRecentTabsSubMenuModel model(this, browser(), &associator_, true);
375 int num_items = model.GetItemCount();
376 EXPECT_EQ(10, num_items);
377
378 sort(tab_times.begin(), tab_times.end(), SortTabTimesByRecency);
379 EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[0].session_id,
380 tab_times[0].window_id,
381 tab_times[0].tab_id)),
382 model.GetLabelAt(3));
383 EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[1].session_id,
384 tab_times[1].window_id,
385 tab_times[1].tab_id)),
386 model.GetLabelAt(6));
387 EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[2].session_id,
388 tab_times[2].window_id,
389 tab_times[2].tab_id)),
390 model.GetLabelAt(9));
391 }
392
393 TEST_F(RecentTabsSubMenuModelTest, MaxTabsPerSessionAndRecency) {
394 SessionID::id_type id = 0;
395 base::Time now = base::Time::Now();
396
397 // Create a session: 2 windows with 5 tabs each.
398 std::vector<TabTime> tab_times;
399 SessionID::id_type session_id = id++;
400 sync_pb::SessionSpecifics meta;
401 BuildSessionSpecifics(session_id, &meta);
402 for (int i = 0; i < 2; ++i) {
403 SessionID::id_type window_id = id++;
404 std::vector<SessionID::id_type> tab_ids;
405 AddWindowSpecifics(window_id, 5, &id, &tab_ids, &meta);
406 std::vector<sync_pb::SessionSpecifics> tabs;
407 BuildTabsSpecifics(session_id, window_id, tab_ids, &tabs);
408 // There's no session recency sorting with only one session, so just use now
409 // as session's modified time.
410 associator_.AssociateForeignSpecifics(meta, now);
411 for (size_t i = 0; i < tabs.size(); ++i) {
412 base::Time timestamp = now +
413 base::TimeDelta::FromMinutes(base::RandUint64());
414 tab_times.push_back(TabTime(session_id, window_id, tab_ids[i],
415 timestamp));
416 associator_.AssociateForeignSpecifics(tabs[i], timestamp);
417 }
418 }
419
420 // Make sure data is populated correctly in SessionModelAssociator.
421 std::vector<const browser_sync::SyncedSession*> sessions;
422 ASSERT_TRUE(associator_.GetAllForeignSessions(&sessions));
423 ASSERT_EQ(1U, sessions.size());
424 std::vector<const SessionWindow*> windows;
425 ASSERT_TRUE(associator_.GetForeignSession(ToSessionTag(session_id),
426 &windows));
427 ASSERT_EQ(2U, windows.size());
428 ASSERT_EQ(5U, windows[0]->tabs.size());
429 ASSERT_EQ(5U, windows[1]->tabs.size());
430
431 // Verify that data is populated correctly in RecentTabsSubMenuModel.
432 // Expected menu:
433 // - max tabs per session is 4, so only 4 most-recent tabs will show,
434 // independent of which window they came from
435 // Menu index Menu items
436 // --------------------------------------
437 // 0 Reopen closed tab
438 // 1 <separator>
439 // 2 <section header for session>
440 // 3-6 <4 most-recent tabs of session>
441
442 TestRecentTabsSubMenuModel model(this, browser(), &associator_, true);
443 int num_items = model.GetItemCount();
444 EXPECT_EQ(7, num_items);
445
446 sort(tab_times.begin(), tab_times.end(), SortTabTimesByRecency);
447 EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[0].session_id,
448 tab_times[0].window_id,
449 tab_times[0].tab_id)),
450 model.GetLabelAt(3));
451 EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[1].session_id,
452 tab_times[1].window_id,
453 tab_times[1].tab_id)),
454 model.GetLabelAt(4));
455 EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[2].session_id,
456 tab_times[2].window_id,
457 tab_times[2].tab_id)),
458 model.GetLabelAt(5));
459 EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[3].session_id,
460 tab_times[3].window_id,
461 tab_times[3].tab_id)),
462 model.GetLabelAt(6));
463 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc ('k') | chrome/browser/ui/toolbar/wrench_menu_model.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698