blob: 4134bc2e3e69df768cb87abe7611234b79def948 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/dbus/menu/menu_property_list.h"
#include <memory>
#include "base/memory/ref_counted_memory.h"
#include "base/notimplemented.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "build/chromecast_buildflags.h"
#include "components/dbus/properties/types.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/models/image_model.h"
#include "ui/base/models/menu_model.h"
#include "ui/base/models/menu_separator_types.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_rep.h"
#include "ui/menus/simple_menu_model.h"
namespace {
class TestMenuModel : public ui::SimpleMenuModel,
public ui::SimpleMenuModel::Delegate {
public:
TestMenuModel(bool checked,
bool enabled,
bool visible,
const std::u16string& label,
const gfx::Image& icon,
const ui::Accelerator& accelerator)
: ui::SimpleMenuModel(this),
checked_(checked),
enabled_(enabled),
visible_(visible),
label_(label),
icon_(icon),
accelerator_(accelerator) {}
~TestMenuModel() override = default;
MenuItemProperties ComputeProperties() {
return ComputeMenuPropertiesForMenuItem(this, 0);
}
protected:
// ui::MenuModel::
bool IsItemDynamicAt(size_t index) const override {
EXPECT_EQ(index, 0u);
// Return true so that GetIconForCommandId() will always be called.
return true;
}
// ui::SimpleMenuModel::Delegate:
bool IsCommandIdChecked(int command_id) const override {
EXPECT_LE(command_id, 0);
return checked_;
}
bool IsCommandIdEnabled(int command_id) const override {
EXPECT_LE(command_id, 0);
return enabled_;
}
bool IsCommandIdVisible(int command_id) const override {
EXPECT_LE(command_id, 0);
return visible_;
}
std::u16string GetLabelForCommandId(int command_id) const override {
EXPECT_LE(command_id, 0);
return label_;
}
ui::ImageModel GetIconForCommandId(int command_id) const override {
EXPECT_LE(command_id, 0);
return icon_.IsEmpty() ? ui::ImageModel()
: ui::ImageModel::FromImage(icon_);
}
void ExecuteCommand(int command_id, int event_flags) override {
EXPECT_LE(command_id, 0);
}
bool GetAcceleratorForCommandId(int command_id,
ui::Accelerator* accelerator) const override {
EXPECT_LE(command_id, 0);
if (accelerator_ == ui::Accelerator()) {
return false;
}
*accelerator = accelerator_;
return true;
}
private:
const bool checked_;
const bool enabled_;
const bool visible_;
const std::u16string label_;
const gfx::Image icon_;
const ui::Accelerator accelerator_;
};
class TestMenuModelBuilder {
public:
TestMenuModelBuilder() = default;
~TestMenuModelBuilder() = default;
TestMenuModelBuilder SetType(ui::MenuModel::ItemType type) const {
TestMenuModelBuilder builder = *this;
builder.type_ = type;
return builder;
}
TestMenuModelBuilder SetChecked(bool checked) const {
TestMenuModelBuilder builder = *this;
builder.checked_ = checked;
return builder;
}
TestMenuModelBuilder SetEnabled(bool enabled) const {
TestMenuModelBuilder builder = *this;
builder.enabled_ = enabled;
return builder;
}
TestMenuModelBuilder SetVisible(bool visible) const {
TestMenuModelBuilder builder = *this;
builder.visible_ = visible;
return builder;
}
TestMenuModelBuilder SetLabel(const std::string& label) const {
TestMenuModelBuilder builder = *this;
builder.label_ = base::ASCIIToUTF16(label);
return builder;
}
TestMenuModelBuilder SetIcon(const gfx::Image& icon) const {
TestMenuModelBuilder builder = *this;
builder.icon_ = icon;
return builder;
}
TestMenuModelBuilder SetAccelerator(
const ui::Accelerator& accelerator) const {
TestMenuModelBuilder builder = *this;
builder.accelerator_ = accelerator;
return builder;
}
std::unique_ptr<TestMenuModel> Build() const {
auto menu = std::make_unique<TestMenuModel>(checked_, enabled_, visible_,
label_, icon_, accelerator_);
switch (type_) {
case ui::MenuModel::TYPE_COMMAND:
menu->AddItem(0, label_);
break;
case ui::MenuModel::TYPE_TITLE:
menu->AddTitle(label_);
break;
case ui::MenuModel::TYPE_CHECK:
menu->AddCheckItem(0, label_);
break;
case ui::MenuModel::TYPE_RADIO:
menu->AddRadioItem(0, label_, 0);
break;
case ui::MenuModel::TYPE_SEPARATOR:
menu->AddSeparator(ui::MenuSeparatorType::SPACING_SEPARATOR);
break;
case ui::MenuModel::TYPE_BUTTON_ITEM:
NOTIMPLEMENTED();
break;
case ui::MenuModel::TYPE_SUBMENU:
menu->AddSubMenu(0, label_, nullptr);
break;
case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU:
menu->AddActionableSubMenu(0, label_, nullptr);
break;
case ui::MenuModel::TYPE_HIGHLIGHTED:
menu->AddHighlightedItemWithIcon(0, label_,
ui::ImageModel::FromImage(icon_));
break;
}
return menu;
}
private:
ui::MenuModel::ItemType type_ = ui::MenuModel::TYPE_COMMAND;
bool checked_ = false;
bool enabled_ = true;
bool visible_ = true;
std::u16string label_;
gfx::Image icon_;
ui::Accelerator accelerator_;
};
} // namespace
TEST(MenuPropertyListTest, ComputePropertiesBasic) {
auto builder = TestMenuModelBuilder();
auto menu = builder.Build();
MenuItemProperties props;
EXPECT_EQ(menu->ComputeProperties(), props);
// Same for TYPE_HIGHLIGHTED.
menu = builder.SetType(ui::MenuModel::TYPE_HIGHLIGHTED).Build();
EXPECT_EQ(menu->ComputeProperties(), props);
}
TEST(MenuPropertyListTest, ComputePropertiesCheck) {
auto menu = TestMenuModelBuilder().SetType(ui::MenuModel::TYPE_CHECK).Build();
MenuItemProperties props;
props["toggle-type"] = MakeDbusVariant(DbusString("checkmark"));
props["toggle-state"] = MakeDbusVariant(DbusInt32(0));
EXPECT_EQ(menu->ComputeProperties(), props);
}
TEST(MenuPropertyListTest, ComputePropertiesRadio) {
auto menu = TestMenuModelBuilder().SetType(ui::MenuModel::TYPE_RADIO).Build();
MenuItemProperties props;
props["toggle-type"] = MakeDbusVariant(DbusString("radio"));
props["toggle-state"] = MakeDbusVariant(DbusInt32(0));
EXPECT_EQ(menu->ComputeProperties(), props);
}
TEST(MenuPropertyListTest, ComputePropertiesCheckedState) {
auto builder = TestMenuModelBuilder().SetChecked(true);
// Types other than radio and check should not have toggle-state set.
auto menu = builder.Build();
MenuItemProperties props;
EXPECT_EQ(menu->ComputeProperties(), props);
// Radio and check buttons should have the toggle-state set.
menu = builder.SetType(ui::MenuModel::TYPE_RADIO).Build();
props["toggle-type"] = MakeDbusVariant(DbusString("radio"));
props["toggle-state"] = MakeDbusVariant(DbusInt32(1));
EXPECT_EQ(menu->ComputeProperties(), props);
menu = builder.SetType(ui::MenuModel::TYPE_CHECK).Build();
props["toggle-type"] = MakeDbusVariant(DbusString("checkmark"));
EXPECT_EQ(menu->ComputeProperties(), props);
}
TEST(MenuPropertyListTest, ComputePropertiesSeparator) {
auto menu =
TestMenuModelBuilder().SetType(ui::MenuModel::TYPE_SEPARATOR).Build();
MenuItemProperties props;
props["type"] = MakeDbusVariant(DbusString("separator"));
EXPECT_EQ(menu->ComputeProperties(), props);
}
TEST(MenuPropertyListTest, ComputePropertiesSubmenu) {
auto builder = TestMenuModelBuilder();
auto menu = builder.SetType(ui::MenuModel::TYPE_SUBMENU).Build();
MenuItemProperties props;
props["children-display"] = MakeDbusVariant(DbusString("submenu"));
EXPECT_EQ(menu->ComputeProperties(), props);
// Same for ACTIONABLE_SUBMENU.
menu = builder.SetType(ui::MenuModel::TYPE_ACTIONABLE_SUBMENU).Build();
EXPECT_EQ(menu->ComputeProperties(), props);
}
TEST(MenuPropertyListTest, ComputePropertiesEnabledState) {
auto builder = TestMenuModelBuilder();
// Enabled.
auto menu = builder.SetEnabled(true).Build();
MenuItemProperties props;
EXPECT_EQ(menu->ComputeProperties(), props);
// Disabled.
menu = builder.SetEnabled(false).Build();
props["enabled"] = MakeDbusVariant(DbusBoolean(false));
EXPECT_EQ(menu->ComputeProperties(), props);
}
TEST(MenuPropertyListTest, ComputePropertiesVisibleState) {
auto builder = TestMenuModelBuilder();
// Visible.
auto menu = builder.SetVisible(true).Build();
MenuItemProperties props;
EXPECT_EQ(menu->ComputeProperties(), props);
// Hidden.
menu = builder.SetVisible(false).Build();
props["visible"] = MakeDbusVariant(DbusBoolean(false));
EXPECT_EQ(menu->ComputeProperties(), props);
}
TEST(MenuPropertyListTest, ComputePropertiesLabel) {
auto builder = TestMenuModelBuilder();
// No label.
auto menu = builder.SetLabel("").Build();
MenuItemProperties props;
EXPECT_EQ(menu->ComputeProperties(), props);
// Non-empty label.
menu = builder.SetLabel("label value").Build();
props["label"] = MakeDbusVariant(DbusString("label value"));
EXPECT_EQ(menu->ComputeProperties(), props);
}
TEST(MenuPropertyListTest, ComputePropertiesIcon) {
auto builder = TestMenuModelBuilder();
// No icon.
auto menu = builder.SetIcon(gfx::Image()).Build();
MenuItemProperties props;
EXPECT_EQ(menu->ComputeProperties(), props);
// Non-empty label.
SkBitmap bitmap;
bitmap.allocN32Pixels(10, 10);
bitmap.eraseARGB(255, 123, 123, 123);
gfx::ImageSkia image_skia;
image_skia.AddRepresentation(gfx::ImageSkiaRep(bitmap, 1.0f));
gfx::Image icon(image_skia);
menu = builder.SetIcon(icon).Build();
props["icon-data"] = MakeDbusVariant(DbusByteArray(icon.As1xPNGBytes()));
EXPECT_EQ(menu->ComputeProperties(), props);
}
TEST(MenuPropertyListTest, ComputePropertiesAccelerator) {
auto builder = TestMenuModelBuilder();
// No accelerator.
auto menu = builder.SetAccelerator(ui::Accelerator()).Build();
MenuItemProperties props;
EXPECT_EQ(menu->ComputeProperties(), props);
// Set a key.
menu = builder.SetAccelerator(ui::Accelerator(ui::VKEY_A, 0)).Build();
props["shortcut"] =
MakeDbusVariant(MakeDbusArray(MakeDbusArray(DbusString("a"))));
EXPECT_EQ(menu->ComputeProperties(), props);
// Add modifiers.
menu = builder
.SetAccelerator(ui::Accelerator(
ui::VKEY_A,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN))
.Build();
props["shortcut"] = MakeDbusVariant(
MakeDbusArray(MakeDbusArray(DbusString("Control"), DbusString("Alt"),
DbusString("Shift"), DbusString("a"))));
EXPECT_EQ(menu->ComputeProperties(), props);
}
TEST(MenuPropertyListTest, ComputePropertyChanges) {
MenuItemProperties old_props;
old_props["1"] = MakeDbusVariant(DbusInt32(1)); // Remains the same.
old_props["2"] = MakeDbusVariant(DbusInt32(2)); // Updates to -2.
old_props["3"] = MakeDbusVariant(DbusInt32(3)); // Removed.
MenuItemProperties new_props;
new_props["1"] = MakeDbusVariant(DbusInt32(1));
new_props["2"] = MakeDbusVariant(DbusInt32(-2));
new_props["4"] = MakeDbusVariant(DbusInt32(4)); // Added.
MenuPropertyList updated;
MenuPropertyList removed;
ComputeMenuPropertyChanges(old_props, new_props, &updated, &removed);
EXPECT_EQ(updated, (MenuPropertyList{"2", "4"}));
EXPECT_EQ(removed, (MenuPropertyList{"3"}));
}