blob: 3d64a9aa729488996924307a17960d377968b530 [file] [log] [blame]
// Copyright (c) 2012 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.
#include "base/command_line.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/sync/sessions/sessions_sync_manager.h"
#include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
#include "chrome/browser/ui/cocoa/run_loop_testing.h"
#import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
#import "chrome/browser/ui/cocoa/view_resizer_pong.h"
#import "chrome/browser/ui/cocoa/wrench_menu/wrench_menu_controller.h"
#include "chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h"
#include "chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h"
#include "chrome/browser/ui/toolbar/wrench_menu_model.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/testing_profile.h"
#include "components/sync_driver/local_device_info_provider_mock.h"
#include "grit/theme_resources.h"
#include "sync/api/fake_sync_change_processor.h"
#include "sync/api/sync_error_factory_mock.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
#include "testing/platform_test.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
namespace {
class MockWrenchMenuModel : public WrenchMenuModel {
public:
MockWrenchMenuModel() : WrenchMenuModel() {}
~MockWrenchMenuModel() {
// This dirty, ugly hack gets around a bug in the test. In
// ~WrenchMenuModel(), there's a call to TabstripModel::RemoveObserver(this)
// which mysteriously leads to this crash: http://crbug.com/49206 . It
// seems that the vector of observers is getting hosed somewhere between
// |-[ToolbarController dealloc]| and ~MockWrenchMenuModel(). This line
// short-circuits the parent destructor to avoid this crash.
tab_strip_model_ = NULL;
}
MOCK_METHOD2(ExecuteCommand, void(int command_id, int event_flags));
};
class DummyRouter : public browser_sync::LocalSessionEventRouter {
public:
~DummyRouter() override {}
void StartRoutingTo(
browser_sync::LocalSessionEventHandler* handler) override {}
void Stop() override {}
};
class WrenchMenuControllerTest
: public CocoaProfileTest {
public:
WrenchMenuControllerTest()
: local_device_(new sync_driver::LocalDeviceInfoProviderMock(
"WrenchMenuControllerTest",
"Test Machine",
"Chromium 10k",
"Chrome 10k",
sync_pb::SyncEnums_DeviceType_TYPE_LINUX,
"device_id")) {
}
virtual ~WrenchMenuControllerTest() {}
virtual void SetUp() override {
CocoaProfileTest::SetUp();
ASSERT_TRUE(browser());
controller_.reset([[WrenchMenuController alloc] initWithBrowser:browser()]);
fake_model_.reset(new MockWrenchMenuModel);
manager_.reset(new browser_sync::SessionsSyncManager(
profile(),
local_device_.get(),
scoped_ptr<browser_sync::LocalSessionEventRouter>(
new DummyRouter())));
manager_->MergeDataAndStartSyncing(
syncer::SESSIONS,
syncer::SyncDataList(),
scoped_ptr<syncer::SyncChangeProcessor>(
new syncer::FakeSyncChangeProcessor),
scoped_ptr<syncer::SyncErrorFactory>(
new syncer::SyncErrorFactoryMock));
}
void RegisterRecentTabs(RecentTabsBuilderTestHelper* helper) {
helper->ExportToSessionsSyncManager(manager_.get());
}
browser_sync::OpenTabsUIDelegate* GetOpenTabsDelegate() {
return manager_.get();
}
virtual void TearDown() override {
fake_model_.reset();
controller_.reset();
manager_.reset();
CocoaProfileTest::TearDown();
}
WrenchMenuController* controller() {
return controller_.get();
}
base::scoped_nsobject<WrenchMenuController> controller_;
scoped_ptr<MockWrenchMenuModel> fake_model_;
private:
scoped_ptr<browser_sync::SessionsSyncManager> manager_;
scoped_ptr<sync_driver::LocalDeviceInfoProviderMock> local_device_;
};
TEST_F(WrenchMenuControllerTest, Initialized) {
EXPECT_TRUE([controller() menu]);
EXPECT_GE([[controller() menu] numberOfItems], 5);
}
TEST_F(WrenchMenuControllerTest, DispatchSimple) {
base::scoped_nsobject<NSButton> button([[NSButton alloc] init]);
[button setTag:IDC_ZOOM_PLUS];
// Set fake model to test dispatching.
EXPECT_CALL(*fake_model_, ExecuteCommand(IDC_ZOOM_PLUS, 0));
[controller() setModel:fake_model_.get()];
[controller() dispatchWrenchMenuCommand:button.get()];
chrome::testing::NSRunLoopRunAllPending();
}
TEST_F(WrenchMenuControllerTest, RecentTabsFavIcon) {
RecentTabsBuilderTestHelper recent_tabs_builder;
recent_tabs_builder.AddSession();
recent_tabs_builder.AddWindow(0);
recent_tabs_builder.AddTab(0, 0);
RegisterRecentTabs(&recent_tabs_builder);
RecentTabsSubMenuModel recent_tabs_sub_menu_model(
NULL, browser(), GetOpenTabsDelegate());
fake_model_->AddSubMenuWithStringId(
IDC_RECENT_TABS_MENU, IDS_RECENT_TABS_MENU,
&recent_tabs_sub_menu_model);
[controller() setModel:fake_model_.get()];
NSMenu* menu = [controller() menu];
[controller() updateRecentTabsSubmenu];
NSString* title = l10n_util::GetNSStringWithFixup(IDS_RECENT_TABS_MENU);
NSMenu* recent_tabs_menu = [[menu itemWithTitle:title] submenu];
EXPECT_TRUE(recent_tabs_menu);
EXPECT_EQ(6, [recent_tabs_menu numberOfItems]);
// Send a icon changed event and verify that the icon is updated.
gfx::Image icon(ResourceBundle::GetSharedInstance().GetNativeImageNamed(
IDR_BOOKMARKS_FAVICON));
recent_tabs_sub_menu_model.SetIcon(3, icon);
EXPECT_NSNE(icon.ToNSImage(), [[recent_tabs_menu itemAtIndex:3] image]);
recent_tabs_sub_menu_model.GetMenuModelDelegate()->OnIconChanged(3);
EXPECT_TRUE([[recent_tabs_menu itemAtIndex:3] image]);
EXPECT_NSEQ(icon.ToNSImage(), [[recent_tabs_menu itemAtIndex:3] image]);
controller_.reset();
fake_model_.reset();
}
TEST_F(WrenchMenuControllerTest, RecentTabsElideTitle) {
// Add 1 session with 1 window and 2 tabs.
RecentTabsBuilderTestHelper recent_tabs_builder;
recent_tabs_builder.AddSession();
recent_tabs_builder.AddWindow(0);
base::string16 tab1_short_title = base::ASCIIToUTF16("Short");
recent_tabs_builder.AddTabWithInfo(0, 0, base::Time::Now(), tab1_short_title);
base::string16 tab2_long_title = base::ASCIIToUTF16(
"Very very very very very very very very very very very very long");
recent_tabs_builder.AddTabWithInfo(0, 0,
base::Time::Now() - base::TimeDelta::FromMinutes(10), tab2_long_title);
RegisterRecentTabs(&recent_tabs_builder);
RecentTabsSubMenuModel recent_tabs_sub_menu_model(
NULL, browser(), GetOpenTabsDelegate());
fake_model_->AddSubMenuWithStringId(
IDC_RECENT_TABS_MENU, IDS_RECENT_TABS_MENU,
&recent_tabs_sub_menu_model);
[controller() setModel:fake_model_.get()];
NSMenu* menu = [controller() menu];
[controller() updateRecentTabsSubmenu];
NSString* title = l10n_util::GetNSStringWithFixup(IDS_RECENT_TABS_MENU);
NSMenu* recent_tabs_menu = [[menu itemWithTitle:title] submenu];
EXPECT_TRUE(recent_tabs_menu);
EXPECT_EQ(7, [recent_tabs_menu numberOfItems]);
// Index 0: restore tabs menu item.
NSString* restore_tab_label = l10n_util::FixUpWindowsStyleLabel(
recent_tabs_sub_menu_model.GetLabelAt(0));
EXPECT_NSEQ(restore_tab_label, [[recent_tabs_menu itemAtIndex:0] title]);
// Item 1: separator.
EXPECT_TRUE([[recent_tabs_menu itemAtIndex:1] isSeparatorItem]);
// Item 2: window title.
EXPECT_NSEQ(
base::SysUTF16ToNSString(recent_tabs_sub_menu_model.GetLabelAt(2)),
[[recent_tabs_menu itemAtIndex:2] title]);
// Item 3: short tab title.
EXPECT_NSEQ(base::SysUTF16ToNSString(tab1_short_title),
[[recent_tabs_menu itemAtIndex:3] title]);
// Item 4: long tab title.
NSString* tab2_actual_title = [[recent_tabs_menu itemAtIndex:4] title];
NSUInteger title_length = [tab2_actual_title length];
EXPECT_GT(tab2_long_title.size(), title_length);
NSString* actual_substring =
[tab2_actual_title substringToIndex:title_length - 1];
NSString* expected_substring = [base::SysUTF16ToNSString(tab2_long_title)
substringToIndex:title_length - 1];
EXPECT_NSEQ(expected_substring, actual_substring);
controller_.reset();
fake_model_.reset();
}
// Verify that |RecentTabsMenuModelDelegate| is deleted before the model
// it's observing.
TEST_F(WrenchMenuControllerTest, RecentTabDeleteOrder) {
[controller_ menuNeedsUpdate:[controller_ menu]];
// If the delete order is wrong then the test will crash on exit.
}
} // namespace