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

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: addressed fred'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 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 bool can_restore_tab)
128 : RecentTabsSubMenuModel(provider, browser),
129 can_restore_tab_(can_restore_tab),
130 execute_count_(0),
131 enable_count_(0) {
132 }
133
134 TestRecentTabsSubMenuModel(ui::AcceleratorProvider* provider,
135 Browser* browser,
136 browser_sync::SessionModelAssociator* associator,
137 bool can_restore_tab)
138 : RecentTabsSubMenuModel(provider, browser, associator, true),
139 can_restore_tab_(can_restore_tab),
140 execute_count_(0),
141 enable_count_(0) {
142 }
143
144 // Testing overrides to ui::SimpleMenuModel::Delegate:
145 virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
146 bool val = command_id == IDC_RESTORE_TAB ? can_restore_tab_ :
147 RecentTabsSubMenuModel::IsCommandIdEnabled(command_id);
148 if (val)
149 ++enable_count_;
150 return val;
151 }
152
153 virtual void ExecuteCommand(int command_id) OVERRIDE {
154 ++execute_count_;
155 }
156
157 bool can_restore_tab_;
158 int execute_count_;
159 int mutable enable_count_; // Mutable because IsCommandIdEnabledAt is const.
160 };
161
162 } // namespace
163
164 class RecentTabsSubMenuModelTest : public BrowserWithTestWindowTest,
165 public ui::AcceleratorProvider {
166 public:
167 RecentTabsSubMenuModelTest()
168 : sync_service_(&testing_profile_),
169 associator_(&sync_service_, true) {
170 associator_.SetCurrentMachineTagForTesting("RecentTabsSubMenuModelTest");
171 }
172
173 // Don't handle accelerators.
174 virtual bool GetAcceleratorForCommandId(
175 int command_id,
176 ui::Accelerator* accelerator) OVERRIDE {
177 return false;
178 }
179
180 private:
181 TestingProfile testing_profile_;
182 testing::NiceMock<ProfileSyncServiceMock> sync_service_;
183
184 protected:
185 browser_sync::SessionModelAssociator associator_;
186 };
187
188 // Test disabled "Reopen closed tab" with no foreign tabs.
189 TEST_F(RecentTabsSubMenuModelTest, NoTabs) {
190 TestRecentTabsSubMenuModel model(this, browser(), false);
191
192 // Expected menu:
193 // Menu index Menu items
194 // --------------------------------------
195 // 0 Reopen closed tab
196 // 1 <separator>
197 // 2 No tabs from other Devices
198
199 int num_items = model.GetItemCount();
200 EXPECT_EQ(3, num_items);
201 EXPECT_FALSE(model.IsEnabledAt(0));
202 EXPECT_FALSE(model.IsEnabledAt(2));
203 EXPECT_EQ(0, model.enable_count_);
204 }
205
206 // Test enabled "Reopen closed tab" with no foreign tabs.
207 TEST_F(RecentTabsSubMenuModelTest, ReopenClosedTab) {
208 TestRecentTabsSubMenuModel model(this, browser(), true);
209 // Menu contents are identical to NoTabs test.
210 int num_items = model.GetItemCount();
211 EXPECT_EQ(3, num_items);
212 EXPECT_TRUE(model.IsEnabledAt(0));
213 model.ActivatedAt(0);
214 EXPECT_FALSE(model.IsEnabledAt(2));
215 EXPECT_EQ(1, model.enable_count_);
216 EXPECT_EQ(1, model.execute_count_);
217 }
218
219 // Test enabled "Reopen closed tab" with multiple sessions, multiple windows,
220 // and multiple enabled tabs from other devices.
221 TEST_F(RecentTabsSubMenuModelTest, OtherDevices) {
222 SessionID::id_type id = 0;
223 std::vector<SessionID::id_type> session_ids;
224 // Tabs are populated in decreasing timestamp.
225 base::Time now = base::Time::Now();
226
227 // Create 1st session : 1 window, 3 tabs
228 SessionID::id_type session_id = id++;
229 session_ids.push_back(session_id);
230 sync_pb::SessionSpecifics meta1;
231 BuildSessionSpecifics(session_id, &meta1);
232 SessionID::id_type window_id = id++;
233 std::vector<SessionID::id_type> tab_ids;
234 AddWindowSpecifics(window_id, 3, &id, &tab_ids, &meta1);
235 std::vector<sync_pb::SessionSpecifics> tabs;
236 BuildTabsSpecifics(session_id, window_id, tab_ids, &tabs);
237 base::Time timestamp = now - base::TimeDelta::FromMinutes(window_id * 10);
238 // First tab's timestamp is the most recent in session, so use it as session's
239 // modified time.
240 associator_.AssociateForeignSpecifics(meta1, timestamp);
241 std::vector<base::Time> tab_times;
242 for (size_t i = 0; i < tabs.size(); ++i)
243 tab_times.push_back(timestamp - base::TimeDelta::FromMinutes(i));
244 for (size_t i = 0; i < tabs.size(); ++i)
245 associator_.AssociateForeignSpecifics(tabs[i], tab_times[i]);
246
247 // Create 2nd session : 2 windows, 1 tab in 1st window, 2 tabs in 2nd window
248 session_id = id++;
249 session_ids.push_back(session_id);
250 sync_pb::SessionSpecifics meta2;
251 BuildSessionSpecifics(session_id, &meta2);
252 window_id = id++;
253 std::vector<SessionID::id_type> tab_ids1;
254 AddWindowSpecifics(window_id, 1, &id, &tab_ids1, &meta2);
255 std::vector<sync_pb::SessionSpecifics> tabs1;
256 BuildTabsSpecifics(session_id, window_id, tab_ids1, &tabs1);
257 timestamp = now - base::TimeDelta::FromMinutes(window_id * 10);
258 associator_.AssociateForeignSpecifics(meta2, timestamp);
259 std::vector<base::Time> tab_times1;
260 for (size_t i = 0; i < tabs1.size(); ++i)
261 tab_times1.push_back(timestamp - base::TimeDelta::FromMinutes(i));
262 for (size_t i = 0; i < tabs1.size(); ++i)
263 associator_.AssociateForeignSpecifics(tabs1[i], tab_times1[i]);
264 window_id = id++;
265 std::vector<SessionID::id_type> tab_ids2;
266 AddWindowSpecifics(window_id, 2, &id, &tab_ids2, &meta2);
267 std::vector<sync_pb::SessionSpecifics> tabs2;
268 BuildTabsSpecifics(session_id, window_id, tab_ids2, &tabs2);
269 timestamp = now - base::TimeDelta::FromMinutes(window_id * 10);
270 // First tab's timestamp is the most recent in session, so use it as session's
271 // modified time.
272 associator_.AssociateForeignSpecifics(meta2, timestamp);
273 std::vector<base::Time> tab_times2;
274 for (size_t i = 0; i < tabs2.size(); ++i)
275 tab_times2.push_back(timestamp - base::TimeDelta::FromMinutes(i));
276 for (size_t i = 0; i < tabs2.size(); ++i)
277 associator_.AssociateForeignSpecifics(tabs2[i], tab_times2[i]);
278
279 // Make sure data is populated correctly in SessionModelAssociator.
280 std::vector<const browser_sync::SyncedSession*> sessions;
281 ASSERT_TRUE(associator_.GetAllForeignSessions(&sessions));
282 ASSERT_EQ(2U, sessions.size());
283 std::vector<const SessionWindow*> windows;
284 ASSERT_TRUE(associator_.GetForeignSession(ToSessionTag(session_ids[0]),
285 &windows));
286 ASSERT_EQ(1U, windows.size());
287 ASSERT_EQ(3U, windows[0]->tabs.size());
288 windows.clear();
289 ASSERT_TRUE(associator_.GetForeignSession(ToSessionTag(session_ids[1]),
290 &windows));
291 ASSERT_EQ(2U, windows.size());
292 ASSERT_EQ(1U, windows[0]->tabs.size());
293 ASSERT_EQ(2U, windows[1]->tabs.size());
294
295 // Verify that data is populated correctly in RecentTabsSubMenuModel.
296 // Expected menu:
297 // - first inserted tab is most recent and hence is top
298 // Menu index Menu items
299 // --------------------------------------
300 // 0 Reopen closed tab
301 // 1 <separator>
302 // 2 <section header for 1st session>
303 // 3-5 <3 tabs of the only window of session 0>
304 // 6 <separator>
305 // 7 <section header for 2nd session>
306 // 8 <the only tab of window 0 of session 1>
307 // 9-10 <2 tabs of window 1 of session 2>
308
309 TestRecentTabsSubMenuModel model(this, browser(), &associator_, true);
310 int num_items = model.GetItemCount();
311 EXPECT_EQ(11, num_items);
312 model.ActivatedAt(0);
313 EXPECT_TRUE(model.IsEnabledAt(0));
314 model.ActivatedAt(3);
315 EXPECT_TRUE(model.IsEnabledAt(3));
316 model.ActivatedAt(4);
317 EXPECT_TRUE(model.IsEnabledAt(4));
318 model.ActivatedAt(5);
319 EXPECT_TRUE(model.IsEnabledAt(5));
320 model.ActivatedAt(8);
321 EXPECT_TRUE(model.IsEnabledAt(8));
322 model.ActivatedAt(9);
323 EXPECT_TRUE(model.IsEnabledAt(9));
324 model.ActivatedAt(10);
325 EXPECT_TRUE(model.IsEnabledAt(10));
326 EXPECT_EQ(7, model.enable_count_);
327 EXPECT_EQ(7, model.execute_count_);
328 }
329
330 TEST_F(RecentTabsSubMenuModelTest, MaxSessionsAndRecency) {
331 SessionID::id_type id = 0;
332 std::vector<SessionID::id_type> session_ids;
333 base::Time now = base::Time::Now();
334
335 // Create 4 sessions : each session has 1 window with 1 tab each.
336 std::vector<TabTime> tab_times;
337 for (int i = 0; i < 4; ++i) {
338 SessionID::id_type session_id = id++;
339 session_ids.push_back(session_id);
340 sync_pb::SessionSpecifics meta;
341 BuildSessionSpecifics(session_id, &meta);
342 SessionID::id_type window_id = id++;
343 std::vector<SessionID::id_type> tab_ids;
344 AddWindowSpecifics(window_id, 1, &id, &tab_ids, &meta);
345 std::vector<sync_pb::SessionSpecifics> tabs;
346 BuildTabsSpecifics(session_id, window_id, tab_ids, &tabs);
347 base::Time timestamp =
348 now + base::TimeDelta::FromMinutes(base::RandUint64());
349 tab_times.push_back(TabTime(session_id, window_id, tab_ids[0], timestamp));
350 // Use tab's timestamp as session's modified time.
351 associator_.AssociateForeignSpecifics(meta, timestamp);
352 associator_.AssociateForeignSpecifics(tabs[0], timestamp);
353 }
354
355 // Make sure data is populated correctly in SessionModelAssociator.
356 std::vector<const browser_sync::SyncedSession*> sessions;
357 ASSERT_TRUE(associator_.GetAllForeignSessions(&sessions));
358 ASSERT_EQ(4U, sessions.size());
359 std::vector<const SessionWindow*> windows;
360 for (size_t i = 0; i < sessions.size(); ++i) {
361 ASSERT_TRUE(associator_.GetForeignSession(ToSessionTag(session_ids[i]),
362 &windows));
363 ASSERT_EQ(1U, windows.size());
364 ASSERT_EQ(1U, windows[0]->tabs.size());
365 }
366
367 // Verify that data is populated correctly in RecentTabsSubMenuModel.
368 // Expected menu:
369 // - max sessions is 3, so only 3 most-recent sessions will show
370 // Menu index Menu items
371 // --------------------------------------
372 // 0 Reopen closed tab
373 // 1 <separator>
374 // 2 <section header for 1st session>
375 // 3 <the only tab of the only window of session 3>
376 // 4 <separator>
377 // 5 <section header for 2nd session>
378 // 6 <the only tab of the only window of session 2>
379 // 7 <separator>
380 // 8 <section header for 3rd session>
381 // 9 <the only tab of the only window of session 1>
382
383 TestRecentTabsSubMenuModel model(this, browser(), &associator_, true);
384 int num_items = model.GetItemCount();
385 EXPECT_EQ(10, num_items);
386
387 sort(tab_times.begin(), tab_times.end(), SortTabTimesByRecency);
388 EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[0].session_id,
389 tab_times[0].window_id,
390 tab_times[0].tab_id)),
391 model.GetLabelAt(3));
392 EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[1].session_id,
393 tab_times[1].window_id,
394 tab_times[1].tab_id)),
395 model.GetLabelAt(6));
396 EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[2].session_id,
397 tab_times[2].window_id,
398 tab_times[2].tab_id)),
399 model.GetLabelAt(9));
400 }
401
402 TEST_F(RecentTabsSubMenuModelTest, MaxTabsPerSessionAndRecency) {
403 SessionID::id_type id = 0;
404 base::Time now = base::Time::Now();
405
406 // Create a session: 2 windows with 5 tabs each.
407 std::vector<TabTime> tab_times;
408 SessionID::id_type session_id = id++;
409 sync_pb::SessionSpecifics meta;
410 BuildSessionSpecifics(session_id, &meta);
411 for (int i = 0; i < 2; ++i) {
412 SessionID::id_type window_id = id++;
413 std::vector<SessionID::id_type> tab_ids;
414 AddWindowSpecifics(window_id, 5, &id, &tab_ids, &meta);
415 std::vector<sync_pb::SessionSpecifics> tabs;
416 BuildTabsSpecifics(session_id, window_id, tab_ids, &tabs);
417 // There's no session recency sorting with only one session, so just use now
418 // as session's modified time.
419 associator_.AssociateForeignSpecifics(meta, now);
420 for (size_t i = 0; i < tabs.size(); ++i) {
421 base::Time timestamp = now +
422 base::TimeDelta::FromMinutes(base::RandUint64());
423 tab_times.push_back(TabTime(session_id, window_id, tab_ids[i],
424 timestamp));
425 associator_.AssociateForeignSpecifics(tabs[i], timestamp);
426 }
427 }
428
429 // Make sure data is populated correctly in SessionModelAssociator.
430 std::vector<const browser_sync::SyncedSession*> sessions;
431 ASSERT_TRUE(associator_.GetAllForeignSessions(&sessions));
432 ASSERT_EQ(1U, sessions.size());
433 std::vector<const SessionWindow*> windows;
434 ASSERT_TRUE(associator_.GetForeignSession(ToSessionTag(session_id),
435 &windows));
436 ASSERT_EQ(2U, windows.size());
437 ASSERT_EQ(5U, windows[0]->tabs.size());
438 ASSERT_EQ(5U, windows[1]->tabs.size());
439
440 // Verify that data is populated correctly in RecentTabsSubMenuModel.
441 // Expected menu:
442 // - max tabs per session is 4, so only 4 most-recent tabs will show,
443 // independent of which window they came from
444 // Menu index Menu items
445 // --------------------------------------
446 // 0 Reopen closed tab
447 // 1 <separator>
448 // 2 <section header for session>
449 // 3-6 <4 most-recent tabs of session>
450
451 TestRecentTabsSubMenuModel model(this, browser(), &associator_, true);
452 int num_items = model.GetItemCount();
453 EXPECT_EQ(7, num_items);
454
455 sort(tab_times.begin(), tab_times.end(), SortTabTimesByRecency);
456 EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[0].session_id,
457 tab_times[0].window_id,
458 tab_times[0].tab_id)),
459 model.GetLabelAt(3));
460 EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[1].session_id,
461 tab_times[1].window_id,
462 tab_times[1].tab_id)),
463 model.GetLabelAt(4));
464 EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[2].session_id,
465 tab_times[2].window_id,
466 tab_times[2].tab_id)),
467 model.GetLabelAt(5));
468 EXPECT_EQ(UTF8ToUTF16(ToTabTitle(tab_times[3].session_id,
469 tab_times[3].window_id,
470 tab_times[3].tab_id)),
471 model.GetLabelAt(6));
472 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698