Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / history_menu_bridge_unittest.mm
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 #import <Cocoa/Cocoa.h>
6 #include <vector>
7
8 #include "base/memory/ref_counted_memory.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/sys_string_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/app/chrome_command_ids.h"
14 #include "chrome/browser/sessions/persistent_tab_restore_service.h"
15 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
16 #include "chrome/browser/ui/cocoa/history_menu_bridge.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "components/favicon_base/favicon_types.h"
19 #include "components/sessions/serialized_navigation_entry_test_helper.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 #import "testing/gtest_mac.h"
23 #include "third_party/skia/include/core/SkBitmap.h"
24 #include "ui/gfx/codec/png_codec.h"
25
26 namespace {
27
28 class MockTRS : public PersistentTabRestoreService {
29  public:
30   MockTRS(Profile* profile) : PersistentTabRestoreService(profile, NULL) {}
31   MOCK_CONST_METHOD0(entries, const TabRestoreService::Entries&());
32 };
33
34 class MockBridge : public HistoryMenuBridge {
35  public:
36   MockBridge(Profile* profile)
37       : HistoryMenuBridge(profile),
38         menu_([[NSMenu alloc] initWithTitle:@"History"]) {}
39
40   NSMenu* HistoryMenu() override { return menu_.get(); }
41
42  private:
43   base::scoped_nsobject<NSMenu> menu_;
44 };
45
46 class HistoryMenuBridgeTest : public CocoaProfileTest {
47  public:
48
49   virtual void SetUp() {
50     CocoaProfileTest::SetUp();
51     profile()->CreateFaviconService();
52     bridge_.reset(new MockBridge(profile()));
53   }
54
55   // We are a friend of HistoryMenuBridge (and have access to
56   // protected methods), but none of the classes generated by TEST_F()
57   // are. Wraps common commands.
58   void ClearMenuSection(NSMenu* menu,
59                         NSInteger tag) {
60     bridge_->ClearMenuSection(menu, tag);
61   }
62
63   void AddItemToBridgeMenu(HistoryMenuBridge::HistoryItem* item,
64                            NSMenu* menu,
65                            NSInteger tag,
66                            NSInteger index) {
67     bridge_->AddItemToMenu(item, menu, tag, index);
68   }
69
70   NSMenuItem* AddItemToMenu(NSMenu* menu,
71                             NSString* title,
72                             SEL selector,
73                             int tag) {
74     NSMenuItem* item = [[[NSMenuItem alloc] initWithTitle:title action:NULL
75                                             keyEquivalent:@""] autorelease];
76     [item setTag:tag];
77     if (selector) {
78       [item setAction:selector];
79       [item setTarget:bridge_->controller_.get()];
80     }
81     [menu addItem:item];
82     return item;
83   }
84
85   HistoryMenuBridge::HistoryItem* CreateItem(const base::string16& title) {
86     HistoryMenuBridge::HistoryItem* item =
87         new HistoryMenuBridge::HistoryItem();
88     item->title = title;
89     item->url = GURL(title);
90     return item;
91   }
92
93   MockTRS::Tab CreateSessionTab(const std::string& url,
94                                 const std::string& title) {
95     MockTRS::Tab tab;
96     tab.current_navigation_index = 0;
97     tab.navigations.push_back(
98         sessions::SerializedNavigationEntryTestHelper::CreateNavigation(
99             url, title));
100     return tab;
101   }
102
103   void GetFaviconForHistoryItem(HistoryMenuBridge::HistoryItem* item) {
104     bridge_->GetFaviconForHistoryItem(item);
105   }
106
107   void GotFaviconData(HistoryMenuBridge::HistoryItem* item,
108                       const favicon_base::FaviconImageResult& image_result) {
109     bridge_->GotFaviconData(item, image_result);
110   }
111
112   scoped_ptr<MockBridge> bridge_;
113 };
114
115 // Edge case test for clearing until the end of a menu.
116 TEST_F(HistoryMenuBridgeTest, ClearHistoryMenuUntilEnd) {
117   NSMenu* menu = [[[NSMenu alloc] initWithTitle:@"history foo"] autorelease];
118   AddItemToMenu(menu, @"HEADER", NULL, HistoryMenuBridge::kVisitedTitle);
119
120   NSInteger tag = HistoryMenuBridge::kVisited;
121   AddItemToMenu(menu, @"alpha", @selector(openHistoryMenuItem:), tag);
122   AddItemToMenu(menu, @"bravo", @selector(openHistoryMenuItem:), tag);
123   AddItemToMenu(menu, @"charlie", @selector(openHistoryMenuItem:), tag);
124   AddItemToMenu(menu, @"delta", @selector(openHistoryMenuItem:), tag);
125
126   ClearMenuSection(menu, HistoryMenuBridge::kVisited);
127
128   EXPECT_EQ(1, [menu numberOfItems]);
129   EXPECT_NSEQ(@"HEADER",
130       [[menu itemWithTag:HistoryMenuBridge::kVisitedTitle] title]);
131 }
132
133 // Skip menu items that are not hooked up to |-openHistoryMenuItem:|.
134 TEST_F(HistoryMenuBridgeTest, ClearHistoryMenuSkipping) {
135   NSMenu* menu = [[[NSMenu alloc] initWithTitle:@"history foo"] autorelease];
136   AddItemToMenu(menu, @"HEADER", NULL, HistoryMenuBridge::kVisitedTitle);
137
138   NSInteger tag = HistoryMenuBridge::kVisited;
139   AddItemToMenu(menu, @"alpha", @selector(openHistoryMenuItem:), tag);
140   AddItemToMenu(menu, @"bravo", @selector(openHistoryMenuItem:), tag);
141   AddItemToMenu(menu, @"TITLE", NULL, HistoryMenuBridge::kRecentlyClosedTitle);
142   AddItemToMenu(menu, @"charlie", @selector(openHistoryMenuItem:), tag);
143
144   ClearMenuSection(menu, tag);
145
146   EXPECT_EQ(2, [menu numberOfItems]);
147   EXPECT_NSEQ(@"HEADER",
148       [[menu itemWithTag:HistoryMenuBridge::kVisitedTitle] title]);
149   EXPECT_NSEQ(@"TITLE",
150       [[menu itemAtIndex:1] title]);
151 }
152
153 // Edge case test for clearing an empty menu.
154 TEST_F(HistoryMenuBridgeTest, ClearHistoryMenuEmpty) {
155   NSMenu* menu = [[[NSMenu alloc] initWithTitle:@"history foo"] autorelease];
156   AddItemToMenu(menu, @"HEADER", NULL, HistoryMenuBridge::kVisited);
157
158   ClearMenuSection(menu, HistoryMenuBridge::kVisited);
159
160   EXPECT_EQ(1, [menu numberOfItems]);
161   EXPECT_NSEQ(@"HEADER",
162       [[menu itemWithTag:HistoryMenuBridge::kVisited] title]);
163 }
164
165 // Test that AddItemToMenu() properly adds HistoryItem objects as menus.
166 TEST_F(HistoryMenuBridgeTest, AddItemToMenu) {
167   NSMenu* menu = [[[NSMenu alloc] initWithTitle:@"history foo"] autorelease];
168
169   const base::string16 short_url = base::ASCIIToUTF16("http://foo/");
170   const base::string16 long_url = base::ASCIIToUTF16(
171       "http://super-duper-long-url--."
172       "that.cannot.possibly.fit.even-in-80-columns"
173       "or.be.reasonably-displayed-in-a-menu"
174       "without.looking-ridiculous.com/"); // 140 chars total
175
176   // HistoryItems are owned by the HistoryMenuBridge when AddItemToBridgeMenu()
177   // is called, which places them into the |menu_item_map_|, which owns them.
178   HistoryMenuBridge::HistoryItem* item1 = CreateItem(short_url);
179   AddItemToBridgeMenu(item1, menu, 100, 0);
180
181   HistoryMenuBridge::HistoryItem* item2 = CreateItem(long_url);
182   AddItemToBridgeMenu(item2, menu, 101, 1);
183
184   EXPECT_EQ(2, [menu numberOfItems]);
185
186   EXPECT_EQ(@selector(openHistoryMenuItem:), [[menu itemAtIndex:0] action]);
187   EXPECT_EQ(@selector(openHistoryMenuItem:), [[menu itemAtIndex:1] action]);
188
189   EXPECT_EQ(100, [[menu itemAtIndex:0] tag]);
190   EXPECT_EQ(101, [[menu itemAtIndex:1] tag]);
191
192   // Make sure a short title looks fine
193   NSString* s = [[menu itemAtIndex:0] title];
194   EXPECT_EQ(base::SysNSStringToUTF16(s), short_url);
195
196   // Make sure a super-long title gets trimmed
197   s = [[menu itemAtIndex:0] title];
198   EXPECT_TRUE([s length] < long_url.length());
199
200   // Confirm tooltips and confirm they are not trimmed (like the item
201   // name might be).  Add tolerance for URL fixer-upping;
202   // e.g. http://foo becomes http://foo/)
203   EXPECT_GE([[[menu itemAtIndex:0] toolTip] length], (2*short_url.length()-5));
204   EXPECT_GE([[[menu itemAtIndex:1] toolTip] length], (2*long_url.length()-5));
205 }
206
207 // Test that the menu is created for a set of simple tabs.
208 TEST_F(HistoryMenuBridgeTest, RecentlyClosedTabs) {
209   scoped_ptr<MockTRS> trs(new MockTRS(profile()));
210   MockTRS::Entries entries;
211
212   MockTRS::Tab tab1 = CreateSessionTab("http://google.com", "Google");
213   tab1.id = 24;
214   entries.push_back(&tab1);
215
216   MockTRS::Tab tab2 = CreateSessionTab("http://apple.com", "Apple");
217   tab2.id = 42;
218   entries.push_back(&tab2);
219
220   using ::testing::ReturnRef;
221   EXPECT_CALL(*trs.get(), entries()).WillOnce(ReturnRef(entries));
222
223   bridge_->TabRestoreServiceChanged(trs.get());
224
225   NSMenu* menu = bridge_->HistoryMenu();
226   ASSERT_EQ(2U, [[menu itemArray] count]);
227
228   NSMenuItem* item1 = [menu itemAtIndex:0];
229   MockBridge::HistoryItem* hist1 = bridge_->HistoryItemForMenuItem(item1);
230   EXPECT_TRUE(hist1);
231   EXPECT_EQ(24, hist1->session_id);
232   EXPECT_NSEQ(@"Google", [item1 title]);
233
234   NSMenuItem* item2 = [menu itemAtIndex:1];
235   MockBridge::HistoryItem* hist2 = bridge_->HistoryItemForMenuItem(item2);
236   EXPECT_TRUE(hist2);
237   EXPECT_EQ(42, hist2->session_id);
238   EXPECT_NSEQ(@"Apple", [item2 title]);
239 }
240
241 // Test that the menu is created for a mix of windows and tabs.
242 TEST_F(HistoryMenuBridgeTest, RecentlyClosedTabsAndWindows) {
243   scoped_ptr<MockTRS> trs(new MockTRS(profile()));
244   MockTRS::Entries entries;
245
246   MockTRS::Tab tab1 = CreateSessionTab("http://google.com", "Google");
247   tab1.id = 24;
248   entries.push_back(&tab1);
249
250   MockTRS::Window win1;
251   win1.id = 30;
252   win1.tabs.push_back(CreateSessionTab("http://foo.com", "foo"));
253   win1.tabs[0].id = 31;
254   win1.tabs.push_back(CreateSessionTab("http://bar.com", "bar"));
255   win1.tabs[1].id = 32;
256   entries.push_back(&win1);
257
258   MockTRS::Tab tab2 = CreateSessionTab("http://apple.com", "Apple");
259   tab2.id = 42;
260   entries.push_back(&tab2);
261
262   MockTRS::Window win2;
263   win2.id = 50;
264   win2.tabs.push_back(CreateSessionTab("http://magic.com", "magic"));
265   win2.tabs[0].id = 51;
266   win2.tabs.push_back(CreateSessionTab("http://goats.com", "goats"));
267   win2.tabs[1].id = 52;
268   win2.tabs.push_back(CreateSessionTab("http://teleporter.com", "teleporter"));
269   win2.tabs[1].id = 53;
270   entries.push_back(&win2);
271
272   using ::testing::ReturnRef;
273   EXPECT_CALL(*trs.get(), entries()).WillOnce(ReturnRef(entries));
274
275   bridge_->TabRestoreServiceChanged(trs.get());
276
277   NSMenu* menu = bridge_->HistoryMenu();
278   ASSERT_EQ(4U, [[menu itemArray] count]);
279
280   NSMenuItem* item1 = [menu itemAtIndex:0];
281   MockBridge::HistoryItem* hist1 = bridge_->HistoryItemForMenuItem(item1);
282   EXPECT_TRUE(hist1);
283   EXPECT_EQ(24, hist1->session_id);
284   EXPECT_NSEQ(@"Google", [item1 title]);
285
286   NSMenuItem* item2 = [menu itemAtIndex:1];
287   MockBridge::HistoryItem* hist2 = bridge_->HistoryItemForMenuItem(item2);
288   EXPECT_TRUE(hist2);
289   EXPECT_EQ(30, hist2->session_id);
290   EXPECT_EQ(2U, hist2->tabs.size());
291   // Do not test menu item title because it is localized.
292   NSMenu* submenu1 = [item2 submenu];
293   EXPECT_EQ(4U, [[submenu1 itemArray] count]);
294   // Do not test Restore All Tabs because it is localiced.
295   EXPECT_TRUE([[submenu1 itemAtIndex:1] isSeparatorItem]);
296   EXPECT_NSEQ(@"foo", [[submenu1 itemAtIndex:2] title]);
297   EXPECT_NSEQ(@"bar", [[submenu1 itemAtIndex:3] title]);
298
299   NSMenuItem* item3 = [menu itemAtIndex:2];
300   MockBridge::HistoryItem* hist3 = bridge_->HistoryItemForMenuItem(item3);
301   EXPECT_TRUE(hist3);
302   EXPECT_EQ(42, hist3->session_id);
303   EXPECT_NSEQ(@"Apple", [item3 title]);
304
305   NSMenuItem* item4 = [menu itemAtIndex:3];
306   MockBridge::HistoryItem* hist4 = bridge_->HistoryItemForMenuItem(item4);
307   EXPECT_TRUE(hist4);
308   EXPECT_EQ(50, hist4->session_id);
309   EXPECT_EQ(3U, hist4->tabs.size());
310   // Do not test menu item title because it is localized.
311   NSMenu* submenu2 = [item4 submenu];
312   EXPECT_EQ(5U, [[submenu2 itemArray] count]);
313   // Do not test Restore All Tabs because it is localiced.
314   EXPECT_TRUE([[submenu2 itemAtIndex:1] isSeparatorItem]);
315   EXPECT_NSEQ(@"magic", [[submenu2 itemAtIndex:2] title]);
316   EXPECT_NSEQ(@"goats", [[submenu2 itemAtIndex:3] title]);
317   EXPECT_NSEQ(@"teleporter", [[submenu2 itemAtIndex:4] title]);
318 }
319
320 // Tests that we properly request an icon from the FaviconService.
321 TEST_F(HistoryMenuBridgeTest, GetFaviconForHistoryItem) {
322   // Create a fake item.
323   HistoryMenuBridge::HistoryItem item;
324   item.title = base::ASCIIToUTF16("Title");
325   item.url = GURL("http://google.com");
326
327   // Request the icon.
328   GetFaviconForHistoryItem(&item);
329
330   // Make sure the item was modified properly.
331   EXPECT_TRUE(item.icon_requested);
332   EXPECT_NE(base::CancelableTaskTracker::kBadTaskId, item.icon_task_id);
333 }
334
335 TEST_F(HistoryMenuBridgeTest, GotFaviconData) {
336   // Create a dummy bitmap.
337   SkBitmap bitmap;
338   bitmap.allocN32Pixels(25, 25);
339   bitmap.eraseARGB(255, 255, 0, 0);
340
341   // Set up the HistoryItem.
342   HistoryMenuBridge::HistoryItem item;
343   item.menu_item.reset([[NSMenuItem alloc] init]);
344   GetFaviconForHistoryItem(&item);
345
346   // Pretend to be called back.
347   favicon_base::FaviconImageResult image_result;
348   image_result.image = gfx::Image::CreateFrom1xBitmap(bitmap);
349   GotFaviconData(&item, image_result);
350
351   // Make sure the callback works.
352   EXPECT_FALSE(item.icon_requested);
353   EXPECT_TRUE(item.icon.get());
354   EXPECT_TRUE([item.menu_item image]);
355 }
356
357 }  // namespace