menu_property_list_unittest.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. // Copyright 2019 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. #include "components/dbus/menu/menu_property_list.h"
  5. #include <memory>
  6. #include "base/strings/utf_string_conversions.h"
  7. #include "build/build_config.h"
  8. #include "build/chromecast_buildflags.h"
  9. #include "build/chromeos_buildflags.h"
  10. #include "components/dbus/properties/types.h"
  11. #include "testing/gtest/include/gtest/gtest.h"
  12. #include "ui/base/accelerators/accelerator.h"
  13. #include "ui/base/models/image_model.h"
  14. #include "ui/base/models/menu_model.h"
  15. #include "ui/base/models/menu_separator_types.h"
  16. #include "ui/base/models/simple_menu_model.h"
  17. #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
  18. #include "ui/events/test/keyboard_layout.h"
  19. #include "ui/gfx/image/image.h"
  20. #include "ui/gfx/image/image_skia.h"
  21. #include "ui/gfx/image/image_skia_rep.h"
  22. #if BUILDFLAG(IS_LINUX)
  23. #include "ui/ozone/public/ozone_platform.h"
  24. #endif
  25. namespace {
  26. class TestMenuModel : public ui::SimpleMenuModel,
  27. public ui::SimpleMenuModel::Delegate {
  28. public:
  29. TestMenuModel(bool checked,
  30. bool enabled,
  31. bool visible,
  32. const std::u16string& label,
  33. const gfx::Image& icon,
  34. const ui::Accelerator& accelerator)
  35. : ui::SimpleMenuModel(this),
  36. checked_(checked),
  37. enabled_(enabled),
  38. visible_(visible),
  39. label_(label),
  40. icon_(icon),
  41. accelerator_(accelerator) {}
  42. ~TestMenuModel() override = default;
  43. MenuItemProperties ComputeProperties() {
  44. return ComputeMenuPropertiesForMenuItem(this, 0);
  45. }
  46. protected:
  47. // ui::MenuModel::
  48. bool IsItemDynamicAt(size_t index) const override {
  49. EXPECT_EQ(index, 0u);
  50. // Return true so that GetIconForCommandId() will always be called.
  51. return true;
  52. }
  53. // ui::SimpleMenuModel::Delegate:
  54. bool IsCommandIdChecked(int command_id) const override {
  55. EXPECT_LE(command_id, 0);
  56. return checked_;
  57. }
  58. bool IsCommandIdEnabled(int command_id) const override {
  59. EXPECT_LE(command_id, 0);
  60. return enabled_;
  61. }
  62. bool IsCommandIdVisible(int command_id) const override {
  63. EXPECT_LE(command_id, 0);
  64. return visible_;
  65. }
  66. std::u16string GetLabelForCommandId(int command_id) const override {
  67. EXPECT_LE(command_id, 0);
  68. return label_;
  69. }
  70. ui::ImageModel GetIconForCommandId(int command_id) const override {
  71. EXPECT_LE(command_id, 0);
  72. return icon_.IsEmpty() ? ui::ImageModel()
  73. : ui::ImageModel::FromImage(icon_);
  74. }
  75. void ExecuteCommand(int command_id, int event_flags) override {
  76. EXPECT_LE(command_id, 0);
  77. }
  78. bool GetAcceleratorForCommandId(int command_id,
  79. ui::Accelerator* accelerator) const override {
  80. EXPECT_LE(command_id, 0);
  81. if (accelerator_ == ui::Accelerator())
  82. return false;
  83. *accelerator = accelerator_;
  84. return true;
  85. }
  86. private:
  87. const bool checked_;
  88. const bool enabled_;
  89. const bool visible_;
  90. const std::u16string label_;
  91. const gfx::Image icon_;
  92. const ui::Accelerator accelerator_;
  93. };
  94. class TestMenuModelBuilder {
  95. public:
  96. TestMenuModelBuilder() = default;
  97. ~TestMenuModelBuilder() = default;
  98. TestMenuModelBuilder SetType(ui::MenuModel::ItemType type) const {
  99. TestMenuModelBuilder builder = *this;
  100. builder.type_ = type;
  101. return builder;
  102. }
  103. TestMenuModelBuilder SetChecked(bool checked) const {
  104. TestMenuModelBuilder builder = *this;
  105. builder.checked_ = checked;
  106. return builder;
  107. }
  108. TestMenuModelBuilder SetEnabled(bool enabled) const {
  109. TestMenuModelBuilder builder = *this;
  110. builder.enabled_ = enabled;
  111. return builder;
  112. }
  113. TestMenuModelBuilder SetVisible(bool visible) const {
  114. TestMenuModelBuilder builder = *this;
  115. builder.visible_ = visible;
  116. return builder;
  117. }
  118. TestMenuModelBuilder SetLabel(const std::string& label) const {
  119. TestMenuModelBuilder builder = *this;
  120. builder.label_ = base::ASCIIToUTF16(label);
  121. return builder;
  122. }
  123. TestMenuModelBuilder SetIcon(const gfx::Image& icon) const {
  124. TestMenuModelBuilder builder = *this;
  125. builder.icon_ = icon;
  126. return builder;
  127. }
  128. TestMenuModelBuilder SetAccelerator(
  129. const ui::Accelerator& accelerator) const {
  130. TestMenuModelBuilder builder = *this;
  131. builder.accelerator_ = accelerator;
  132. return builder;
  133. }
  134. std::unique_ptr<TestMenuModel> Build() const {
  135. auto menu = std::make_unique<TestMenuModel>(checked_, enabled_, visible_,
  136. label_, icon_, accelerator_);
  137. switch (type_) {
  138. case ui::MenuModel::TYPE_COMMAND:
  139. menu->AddItem(0, label_);
  140. break;
  141. case ui::MenuModel::TYPE_TITLE:
  142. menu->AddTitle(label_);
  143. break;
  144. case ui::MenuModel::TYPE_CHECK:
  145. menu->AddCheckItem(0, label_);
  146. break;
  147. case ui::MenuModel::TYPE_RADIO:
  148. menu->AddRadioItem(0, label_, 0);
  149. break;
  150. case ui::MenuModel::TYPE_SEPARATOR:
  151. menu->AddSeparator(ui::MenuSeparatorType::SPACING_SEPARATOR);
  152. break;
  153. case ui::MenuModel::TYPE_BUTTON_ITEM:
  154. NOTIMPLEMENTED();
  155. break;
  156. case ui::MenuModel::TYPE_SUBMENU:
  157. menu->AddSubMenu(0, label_, nullptr);
  158. break;
  159. case ui::MenuModel::TYPE_ACTIONABLE_SUBMENU:
  160. menu->AddActionableSubMenu(0, label_, nullptr);
  161. break;
  162. case ui::MenuModel::TYPE_HIGHLIGHTED:
  163. menu->AddHighlightedItemWithIcon(0, label_,
  164. ui::ImageModel::FromImage(icon_));
  165. break;
  166. }
  167. return menu;
  168. }
  169. private:
  170. ui::MenuModel::ItemType type_ = ui::MenuModel::TYPE_COMMAND;
  171. bool checked_ = false;
  172. bool enabled_ = true;
  173. bool visible_ = true;
  174. std::u16string label_;
  175. gfx::Image icon_;
  176. ui::Accelerator accelerator_;
  177. };
  178. } // namespace
  179. TEST(MenuPropertyListTest, ComputePropertiesBasic) {
  180. auto builder = TestMenuModelBuilder();
  181. auto menu = builder.Build();
  182. MenuItemProperties props;
  183. EXPECT_EQ(menu->ComputeProperties(), props);
  184. // Same for TYPE_HIGHLIGHTED.
  185. menu = builder.SetType(ui::MenuModel::TYPE_HIGHLIGHTED).Build();
  186. EXPECT_EQ(menu->ComputeProperties(), props);
  187. }
  188. TEST(MenuPropertyListTest, ComputePropertiesCheck) {
  189. auto menu = TestMenuModelBuilder().SetType(ui::MenuModel::TYPE_CHECK).Build();
  190. MenuItemProperties props;
  191. props["toggle-type"] = MakeDbusVariant(DbusString("checkmark"));
  192. props["toggle-state"] = MakeDbusVariant(DbusInt32(0));
  193. EXPECT_EQ(menu->ComputeProperties(), props);
  194. }
  195. TEST(MenuPropertyListTest, ComputePropertiesRadio) {
  196. auto menu = TestMenuModelBuilder().SetType(ui::MenuModel::TYPE_RADIO).Build();
  197. MenuItemProperties props;
  198. props["toggle-type"] = MakeDbusVariant(DbusString("radio"));
  199. props["toggle-state"] = MakeDbusVariant(DbusInt32(0));
  200. EXPECT_EQ(menu->ComputeProperties(), props);
  201. }
  202. TEST(MenuPropertyListTest, ComputePropertiesCheckedState) {
  203. auto builder = TestMenuModelBuilder().SetChecked(true);
  204. // Types other than radio and check should not have toggle-state set.
  205. auto menu = builder.Build();
  206. MenuItemProperties props;
  207. EXPECT_EQ(menu->ComputeProperties(), props);
  208. // Radio and check buttons should have the toggle-state set.
  209. menu = builder.SetType(ui::MenuModel::TYPE_RADIO).Build();
  210. props["toggle-type"] = MakeDbusVariant(DbusString("radio"));
  211. props["toggle-state"] = MakeDbusVariant(DbusInt32(1));
  212. EXPECT_EQ(menu->ComputeProperties(), props);
  213. menu = builder.SetType(ui::MenuModel::TYPE_CHECK).Build();
  214. props["toggle-type"] = MakeDbusVariant(DbusString("checkmark"));
  215. EXPECT_EQ(menu->ComputeProperties(), props);
  216. }
  217. TEST(MenuPropertyListTest, ComputePropertiesSeparator) {
  218. auto menu =
  219. TestMenuModelBuilder().SetType(ui::MenuModel::TYPE_SEPARATOR).Build();
  220. MenuItemProperties props;
  221. props["type"] = MakeDbusVariant(DbusString("separator"));
  222. EXPECT_EQ(menu->ComputeProperties(), props);
  223. }
  224. TEST(MenuPropertyListTest, ComputePropertiesSubmenu) {
  225. auto builder = TestMenuModelBuilder();
  226. auto menu = builder.SetType(ui::MenuModel::TYPE_SUBMENU).Build();
  227. MenuItemProperties props;
  228. props["children-display"] = MakeDbusVariant(DbusString("submenu"));
  229. EXPECT_EQ(menu->ComputeProperties(), props);
  230. // Same for ACTIONABLE_SUBMENU.
  231. menu = builder.SetType(ui::MenuModel::TYPE_ACTIONABLE_SUBMENU).Build();
  232. EXPECT_EQ(menu->ComputeProperties(), props);
  233. }
  234. TEST(MenuPropertyListTest, ComputePropertiesEnabledState) {
  235. auto builder = TestMenuModelBuilder();
  236. // Enabled.
  237. auto menu = builder.SetEnabled(true).Build();
  238. MenuItemProperties props;
  239. EXPECT_EQ(menu->ComputeProperties(), props);
  240. // Disabled.
  241. menu = builder.SetEnabled(false).Build();
  242. props["enabled"] = MakeDbusVariant(DbusBoolean(false));
  243. EXPECT_EQ(menu->ComputeProperties(), props);
  244. }
  245. TEST(MenuPropertyListTest, ComputePropertiesVisibleState) {
  246. auto builder = TestMenuModelBuilder();
  247. // Visible.
  248. auto menu = builder.SetVisible(true).Build();
  249. MenuItemProperties props;
  250. EXPECT_EQ(menu->ComputeProperties(), props);
  251. // Hidden.
  252. menu = builder.SetVisible(false).Build();
  253. props["visible"] = MakeDbusVariant(DbusBoolean(false));
  254. EXPECT_EQ(menu->ComputeProperties(), props);
  255. }
  256. TEST(MenuPropertyListTest, ComputePropertiesLabel) {
  257. auto builder = TestMenuModelBuilder();
  258. // No label.
  259. auto menu = builder.SetLabel("").Build();
  260. MenuItemProperties props;
  261. EXPECT_EQ(menu->ComputeProperties(), props);
  262. // Non-empty label.
  263. menu = builder.SetLabel("label value").Build();
  264. props["label"] = MakeDbusVariant(DbusString("label value"));
  265. EXPECT_EQ(menu->ComputeProperties(), props);
  266. }
  267. TEST(MenuPropertyListTest, ComputePropertiesIcon) {
  268. auto builder = TestMenuModelBuilder();
  269. // No icon.
  270. auto menu = builder.SetIcon(gfx::Image()).Build();
  271. MenuItemProperties props;
  272. EXPECT_EQ(menu->ComputeProperties(), props);
  273. // Non-empty label.
  274. SkBitmap bitmap;
  275. bitmap.allocN32Pixels(10, 10);
  276. bitmap.eraseARGB(255, 123, 123, 123);
  277. gfx::ImageSkia image_skia;
  278. image_skia.AddRepresentation(gfx::ImageSkiaRep(bitmap, 1.0f));
  279. gfx::Image icon(image_skia);
  280. menu = builder.SetIcon(icon).Build();
  281. props["icon-data"] = MakeDbusVariant(DbusByteArray(icon.As1xPNGBytes()));
  282. EXPECT_EQ(menu->ComputeProperties(), props);
  283. }
  284. #if BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)
  285. TEST(MenuPropertyListTest, ComputePropertiesAccelerator) {
  286. // The Wayland implementation requires the keyboard layout to be set.
  287. // The ScopedKeyboardLayout does not unset the already existing layout engine,
  288. // so we do so here and restore in the end of the test.
  289. auto* const old_layout =
  290. ui::KeyboardLayoutEngineManager::GetKeyboardLayoutEngine();
  291. ui::KeyboardLayoutEngineManager::ResetKeyboardLayoutEngine();
  292. {
  293. ui::ScopedKeyboardLayout keyboard_layout(ui::KEYBOARD_LAYOUT_ENGLISH_US);
  294. auto builder = TestMenuModelBuilder();
  295. // No accelerator.
  296. auto menu = builder.SetAccelerator(ui::Accelerator()).Build();
  297. MenuItemProperties props;
  298. EXPECT_EQ(menu->ComputeProperties(), props);
  299. // Set a key.
  300. menu = builder.SetAccelerator(ui::Accelerator(ui::VKEY_A, 0)).Build();
  301. props["shortcut"] =
  302. MakeDbusVariant(MakeDbusArray(MakeDbusArray(DbusString("a"))));
  303. EXPECT_EQ(menu->ComputeProperties(), props);
  304. // Add modifiers.
  305. menu = builder
  306. .SetAccelerator(ui::Accelerator(
  307. ui::VKEY_A,
  308. ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN))
  309. .Build();
  310. props["shortcut"] = MakeDbusVariant(
  311. MakeDbusArray(MakeDbusArray(DbusString("Control"), DbusString("Alt"),
  312. DbusString("Shift"), DbusString("a"))));
  313. EXPECT_EQ(menu->ComputeProperties(), props);
  314. }
  315. if (old_layout)
  316. ui::KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(old_layout);
  317. }
  318. #endif // BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS)
  319. TEST(MenuPropertyListTest, ComputePropertyChanges) {
  320. MenuItemProperties old_props;
  321. old_props["1"] = MakeDbusVariant(DbusInt32(1)); // Remains the same.
  322. old_props["2"] = MakeDbusVariant(DbusInt32(2)); // Updates to -2.
  323. old_props["3"] = MakeDbusVariant(DbusInt32(3)); // Removed.
  324. MenuItemProperties new_props;
  325. new_props["1"] = MakeDbusVariant(DbusInt32(1));
  326. new_props["2"] = MakeDbusVariant(DbusInt32(-2));
  327. new_props["4"] = MakeDbusVariant(DbusInt32(4)); // Added.
  328. MenuPropertyList updated;
  329. MenuPropertyList removed;
  330. ComputeMenuPropertyChanges(old_props, new_props, &updated, &removed);
  331. EXPECT_EQ(updated, (MenuPropertyList{"2", "4"}));
  332. EXPECT_EQ(removed, (MenuPropertyList{"3"}));
  333. }