// Copyright (c) 2010 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. #import #include #include "base/ref_counted_memory.h" #include "base/string_util.h" #include "base/sys_string_conversions.h" #include "chrome/app/chrome_dll_resource.h" #include "chrome/browser/browser.h" #include "chrome/browser/cancelable_request.h" #include "chrome/browser/cocoa/cocoa_test_helper.h" #include "chrome/browser/cocoa/history_menu_bridge.h" #include "chrome/browser/cocoa/browser_test_helper.h" #include "gfx/codec/png_codec.h" #include "testing/gtest/include/gtest/gtest.h" class HistoryMenuBridgeTest : public CocoaTest { public: virtual void SetUp() { CocoaTest::SetUp(); browser_test_helper_.profile()->CreateFaviconService(); } // We are a friend of HistoryMenuBridge (and have access to // protected methods), but none of the classes generated by TEST_F() // are. Wraps common commands. void ClearMenuSection(HistoryMenuBridge* bridge, NSMenu* menu, NSInteger tag, unsigned int count) { bridge->ClearMenuSection(menu, tag, count); } void AddItemToBridgeMenu(HistoryMenuBridge* bridge, HistoryMenuBridge::HistoryItem* item, NSMenu* menu, NSInteger tag, NSInteger index) { bridge->AddItemToMenu(item, menu, tag, index); } NSMenuItem* AddItemToMenu(NSMenu* menu, NSString* title, SEL selector, int tag) { NSMenuItem* item = [[[NSMenuItem alloc] initWithTitle:title action:NULL keyEquivalent:@""] autorelease]; [item setTag:tag]; if (selector) [item setAction:selector]; [menu addItem:item]; return item; } HistoryMenuBridge::HistoryItem* CreateItem(const string16& title) { HistoryMenuBridge::HistoryItem* item = new HistoryMenuBridge::HistoryItem(); item->title = title; item->url = GURL(title); return item; } void GetFaviconForHistoryItem(HistoryMenuBridge* bridge, HistoryMenuBridge::HistoryItem* item) { bridge->GetFaviconForHistoryItem(item); } void GotFaviconData(HistoryMenuBridge* bridge, FaviconService::Handle handle, bool know_favicon, scoped_refptr data, bool expired, GURL url) { bridge->GotFaviconData(handle, know_favicon, data, expired, url); } CancelableRequestConsumerTSimple& favicon_consumer(HistoryMenuBridge* bridge) { return bridge->favicon_consumer_; } BrowserTestHelper browser_test_helper_; }; // Edge case test for clearing until the end of a menu. TEST_F(HistoryMenuBridgeTest, ClearHistoryMenuUntilEnd) { scoped_ptr bridge( new HistoryMenuBridge(browser_test_helper_.profile())); EXPECT_TRUE(bridge.get()); NSMenu* menu = [[[NSMenu alloc] initWithTitle:@"history foo"] autorelease]; NSInteger tag = IDC_HISTORY_MENU_VISITED; AddItemToMenu(menu, @"HEADER", NULL, tag); AddItemToMenu(menu, @"alpha", @selector(openHistoryMenuItem:), ++tag); AddItemToMenu(menu, @"bravo", @selector(openHistoryMenuItem:), ++tag); AddItemToMenu(menu, @"charlie", @selector(openHistoryMenuItem:), ++tag); AddItemToMenu(menu, @"delta", @selector(openHistoryMenuItem:), ++tag); ClearMenuSection(bridge.get(), menu, IDC_HISTORY_MENU_VISITED, 4); EXPECT_EQ(1, [menu numberOfItems]); EXPECT_EQ(@"HEADER", [[menu itemWithTag:IDC_HISTORY_MENU_VISITED] title]); } // Skip menu items that are not hooked up to |-openHistoryMenuItem:|. TEST_F(HistoryMenuBridgeTest, ClearHistoryMenuSkipping) { scoped_ptr bridge( new HistoryMenuBridge(browser_test_helper_.profile())); EXPECT_TRUE(bridge.get()); NSMenu* menu = [[[NSMenu alloc] initWithTitle:@"history foo"] autorelease]; NSInteger section_tag = IDC_HISTORY_MENU_VISITED; AddItemToMenu(menu, @"HEADER", NULL, section_tag); NSInteger tag = section_tag; AddItemToMenu(menu, @"alpha", @selector(openHistoryMenuItem:), ++tag); AddItemToMenu(menu, @"bravo", @selector(openHistoryMenuItem:), ++tag); AddItemToMenu(menu, @"unhooked", NULL, ++tag); AddItemToMenu(menu, @"charlie", @selector(openHistoryMenuItem:), ++tag); ClearMenuSection(bridge.get(), menu, section_tag, 4); EXPECT_EQ(2, [menu numberOfItems]); EXPECT_EQ(@"HEADER", [[menu itemWithTag:section_tag] title]); EXPECT_EQ(@"unhooked", [[menu itemAtIndex:1] title]); } // Edge case test for clearing an empty menu. TEST_F(HistoryMenuBridgeTest, ClearHistoryMenuEmpty) { scoped_ptr bridge( new HistoryMenuBridge(browser_test_helper_.profile())); EXPECT_TRUE(bridge.get()); NSMenu* menu = [[[NSMenu alloc] initWithTitle:@"history foo"] autorelease]; AddItemToMenu(menu, @"HEADER", NULL, IDC_HISTORY_MENU_VISITED); ClearMenuSection(bridge.get(), menu, IDC_HISTORY_MENU_VISITED, 1); EXPECT_EQ(1, [menu numberOfItems]); EXPECT_EQ(@"HEADER", [[menu itemWithTag:IDC_HISTORY_MENU_VISITED] title]); } // Test that AddItemToMenu() properly adds HistoryItem objects as menus. TEST_F(HistoryMenuBridgeTest, AddItemToMenu) { scoped_ptr bridge( new HistoryMenuBridge(browser_test_helper_.profile())); EXPECT_TRUE(bridge.get()); NSMenu* menu = [[[NSMenu alloc] initWithTitle:@"history foo"] autorelease]; const string16 short_url = ASCIIToUTF16("http://foo/"); const string16 long_url = ASCIIToUTF16("http://super-duper-long-url--." "that.cannot.possibly.fit.even-in-80-columns" "or.be.reasonably-displayed-in-a-menu" "without.looking-ridiculous.com/"); // 140 chars total scoped_ptr item1(CreateItem(short_url)); AddItemToBridgeMenu(bridge.get(), item1.get(), menu, 100, 0); scoped_ptr item2(CreateItem(long_url)); AddItemToBridgeMenu(bridge.get(), item2.get(), menu, 101, 1); EXPECT_EQ(2, [menu numberOfItems]); EXPECT_EQ(@selector(openHistoryMenuItem:), [[menu itemAtIndex:0] action]); EXPECT_EQ(@selector(openHistoryMenuItem:), [[menu itemAtIndex:1] action]); EXPECT_EQ(100, [[menu itemAtIndex:0] tag]); EXPECT_EQ(101, [[menu itemAtIndex:1] tag]); // Make sure a short title looks fine NSString* s = [[menu itemAtIndex:0] title]; EXPECT_EQ(base::SysNSStringToUTF16(s), short_url); // Make sure a super-long title gets trimmed s = [[menu itemAtIndex:0] title]; EXPECT_TRUE([s length] < long_url.length()); // Confirm tooltips and confirm they are not trimmed (like the item // name might be). Add tolerance for URL fixer-upping; // e.g. http://foo becomes http://foo/) EXPECT_GE([[[menu itemAtIndex:0] toolTip] length], (2*short_url.length()-5)); EXPECT_GE([[[menu itemAtIndex:1] toolTip] length], (2*long_url.length()-5)); } // Tests that we properly request an icon from the FaviconService. TEST_F(HistoryMenuBridgeTest, GetFaviconForHistoryItem) { TestingProfile* profile = browser_test_helper_.profile(); HistoryMenuBridge bridge(profile); // Create a fake item. HistoryMenuBridge::HistoryItem item; item.title = ASCIIToUTF16("Title"); item.url = GURL("http://google.com"); // Request the icon. GetFaviconForHistoryItem(&bridge, &item); // Make sure that there is ClientData for the request. std::vector data; favicon_consumer(&bridge).GetAllClientData(&data); ASSERT_EQ(data.size(), 1U); EXPECT_EQ(&item, data[0]); // Make sure the item was modified properly. EXPECT_TRUE(item.icon_requested); EXPECT_GT(item.icon_handle, 0); } TEST_F(HistoryMenuBridgeTest, GotFaviconData) { TestingProfile* profile = browser_test_helper_.profile(); HistoryMenuBridge bridge(profile); // Create a dummy bitmap. SkBitmap bitmap; bitmap.setConfig(SkBitmap::kARGB_8888_Config, 25, 25); bitmap.allocPixels(); bitmap.eraseRGB(255, 0, 0); // Convert it to raw PNG bytes. We totally ignore color order here because // we just want to test the roundtrip through the Bridge, not that we can // make icons look pretty. std::vector raw; gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, true, &raw); scoped_refptr bytes(new RefCountedBytes(raw)); // Set up the HistoryItem. HistoryMenuBridge::HistoryItem item; scoped_nsobject menu_item([[NSMenuItem alloc] init]); item.menu_item = menu_item.get(); GetFaviconForHistoryItem(&bridge, &item); // Pretend to be called back. GotFaviconData(&bridge, item.icon_handle, true, bytes, false, GURL()); // Make sure the callback works. EXPECT_EQ(false, item.icon_requested); EXPECT_TRUE(item.icon.get()); EXPECT_TRUE([item.menu_item image]); }