| // Copyright 2016 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 <unordered_map> |
| #include <utility> |
| #include <vector> |
| |
| #include "ash/link_handler_model.h" |
| #include "base/format_macros.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "chrome/app/chrome_command_ids.h" |
| #include "chrome/browser/renderer_context_menu/mock_render_view_context_menu.h" |
| #include "chrome/browser/renderer_context_menu/open_with_menu_factory_ash.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/renderer_context_menu/render_view_context_menu_observer.h" |
| #include "components/renderer_context_menu/render_view_context_menu_proxy.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/gfx/image/image_skia.h" |
| |
| namespace { |
| |
| // All tests in this file assume that 4 and 10 IDC command IDs are reserved |
| // for the main and sub menus, respectively. |
| const int kFirstMainMenuId = IDC_CONTENT_CONTEXT_OPEN_WITH1; |
| const int kFirstSubMenuId = kFirstMainMenuId + 4; |
| const int kNumCommandIds = |
| IDC_CONTENT_CONTEXT_OPEN_WITH_LAST - IDC_CONTENT_CONTEXT_OPEN_WITH1 + 1; |
| |
| static_assert(kNumCommandIds == 14, |
| "invalid number of command IDs reserved for open with"); |
| |
| std::vector<ash::LinkHandlerInfo> CreateLinkHandlerInfo(size_t num_apps) { |
| std::vector<ash::LinkHandlerInfo> handlers; |
| for (size_t i = 0; i < num_apps; ++i) { |
| gfx::ImageSkia image_skia; |
| image_skia.AddRepresentation(gfx::ImageSkiaRep(gfx::Size(1, 1), 1.0f)); |
| ash::LinkHandlerInfo info = { |
| base::StringPrintf("App %" PRIuS, i), |
| // Use an empty image for the first item to test ModelChanged() with |
| // both empty and non-empty icons. |
| (i == 0) ? gfx::Image() : gfx::Image(image_skia), i}; |
| handlers.push_back(info); |
| } |
| return handlers; |
| } |
| |
| std::pair<OpenWithMenuObserver::HandlerMap, int> BuildHandlersMap( |
| size_t num_apps) { |
| return OpenWithMenuObserver::BuildHandlersMapForTesting( |
| CreateLinkHandlerInfo(num_apps)); |
| } |
| |
| base::string16 GetTitle(size_t i) { |
| return l10n_util::GetStringFUTF16( |
| IDS_CONTENT_CONTEXT_OPEN_WITH_APP, |
| base::UTF8ToUTF16(base::StringPrintf("App %" PRIuS, i))); |
| } |
| |
| } // namespace |
| |
| TEST(OpenWithMenuObserverTest, TestBuildHandlersMap) { |
| auto result = BuildHandlersMap(0); |
| EXPECT_EQ(0U, result.first.size()); |
| EXPECT_EQ(-1, result.second); |
| |
| result = BuildHandlersMap(1); |
| ASSERT_EQ(1U, result.first.size()); |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId)); |
| EXPECT_EQ(-1, result.second); |
| EXPECT_EQ("App 0", result.first[kFirstMainMenuId].name); |
| |
| result = BuildHandlersMap(2); |
| EXPECT_EQ(2U, result.first.size()); |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId)); |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId + 1)); |
| EXPECT_EQ(-1, result.second); |
| EXPECT_EQ("App 0", result.first[kFirstMainMenuId].name); |
| EXPECT_EQ("App 1", result.first[kFirstMainMenuId + 1].name); |
| |
| result = BuildHandlersMap(3); |
| EXPECT_EQ(3U, result.first.size()); |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId)); |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId + 1)); |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId + 2)); |
| EXPECT_EQ(-1, result.second); |
| EXPECT_EQ("App 0", result.first[kFirstMainMenuId].name); |
| EXPECT_EQ("App 1", result.first[kFirstMainMenuId + 1].name); |
| EXPECT_EQ("App 2", result.first[kFirstMainMenuId + 2].name); |
| |
| // Test if app names will overflow to the sub menu. |
| result = BuildHandlersMap(4); |
| EXPECT_EQ(4U, result.first.size()); |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId)); |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId + 1)); |
| // In this case, kFirstMainMenuId + 2 should be hidden. |
| EXPECT_EQ(kFirstMainMenuId + 3, result.second); |
| ASSERT_EQ(1U, result.first.count(kFirstSubMenuId)); |
| ASSERT_EQ(1U, result.first.count(kFirstSubMenuId + 1)); |
| EXPECT_EQ("App 0", result.first[kFirstMainMenuId].name); |
| EXPECT_EQ("App 1", result.first[kFirstMainMenuId + 1].name); |
| EXPECT_EQ("App 2", result.first[kFirstSubMenuId].name); |
| EXPECT_EQ("App 3", result.first[kFirstSubMenuId + 1].name); |
| |
| result = BuildHandlersMap(11); |
| EXPECT_EQ(11U, result.first.size()); |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId)); |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId + 1)); |
| EXPECT_EQ(kFirstMainMenuId + 3, result.second); |
| EXPECT_EQ("App 0", result.first[kFirstMainMenuId].name); |
| EXPECT_EQ("App 1", result.first[kFirstMainMenuId + 1].name); |
| for (size_t i = 0; i < 9; ++i) { |
| ASSERT_EQ(1U, result.first.count(kFirstSubMenuId + i)) << i; |
| } |
| |
| // The main and sub menus can show up to 12 (=3+10-1) app names. |
| result = BuildHandlersMap(12); |
| EXPECT_EQ(12U, result.first.size()); |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId)); |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId + 1)); |
| EXPECT_EQ(kFirstMainMenuId + 3, result.second); |
| EXPECT_EQ("App 0", result.first[kFirstMainMenuId].name); |
| EXPECT_EQ("App 1", result.first[kFirstMainMenuId + 1].name); |
| for (size_t i = 0; i < 10; ++i) { |
| const int id = kFirstSubMenuId + i; |
| ASSERT_EQ(1U, result.first.count(id)) << i; |
| EXPECT_EQ(base::StringPrintf("App %zu", i + 2), result.first[id].name) << i; |
| } |
| |
| result = BuildHandlersMap(13); |
| EXPECT_EQ(12U, result.first.size()); // still 12 |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId)); |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId + 1)); |
| EXPECT_EQ(kFirstMainMenuId + 3, result.second); |
| EXPECT_EQ("App 0", result.first[kFirstMainMenuId].name); |
| EXPECT_EQ("App 1", result.first[kFirstMainMenuId + 1].name); |
| for (size_t i = 0; i < 10; ++i) { // still 10 |
| const int id = kFirstSubMenuId + i; |
| ASSERT_EQ(1U, result.first.count(id)) << i; |
| EXPECT_EQ(base::StringPrintf("App %zu", i + 2), result.first[id].name) << i; |
| } |
| |
| result = BuildHandlersMap(1000); |
| EXPECT_EQ(12U, result.first.size()); // still 12 |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId)); |
| ASSERT_EQ(1U, result.first.count(kFirstMainMenuId + 1)); |
| EXPECT_EQ(kFirstMainMenuId + 3, result.second); |
| EXPECT_EQ("App 0", result.first[kFirstMainMenuId].name); |
| EXPECT_EQ("App 1", result.first[kFirstMainMenuId + 1].name); |
| for (size_t i = 0; i < 10; ++i) { // still 10 |
| const int id = kFirstSubMenuId + i; |
| ASSERT_EQ(1U, result.first.count(id)) << i; |
| EXPECT_EQ(base::StringPrintf("App %zu", i + 2), result.first[id].name) << i; |
| } |
| } |
| |
| TEST(OpenWithMenuObserverTest, TestModelChanged) { |
| content::TestBrowserThreadBundle thread_bundle; |
| MockRenderViewContextMenu mock_menu(false); |
| OpenWithMenuObserver observer(&mock_menu); |
| mock_menu.SetObserver(&observer); |
| |
| // Do the initial setup. |
| ui::SimpleMenuModel sub_menu(nullptr); |
| OpenWithMenuObserver::AddPlaceholderItemsForTesting(&mock_menu, &sub_menu); |
| CHECK_EQ(static_cast<size_t>(kNumCommandIds), mock_menu.GetMenuSize()); |
| |
| // Check that all menu items are hidden when there is no app to show. |
| const size_t kZeroApps = 0; |
| MockRenderViewContextMenu::MockMenuItem item; |
| observer.ModelChanged(CreateLinkHandlerInfo(kZeroApps)); |
| EXPECT_EQ(static_cast<size_t>(kNumCommandIds), mock_menu.GetMenuSize()); |
| for (size_t i = 0; i < mock_menu.GetMenuSize(); ++i) { |
| EXPECT_TRUE(mock_menu.GetMenuItem(i, &item)) << i; |
| EXPECT_TRUE(item.hidden) << i; |
| } |
| |
| // Check that all 3 apps are on the main menu. |
| const size_t kThreeApps = 3; |
| observer.ModelChanged(CreateLinkHandlerInfo(kThreeApps)); |
| EXPECT_EQ(static_cast<size_t>(kNumCommandIds), mock_menu.GetMenuSize()); |
| for (size_t i = 0; i < kThreeApps; ++i) { |
| EXPECT_TRUE(mock_menu.GetMenuItem(i, &item)) << i; |
| EXPECT_TRUE(item.enabled) << i; |
| EXPECT_FALSE(item.hidden) << i; |
| EXPECT_EQ(GetTitle(i), item.title) << i; |
| if (i == 0) { |
| EXPECT_TRUE(item.icon.IsEmpty()); |
| } else { |
| EXPECT_FALSE(item.icon.IsEmpty()); |
| } |
| } |
| for (size_t i = kThreeApps; i < mock_menu.GetMenuSize(); ++i) { |
| EXPECT_TRUE(mock_menu.GetMenuItem(i, &item)) << i; |
| EXPECT_TRUE(item.hidden) << i; |
| } |
| |
| // Check that the first 2 apps are on the main menu and the rest is on the |
| // submenu. |
| const size_t kFourApps = 4; |
| observer.ModelChanged(CreateLinkHandlerInfo(kFourApps)); |
| EXPECT_EQ(static_cast<size_t>(kNumCommandIds), mock_menu.GetMenuSize()); |
| for (size_t i = 0; i < 2; ++i) { |
| EXPECT_TRUE(mock_menu.GetMenuItem(i, &item)) << i; |
| EXPECT_TRUE(item.enabled) << i; |
| EXPECT_FALSE(item.hidden) << i; |
| EXPECT_EQ(GetTitle(i), item.title) << i; |
| if (i == 0) { |
| EXPECT_TRUE(item.icon.IsEmpty()); |
| } else { |
| EXPECT_FALSE(item.icon.IsEmpty()) << i; |
| } |
| } |
| // The third item on the main menu should be hidden. |
| EXPECT_TRUE(mock_menu.GetMenuItem(2, &item)); |
| EXPECT_TRUE(item.hidden); |
| // The parent of the submenu. |
| EXPECT_TRUE(mock_menu.GetMenuItem(3, &item)); |
| EXPECT_TRUE(item.enabled); |
| EXPECT_FALSE(item.hidden); |
| EXPECT_EQ(l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_MORE_APPS), |
| item.title); |
| for (size_t i = 4; i < 6; ++i) { |
| EXPECT_TRUE(mock_menu.GetMenuItem(i, &item)) << i; |
| EXPECT_TRUE(item.enabled) << i; |
| EXPECT_FALSE(item.hidden) << i; |
| EXPECT_EQ(GetTitle(i - 2), item.title) << i; |
| EXPECT_FALSE(item.icon.IsEmpty()) << i; |
| } |
| for (size_t i = 6; i < mock_menu.GetMenuSize(); ++i) { |
| EXPECT_TRUE(mock_menu.GetMenuItem(i, &item)) << i; |
| EXPECT_TRUE(item.hidden) << i; |
| } |
| } |