Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / ui / views / controls / menu / menu_controller_unittest.cc
blob381f489d716904e9f89445d1505420ea516a73bc
1 // Copyright 2014 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.
5 #include "ui/views/controls/menu/menu_controller.h"
7 #include "base/run_loop.h"
8 #include "ui/aura/scoped_window_targeter.h"
9 #include "ui/aura/window.h"
10 #include "ui/events/event_targeter.h"
11 #include "ui/events/platform/platform_event_source.h"
12 #include "ui/views/controls/menu/menu_item_view.h"
13 #include "ui/views/test/views_test_base.h"
14 #include "ui/wm/public/dispatcher_client.h"
16 #if defined(OS_WIN)
17 #include "base/message_loop/message_pump_dispatcher.h"
18 #elif defined(USE_X11)
19 #include <X11/Xlib.h>
20 #undef Bool
21 #undef None
22 #include "ui/events/devices/x11/device_data_manager_x11.h"
23 #include "ui/events/test/events_test_utils_x11.h"
24 #elif defined(USE_OZONE)
25 #include "ui/events/event.h"
26 #endif
28 namespace views {
30 namespace {
32 class TestMenuItemView : public MenuItemView {
33 public:
34 TestMenuItemView() : MenuItemView(NULL) {}
35 ~TestMenuItemView() override {}
37 private:
38 DISALLOW_COPY_AND_ASSIGN(TestMenuItemView);
41 class TestPlatformEventSource : public ui::PlatformEventSource {
42 public:
43 TestPlatformEventSource() {
44 #if defined(USE_X11)
45 ui::DeviceDataManagerX11::CreateInstance();
46 #endif
48 ~TestPlatformEventSource() override {}
50 uint32_t Dispatch(const ui::PlatformEvent& event) {
51 return DispatchEvent(event);
54 private:
55 DISALLOW_COPY_AND_ASSIGN(TestPlatformEventSource);
58 class TestNullTargeter : public ui::EventTargeter {
59 public:
60 TestNullTargeter() {}
61 ~TestNullTargeter() override {}
63 ui::EventTarget* FindTargetForEvent(ui::EventTarget* root,
64 ui::Event* event) override {
65 return NULL;
68 private:
69 DISALLOW_COPY_AND_ASSIGN(TestNullTargeter);
72 class TestDispatcherClient : public aura::client::DispatcherClient {
73 public:
74 TestDispatcherClient() : dispatcher_(NULL) {}
75 ~TestDispatcherClient() override {}
77 base::MessagePumpDispatcher* dispatcher() {
78 return dispatcher_;
81 // aura::client::DispatcherClient:
82 void PrepareNestedLoopClosures(base::MessagePumpDispatcher* dispatcher,
83 base::Closure* run_closure,
84 base::Closure* quit_closure) override {
85 scoped_ptr<base::RunLoop> run_loop(new base::RunLoop());
86 *quit_closure = run_loop->QuitClosure();
87 *run_closure = base::Bind(&TestDispatcherClient::RunNestedDispatcher,
88 base::Unretained(this),
89 base::Passed(&run_loop),
90 dispatcher);
93 private:
94 void RunNestedDispatcher(scoped_ptr<base::RunLoop> run_loop,
95 base::MessagePumpDispatcher* dispatcher) {
96 base::AutoReset<base::MessagePumpDispatcher*> reset_dispatcher(&dispatcher_,
97 dispatcher);
98 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
99 base::MessageLoop::ScopedNestableTaskAllower allow(loop);
100 run_loop->Run();
103 base::MessagePumpDispatcher* dispatcher_;
105 DISALLOW_COPY_AND_ASSIGN(TestDispatcherClient);
108 } // namespace
110 class MenuControllerTest : public ViewsTestBase {
111 public:
112 MenuControllerTest() : controller_(NULL) {}
113 ~MenuControllerTest() override { ResetMenuController(); }
115 // Dispatches |count| number of items, each in a separate iteration of the
116 // message-loop, by posting a task.
117 void Step3_DispatchEvents(int count) {
118 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
119 base::MessageLoop::ScopedNestableTaskAllower allow(loop);
120 controller_->exit_type_ = MenuController::EXIT_ALL;
122 DispatchEvent();
123 if (count) {
124 base::MessageLoop::current()->PostTask(
125 FROM_HERE,
126 base::Bind(&MenuControllerTest::Step3_DispatchEvents,
127 base::Unretained(this),
128 count - 1));
129 } else {
130 EXPECT_TRUE(run_loop_->running());
131 run_loop_->Quit();
135 // Runs a nested message-loop that does not involve the menu itself.
136 void Step2_RunNestedLoop() {
137 base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
138 base::MessageLoop::ScopedNestableTaskAllower allow(loop);
139 base::MessageLoop::current()->PostTask(
140 FROM_HERE,
141 base::Bind(&MenuControllerTest::Step3_DispatchEvents,
142 base::Unretained(this),
143 3));
144 run_loop_.reset(new base::RunLoop());
145 run_loop_->Run();
148 void Step1_RunMenu() {
149 base::MessageLoop::current()->PostTask(
150 FROM_HERE,
151 base::Bind(&MenuControllerTest::Step2_RunNestedLoop,
152 base::Unretained(this)));
153 scoped_ptr<Widget> owner(CreateOwnerWidget());
154 RunMenu(owner.get());
157 scoped_ptr<Widget> CreateOwnerWidget() {
158 scoped_ptr<Widget> widget(new Widget);
159 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
160 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
161 widget->Init(params);
162 widget->Show();
164 aura::client::SetDispatcherClient(
165 widget->GetNativeWindow()->GetRootWindow(), &dispatcher_client_);
166 return widget.Pass();
169 void RunMenu(views::Widget* owner) {
170 scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView);
171 ResetMenuController();
172 controller_ = new MenuController(NULL, true, NULL);
173 controller_->owner_ = owner;
174 controller_->showing_ = true;
175 controller_->SetSelection(menu_item.get(),
176 MenuController::SELECTION_UPDATE_IMMEDIATELY);
177 controller_->RunMessageLoop(false);
180 #if defined(USE_X11)
181 void DispatchEscapeAndExpect(MenuController::ExitType exit_type) {
182 ui::ScopedXI2Event key_event;
183 key_event.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0);
184 event_source_.Dispatch(key_event);
185 EXPECT_EQ(exit_type, controller_->exit_type());
186 controller_->exit_type_ = MenuController::EXIT_ALL;
187 DispatchEvent();
189 #endif
191 void DispatchEvent() {
192 #if defined(USE_X11)
193 XEvent xevent;
194 memset(&xevent, 0, sizeof(xevent));
195 event_source_.Dispatch(&xevent);
196 #elif defined(OS_WIN)
197 MSG msg;
198 memset(&msg, 0, sizeof(MSG));
199 dispatcher_client_.dispatcher()->Dispatch(msg);
200 #elif defined(USE_OZONE)
201 ui::KeyEvent event(' ', ui::VKEY_SPACE, ui::EF_NONE);
202 event_source_.Dispatch(&event);
203 #else
204 #error Unsupported platform
205 #endif
208 private:
209 void ResetMenuController() {
210 if (controller_) {
211 // These properties are faked by RunMenu for the purposes of testing and
212 // need to be undone before we call the destructor.
213 controller_->owner_ = NULL;
214 controller_->showing_ = false;
215 delete controller_;
216 controller_ = NULL;
220 // A weak pointer to the MenuController owned by this class.
221 MenuController* controller_;
222 scoped_ptr<base::RunLoop> run_loop_;
223 TestPlatformEventSource event_source_;
224 TestDispatcherClient dispatcher_client_;
226 DISALLOW_COPY_AND_ASSIGN(MenuControllerTest);
229 TEST_F(MenuControllerTest, Basic) {
230 base::MessageLoop::ScopedNestableTaskAllower allow_nested(
231 base::MessageLoop::current());
232 message_loop()->PostTask(
233 FROM_HERE,
234 base::Bind(&MenuControllerTest::Step1_RunMenu, base::Unretained(this)));
237 #if defined(OS_LINUX) && defined(USE_X11)
238 // Tests that an event targeter which blocks events will be honored by the menu
239 // event dispatcher.
240 TEST_F(MenuControllerTest, EventTargeter) {
242 // Verify that the menu handles the escape key under normal circumstances.
243 scoped_ptr<Widget> owner(CreateOwnerWidget());
244 message_loop()->PostTask(
245 FROM_HERE,
246 base::Bind(&MenuControllerTest::DispatchEscapeAndExpect,
247 base::Unretained(this),
248 MenuController::EXIT_OUTERMOST));
249 RunMenu(owner.get());
253 // With the NULL targeter instantiated and assigned we expect the menu to
254 // not handle the key event.
255 scoped_ptr<Widget> owner(CreateOwnerWidget());
256 aura::ScopedWindowTargeter scoped_targeter(
257 owner->GetNativeWindow()->GetRootWindow(),
258 scoped_ptr<ui::EventTargeter>(new TestNullTargeter));
259 message_loop()->PostTask(
260 FROM_HERE,
261 base::Bind(&MenuControllerTest::DispatchEscapeAndExpect,
262 base::Unretained(this),
263 MenuController::EXIT_NONE));
264 RunMenu(owner.get());
267 #endif
269 } // namespace views