menu_model_adapter_test.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
1// Copyright (c) 2012 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
5#include "base/callback.h"
6#include "base/strings/utf_string_conversions.h"
7#include "chrome/test/base/interactive_test_utils.h"
8#include "chrome/test/base/ui_test_utils.h"
9#include "chrome/test/base/view_event_test_base.h"
10#include "ui/base/models/menu_model.h"
11#include "ui/base/test/ui_controls.h"
12#include "ui/views/controls/button/menu_button.h"
13#include "ui/views/controls/button/menu_button_listener.h"
14#include "ui/views/controls/menu/menu_controller.h"
15#include "ui/views/controls/menu/menu_item_view.h"
16#include "ui/views/controls/menu/menu_model_adapter.h"
17#include "ui/views/controls/menu/menu_runner.h"
18#include "ui/views/controls/menu/submenu_view.h"
19#include "ui/views/test/test_views_delegate.h"
20#include "ui/views/widget/root_view.h"
21#include "ui/views/widget/widget.h"
22
23namespace {
24
25const int kTopMenuBaseId = 100;
26const int kSubMenuBaseId = 200;
27
28// Implement most of the ui::MenuModel pure virtual methods for subclasses
29//
30// Exceptions:
31//  virtual int GetItemCount() const = 0;
32//  virtual ItemType GetTypeAt(int index) const = 0;
33//  virtual int GetCommandIdAt(int index) const = 0;
34//  virtual base::string16 GetLabelAt(int index) const = 0;
35class CommonMenuModel : public ui::MenuModel {
36 public:
37  CommonMenuModel() {
38  }
39
40  virtual ~CommonMenuModel() {
41  }
42
43 protected:
44  // ui::MenuModel implementation.
45  virtual bool HasIcons() const OVERRIDE {
46    return false;
47  }
48
49  virtual bool IsItemDynamicAt(int index) const OVERRIDE {
50    return false;
51  }
52
53  virtual bool GetAcceleratorAt(int index,
54                                ui::Accelerator* accelerator) const OVERRIDE {
55    return false;
56  }
57
58  virtual ui::MenuSeparatorType GetSeparatorTypeAt(int index) const OVERRIDE {
59    return ui::NORMAL_SEPARATOR;
60  }
61
62  virtual bool IsItemCheckedAt(int index) const OVERRIDE {
63    return false;
64  }
65
66  virtual int GetGroupIdAt(int index) const OVERRIDE {
67    return 0;
68  }
69
70  virtual bool GetIconAt(int index, gfx::Image* icon) OVERRIDE {
71    return false;
72  }
73
74  virtual ui::ButtonMenuItemModel* GetButtonMenuItemAt(
75      int index) const OVERRIDE {
76    return NULL;
77  }
78
79  virtual bool IsEnabledAt(int index) const OVERRIDE {
80    return true;
81  }
82
83  virtual ui::MenuModel* GetSubmenuModelAt(int index) const OVERRIDE {
84    return NULL;
85  }
86
87  virtual void HighlightChangedTo(int index) OVERRIDE {
88  }
89
90  virtual void ActivatedAt(int index) OVERRIDE {
91  }
92
93  virtual void SetMenuModelDelegate(ui::MenuModelDelegate* delegate) OVERRIDE {
94  }
95
96  virtual ui::MenuModelDelegate* GetMenuModelDelegate() const OVERRIDE {
97    return NULL;
98  }
99
100 private:
101  DISALLOW_COPY_AND_ASSIGN(CommonMenuModel);
102};
103
104class SubMenuModel : public CommonMenuModel {
105 public:
106  SubMenuModel()
107      : showing_(false) {
108  }
109
110  virtual ~SubMenuModel() {
111  }
112
113  bool showing() const {
114    return showing_;
115  }
116
117 private:
118  // ui::MenuModel implementation.
119  virtual int GetItemCount() const OVERRIDE {
120    return 1;
121  }
122
123  virtual ItemType GetTypeAt(int index) const OVERRIDE {
124    return TYPE_COMMAND;
125  }
126
127  virtual int GetCommandIdAt(int index) const OVERRIDE {
128    return index + kSubMenuBaseId;
129  }
130
131  virtual base::string16 GetLabelAt(int index) const OVERRIDE {
132    return ASCIIToUTF16("Item");
133  }
134
135  virtual void MenuWillShow() OVERRIDE {
136    showing_ = true;
137  }
138
139  // Called when the menu has been closed.
140  virtual void MenuClosed() OVERRIDE {
141    showing_ = false;
142  }
143
144  bool showing_;
145
146  DISALLOW_COPY_AND_ASSIGN(SubMenuModel);
147};
148
149class TopMenuModel : public CommonMenuModel {
150 public:
151  TopMenuModel() {
152  }
153
154  virtual ~TopMenuModel() {
155  }
156
157  bool IsSubmenuShowing() {
158    return sub_menu_model_.showing();
159  }
160
161 private:
162  // ui::MenuModel implementation.
163  virtual int GetItemCount() const OVERRIDE {
164    return 1;
165  }
166
167  virtual ItemType GetTypeAt(int index) const OVERRIDE {
168    return TYPE_SUBMENU;
169  }
170
171  virtual int GetCommandIdAt(int index) const OVERRIDE {
172    return index + kTopMenuBaseId;
173  }
174
175  virtual base::string16 GetLabelAt(int index) const OVERRIDE {
176    return ASCIIToUTF16("submenu");
177  }
178
179  virtual MenuModel* GetSubmenuModelAt(int index) const OVERRIDE {
180    return &sub_menu_model_;
181  }
182
183  mutable SubMenuModel sub_menu_model_;
184
185  DISALLOW_COPY_AND_ASSIGN(TopMenuModel);
186};
187
188}  // namespace
189
190class MenuModelAdapterTest : public ViewEventTestBase,
191                             public views::MenuButtonListener {
192 public:
193  MenuModelAdapterTest()
194      : ViewEventTestBase(),
195        button_(NULL),
196        menu_model_adapter_(&top_menu_model_),
197        menu_(NULL) {
198    old_views_delegate_ = views::ViewsDelegate::views_delegate;
199    views::ViewsDelegate::views_delegate = &views_delegate_;
200  }
201
202  virtual ~MenuModelAdapterTest() {
203    views::ViewsDelegate::views_delegate = old_views_delegate_;
204  }
205
206  // ViewEventTestBase implementation.
207
208  virtual void SetUp() OVERRIDE {
209    button_ = new views::MenuButton(
210        NULL, ASCIIToUTF16("Menu Adapter Test"), this, true);
211
212    menu_ = menu_model_adapter_.CreateMenu();
213    menu_runner_.reset(new views::MenuRunner(menu_));
214
215    ViewEventTestBase::SetUp();
216  }
217
218  virtual void TearDown() OVERRIDE {
219    menu_runner_.reset(NULL);
220    menu_ = NULL;
221    ViewEventTestBase::TearDown();
222  }
223
224  virtual views::View* CreateContentsView() OVERRIDE {
225    return button_;
226  }
227
228  virtual gfx::Size GetPreferredSize() OVERRIDE {
229    return button_->GetPreferredSize();
230  }
231
232  // views::MenuButtonListener implementation.
233  virtual void OnMenuButtonClicked(views::View* source,
234                                   const gfx::Point& point) OVERRIDE {
235    gfx::Point screen_location;
236    views::View::ConvertPointToScreen(source, &screen_location);
237    gfx::Rect bounds(screen_location, source->size());
238    ignore_result(menu_runner_->RunMenuAt(
239        source->GetWidget(),
240        button_,
241        bounds,
242        views::MenuItemView::TOPLEFT,
243        ui::MENU_SOURCE_NONE,
244        views::MenuRunner::HAS_MNEMONICS));
245  }
246
247  // ViewEventTestBase implementation
248  virtual void DoTestOnMessageLoop() OVERRIDE {
249    Click(button_, CreateEventTask(this, &MenuModelAdapterTest::Step1));
250  }
251
252  // Open the submenu.
253  void Step1() {
254    views::SubmenuView* topmenu = menu_->GetSubmenu();
255    ASSERT_TRUE(topmenu);
256    ASSERT_TRUE(topmenu->IsShowing());
257    ASSERT_FALSE(top_menu_model_.IsSubmenuShowing());
258
259    // Click the first item to open the submenu.
260    views::MenuItemView* item = topmenu->GetMenuItemAt(0);
261    ASSERT_TRUE(item);
262    Click(item, CreateEventTask(this, &MenuModelAdapterTest::Step2));
263  }
264
265  // Rebuild the menu which should close the submenu.
266  void Step2() {
267    views::SubmenuView* topmenu = menu_->GetSubmenu();
268    ASSERT_TRUE(topmenu);
269    ASSERT_TRUE(topmenu->IsShowing());
270    ASSERT_TRUE(top_menu_model_.IsSubmenuShowing());
271
272    menu_model_adapter_.BuildMenu(menu_);
273
274    base::MessageLoopForUI::current()->PostTask(
275        FROM_HERE, CreateEventTask(this, &MenuModelAdapterTest::Step3));
276  }
277
278  // Verify that the submenu MenuModel received the close callback
279  // and close the menu.
280  void Step3() {
281    views::SubmenuView* topmenu = menu_->GetSubmenu();
282    ASSERT_TRUE(topmenu);
283    ASSERT_TRUE(topmenu->IsShowing());
284    ASSERT_FALSE(top_menu_model_.IsSubmenuShowing());
285
286    // Click the button to exit the menu.
287    Click(button_, CreateEventTask(this, &MenuModelAdapterTest::Step4));
288  }
289
290  // All done.
291  void Step4() {
292    views::SubmenuView* topmenu = menu_->GetSubmenu();
293    ASSERT_TRUE(topmenu);
294    ASSERT_FALSE(topmenu->IsShowing());
295    ASSERT_FALSE(top_menu_model_.IsSubmenuShowing());
296
297    Done();
298  }
299
300 private:
301  // Generate a mouse click on the specified view and post a new task.
302  virtual void Click(views::View* view, const base::Closure& next) {
303    ui_test_utils::MoveMouseToCenterAndPress(
304        view,
305        ui_controls::LEFT,
306        ui_controls::DOWN | ui_controls::UP,
307        next);
308  }
309
310  views::ViewsDelegate* old_views_delegate_;
311  views::TestViewsDelegate views_delegate_;
312
313  views::MenuButton* button_;
314  TopMenuModel top_menu_model_;
315  views::MenuModelAdapter menu_model_adapter_;
316  views::MenuItemView* menu_;
317  scoped_ptr<views::MenuRunner> menu_runner_;
318};
319
320VIEW_TEST(MenuModelAdapterTest, RebuildMenu)
321