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