Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(237)

Unified Diff: ui/views/controls/menu/menu_controller_unittest.cc

Issue 1138523006: Enable keyboard accelerators while a menu is open (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Working solution (Even on Windows) Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/views/controls/menu/menu_controller_unittest.cc
diff --git a/ui/views/controls/menu/menu_controller_unittest.cc b/ui/views/controls/menu/menu_controller_unittest.cc
index 3b29d7bc1b4ee2cf40a7da00199d083675f09e54..786b158d2c6127abca20f6460a127c9a0e323c56 100644
--- a/ui/views/controls/menu/menu_controller_unittest.cc
+++ b/ui/views/controls/menu/menu_controller_unittest.cc
@@ -4,28 +4,22 @@
#include "ui/views/controls/menu/menu_controller.h"
-#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/aura/scoped_window_targeter.h"
#include "ui/aura/window.h"
#include "ui/events/event_handler.h"
#include "ui/events/null_event_targeter.h"
-#include "ui/events/platform/platform_event_source.h"
+#include "ui/events/test/event_generator.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/test/views_test_base.h"
-#include "ui/wm/public/dispatcher_client.h"
-#if defined(OS_WIN)
-#include "base/message_loop/message_pump_dispatcher.h"
-#elif defined(USE_X11)
+#if defined(USE_X11)
#include <X11/Xlib.h>
#undef Bool
#undef None
#include "ui/events/devices/x11/device_data_manager_x11.h"
#include "ui/events/test/events_test_utils_x11.h"
-#elif defined(USE_OZONE)
-#include "ui/events/event.h"
#endif
namespace views {
@@ -41,357 +35,241 @@ class TestMenuItemView : public MenuItemView {
DISALLOW_COPY_AND_ASSIGN(TestMenuItemView);
};
-class TestPlatformEventSource : public ui::PlatformEventSource {
- public:
- TestPlatformEventSource() {
-#if defined(USE_X11)
- ui::DeviceDataManagerX11::CreateInstance();
-#endif
- }
- ~TestPlatformEventSource() override {}
-
- uint32_t Dispatch(const ui::PlatformEvent& event) {
- return DispatchEvent(event);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(TestPlatformEventSource);
-};
-
-class TestDispatcherClient : public aura::client::DispatcherClient {
+class TestEventHandler : public ui::EventHandler {
public:
- TestDispatcherClient() : dispatcher_(nullptr) {}
- ~TestDispatcherClient() override {}
+ TestEventHandler() : outstanding_touches_(0) {}
- base::MessagePumpDispatcher* dispatcher() {
- return dispatcher_;
+ void OnTouchEvent(ui::TouchEvent* event) override {
+ switch(event->type()) {
+ case ui::ET_TOUCH_PRESSED:
+ outstanding_touches_++;
+ break;
+ case ui::ET_TOUCH_RELEASED:
+ case ui::ET_TOUCH_CANCELLED:
+ outstanding_touches_--;
+ break;
+ default:
+ break;
+ }
}
- // aura::client::DispatcherClient:
- void PrepareNestedLoopClosures(base::MessagePumpDispatcher* dispatcher,
- base::Closure* run_closure,
- base::Closure* quit_closure) override {
- scoped_ptr<base::RunLoop> run_loop(new base::RunLoop());
- *quit_closure = run_loop->QuitClosure();
- *run_closure = base::Bind(&TestDispatcherClient::RunNestedDispatcher,
- base::Unretained(this),
- base::Passed(&run_loop),
- dispatcher);
- }
+ int outstanding_touches() const { return outstanding_touches_; }
private:
- void RunNestedDispatcher(scoped_ptr<base::RunLoop> run_loop,
- base::MessagePumpDispatcher* dispatcher) {
- base::AutoReset<base::MessagePumpDispatcher*> reset_dispatcher(&dispatcher_,
- dispatcher);
- base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
- base::MessageLoop::ScopedNestableTaskAllower allow(loop);
- run_loop->Run();
- }
-
- base::MessagePumpDispatcher* dispatcher_;
-
- DISALLOW_COPY_AND_ASSIGN(TestDispatcherClient);
+ int outstanding_touches_;
};
} // namespace
class MenuControllerTest : public ViewsTestBase {
public:
- MenuControllerTest() : controller_(nullptr) {}
- ~MenuControllerTest() override { ResetMenuController(); }
-
- // Dispatches |count| number of items, each in a separate iteration of the
- // message-loop, by posting a task.
- void Step3_DispatchEvents(int count) {
- base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
- base::MessageLoop::ScopedNestableTaskAllower allow(loop);
- controller_->exit_type_ = MenuController::EXIT_ALL;
-
- DispatchEvent();
- if (count) {
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&MenuControllerTest::Step3_DispatchEvents,
- base::Unretained(this),
- count - 1));
- } else {
- EXPECT_TRUE(run_loop_->running());
- run_loop_->Quit();
- }
+ MenuControllerTest()
+ : owner_(),
+ event_generator_(),
+ menu_item_(),
oshima 2015/06/12 19:59:18 you can skip these
afakhry 2015/06/12 23:50:46 I know but I always like to be explicit .. however
+ menu_controller_(nullptr) {
}
- // Runs a nested message-loop that does not involve the menu itself.
- void Step2_RunNestedLoop() {
- base::MessageLoopForUI* loop = base::MessageLoopForUI::current();
- base::MessageLoop::ScopedNestableTaskAllower allow(loop);
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&MenuControllerTest::Step3_DispatchEvents,
- base::Unretained(this),
- 3));
- run_loop_.reset(new base::RunLoop());
- run_loop_->Run();
+ ~MenuControllerTest() override {}
+
+ void SetUp() override {
+ ViewsTestBase::SetUp();
+ Init();
}
- void Step1_RunMenu() {
- base::MessageLoop::current()->PostTask(
- FROM_HERE,
- base::Bind(&MenuControllerTest::Step2_RunNestedLoop,
- base::Unretained(this)));
- scoped_ptr<Widget> owner(CreateOwnerWidget());
- RunMenu(owner.get());
+ void TearDown() override {
+ if (menu_controller_) {
+ menu_controller_->owner_ = nullptr;
+ menu_controller_->showing_ = false;
oshima 2015/06/12 19:59:18 do you need these?
afakhry 2015/06/12 23:50:46 Yes the destructor will crash on a DCHECK() if |sh
+ delete menu_controller_;
+ menu_controller_ = nullptr;
oshima 2015/06/12 19:59:18 any reason why we shouldn't use scoped_ptr for thi
afakhry 2015/06/12 23:50:46 Done. Using a scoped_ptr with a specialized Delete
+ }
+
+ ViewsTestBase::TearDown();
}
- scoped_ptr<Widget> CreateOwnerWidget() {
- scoped_ptr<Widget> widget(new Widget);
- Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
- params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
- widget->Init(params);
- widget->Show();
+ void ReleaseTouchId(int id) {
+ event_generator_->ReleaseTouchId(id);
+ }
- aura::client::SetDispatcherClient(
- widget->GetNativeWindow()->GetRootWindow(), &dispatcher_client_);
- return widget.Pass();
+ void PressKey(ui::KeyboardCode key_code) {
+ event_generator_->PressKey(key_code, 0);
}
- const MenuItemView* pending_state_item() const {
- return controller_->pending_state_.item;
+ void TestEventTargeter() {
+ // With the NULL targeter instantiated and assigned we expect the menu to
oshima 2015/06/12 19:59:18 nullptr
afakhry 2015/06/12 23:50:46 Actually NULL here refers to the ui::NullEventTarg
+ // not handle the key event.
+ scoped_ptr<aura::ScopedWindowTargeter> scoped_targeter(
+ new aura::ScopedWindowTargeter(
+ owner()->GetNativeWindow()->GetRootWindow(),
+ scoped_ptr<ui::EventTargeter>(new ui::NullEventTargeter)));
+ event_generator_->PressKey(ui::VKEY_ESCAPE, 0);
+ EXPECT_EQ(MenuController::EXIT_NONE, menu_exit_type());
+
+ // Now remove the targeter an expect to exit the menu normally.
+ scoped_targeter.reset();
+ event_generator_->PressKey(ui::VKEY_ESCAPE, 0);
+ EXPECT_EQ(MenuController::EXIT_OUTERMOST, menu_exit_type());
}
+ protected:
void SetPendingStateItem(MenuItemView* item) {
- controller_->pending_state_.item = item;
+ menu_controller_->pending_state_.item = item;
}
void ResetSelection() {
- controller_->SetSelection(nullptr,
- MenuController::SELECTION_EXIT |
- MenuController::SELECTION_UPDATE_IMMEDIATELY);
+ menu_controller_->SetSelection(
+ nullptr,
+ MenuController::SELECTION_EXIT |
+ MenuController::SELECTION_UPDATE_IMMEDIATELY);
}
void IncrementSelection(int delta) {
- controller_->IncrementSelection(delta);
+ menu_controller_->IncrementSelection(delta);
}
MenuItemView* FindFirstSelectableMenuItem(MenuItemView* parent) {
- return controller_->FindFirstSelectableMenuItem(parent);
+ return menu_controller_->FindFirstSelectableMenuItem(parent);
}
MenuItemView* FindNextSelectableMenuItem(MenuItemView* parent,
int index,
int delta) {
- return controller_->FindNextSelectableMenuItem(parent, index, delta);
- }
- void SetupMenu(views::Widget* owner, views::MenuItemView* item) {
- ResetMenuController();
- controller_ = new MenuController(nullptr, true, nullptr);
- controller_->owner_ = owner;
- controller_->showing_ = true;
- controller_->SetSelection(item,
- MenuController::SELECTION_UPDATE_IMMEDIATELY);
+
+ return menu_controller_->FindNextSelectableMenuItem(parent, index, delta);
}
- void RunMenu(views::Widget* owner) {
- scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView);
- SetupMenu(owner, menu_item.get());
- controller_->RunMessageLoop(false);
+ void RunMenu() {
+ menu_controller_->RunMessageLoop(false);
}
-#if defined(USE_X11)
- void DispatchEscapeAndExpect(MenuController::ExitType exit_type) {
- ui::ScopedXI2Event key_event;
- key_event.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_ESCAPE, 0);
- event_source_.Dispatch(key_event);
- EXPECT_EQ(exit_type, controller_->exit_type());
- controller_->exit_type_ = MenuController::EXIT_ALL;
- DispatchEvent();
+ Widget* owner() { return owner_.get(); }
+ ui::test::EventGenerator* event_generator() { return event_generator_.get(); }
+ TestMenuItemView* menu_item() { return menu_item_.get(); }
+ MenuController* menu_controller() { return menu_controller_; }
+ const MenuItemView* pending_state_item() const {
+ return menu_controller_->pending_state_.item;
}
+ const MenuController::ExitType menu_exit_type() const {
+ return menu_controller_->exit_type_;
+ }
+
+ private:
+ void Init() {
+ owner_.reset(new Widget);
+ Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP);
+ params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ owner_->Init(params);
+ event_generator_.reset(new ui::test::EventGenerator(
+ owner_->GetNativeWindow()->GetRootWindow(), owner_->GetNativeWindow()));
+ owner_->Show();
- void DispatchTouch(int evtype, int id) {
- ui::ScopedXI2Event touch_event;
- std::vector<ui::Valuator> valuators;
- touch_event.InitTouchEvent(1, evtype, id, gfx::Point(10, 10), valuators);
- event_source_.Dispatch(touch_event);
- DispatchEvent();
+ SetupMenuItem();
+
+ SetupMenuController();
}
-#endif
- void DispatchEvent() {
-#if defined(USE_X11)
- XEvent xevent;
- memset(&xevent, 0, sizeof(xevent));
- event_source_.Dispatch(&xevent);
-#elif defined(OS_WIN)
- MSG msg;
- memset(&msg, 0, sizeof(MSG));
- dispatcher_client_.dispatcher()->Dispatch(msg);
-#elif defined(USE_OZONE)
- ui::KeyEvent event(' ', ui::VKEY_SPACE, ui::EF_NONE);
- event_source_.Dispatch(&event);
-#else
-#error Unsupported platform
-#endif
+ void SetupMenuItem() {
+ menu_item_.reset(new TestMenuItemView);
+ menu_item_->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One"));
+ menu_item_->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two"));
+ menu_item_->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("Three"));
+ menu_item_->AppendMenuItemWithLabel(4, base::ASCIIToUTF16("Four"));
}
- private:
- void ResetMenuController() {
- if (controller_) {
- // These properties are faked by RunMenu for the purposes of testing and
- // need to be undone before we call the destructor.
- controller_->owner_ = nullptr;
- controller_->showing_ = false;
- delete controller_;
- controller_ = nullptr;
- }
+ void SetupMenuController() {
+ menu_controller_ = new MenuController(nullptr, true, nullptr);
+ menu_controller_->owner_ = owner_.get();
+ menu_controller_->showing_ = true;
+ menu_controller_->SetSelection(
+ menu_item_.get(), MenuController::SELECTION_UPDATE_IMMEDIATELY);
}
- // A weak pointer to the MenuController owned by this class.
- MenuController* controller_;
- scoped_ptr<base::RunLoop> run_loop_;
- TestPlatformEventSource event_source_;
- TestDispatcherClient dispatcher_client_;
+ scoped_ptr<Widget> owner_;
+ scoped_ptr<ui::test::EventGenerator> event_generator_;
+ scoped_ptr<TestMenuItemView> menu_item_;
+ MenuController* menu_controller_;
DISALLOW_COPY_AND_ASSIGN(MenuControllerTest);
};
-TEST_F(MenuControllerTest, Basic) {
- base::MessageLoop::ScopedNestableTaskAllower allow_nested(
- base::MessageLoop::current());
- message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&MenuControllerTest::Step1_RunMenu, base::Unretained(this)));
-}
-
-#if defined(OS_LINUX) && defined(USE_X11)
// Tests that an event targeter which blocks events will be honored by the menu
// event dispatcher.
TEST_F(MenuControllerTest, EventTargeter) {
- {
- // Verify that the menu handles the escape key under normal circumstances.
- scoped_ptr<Widget> owner(CreateOwnerWidget());
- message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&MenuControllerTest::DispatchEscapeAndExpect,
- base::Unretained(this),
- MenuController::EXIT_OUTERMOST));
- RunMenu(owner.get());
- }
-
- {
- // With the NULL targeter instantiated and assigned we expect the menu to
- // not handle the key event.
- scoped_ptr<Widget> owner(CreateOwnerWidget());
- aura::ScopedWindowTargeter scoped_targeter(
- owner->GetNativeWindow()->GetRootWindow(),
- scoped_ptr<ui::EventTargeter>(new ui::NullEventTargeter));
- message_loop()->PostTask(
- FROM_HERE,
- base::Bind(&MenuControllerTest::DispatchEscapeAndExpect,
- base::Unretained(this),
- MenuController::EXIT_NONE));
- RunMenu(owner.get());
- }
+ base::MessageLoopForUI::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MenuControllerTest::TestEventTargeter,
+ base::Unretained(this)));
+ RunMenu();
}
-#endif
#if defined(USE_X11)
-class TestEventHandler : public ui::EventHandler {
- public:
- TestEventHandler() : outstanding_touches_(0) {}
-
- void OnTouchEvent(ui::TouchEvent* event) override {
- switch(event->type()) {
- case ui::ET_TOUCH_PRESSED:
- outstanding_touches_++;
- break;
- case ui::ET_TOUCH_RELEASED:
- case ui::ET_TOUCH_CANCELLED:
- outstanding_touches_--;
- break;
- default:
- break;
- }
- }
-
- int outstanding_touches() const { return outstanding_touches_; }
-
- private:
- int outstanding_touches_;
-};
-
// Tests that touch event ids are released correctly. See
// crbug.com/439051 for details. When the ids aren't managed
// correctly, we get stuck down touches.
TEST_F(MenuControllerTest, TouchIdsReleasedCorrectly) {
- scoped_ptr<Widget> owner(CreateOwnerWidget());
TestEventHandler test_event_handler;
- owner->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler(
+ owner()->GetNativeWindow()->GetRootWindow()->AddPreTargetHandler(
&test_event_handler);
std::vector<int> devices;
devices.push_back(1);
ui::SetUpTouchDevicesForTest(devices);
- DispatchTouch(XI_TouchBegin, 0);
- DispatchTouch(XI_TouchBegin, 1);
- DispatchTouch(XI_TouchEnd, 0);
+ event_generator()->PressTouchId(0);
+ event_generator()->PressTouchId(1);
+ event_generator()->ReleaseTouchId(0);
- message_loop()->PostTask(FROM_HERE,
- base::Bind(&MenuControllerTest::DispatchTouch,
- base::Unretained(this), XI_TouchEnd, 1));
+ base::MessageLoopForUI::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&MenuControllerTest::ReleaseTouchId,
+ base::Unretained(this),
+ 1));
- message_loop()->PostTask(
+ base::MessageLoopForUI::current()->PostTask(
FROM_HERE,
- base::Bind(&MenuControllerTest::DispatchEscapeAndExpect,
- base::Unretained(this), MenuController::EXIT_OUTERMOST));
+ base::Bind(&MenuControllerTest::PressKey,
+ base::Unretained(this),
+ ui::VKEY_ESCAPE));
+
+ RunMenu();
- RunMenu(owner.get());
+ EXPECT_EQ(MenuController::EXIT_OUTERMOST, menu_exit_type());
EXPECT_EQ(0, test_event_handler.outstanding_touches());
- owner->GetNativeWindow()->GetRootWindow()->RemovePreTargetHandler(
+ owner()->GetNativeWindow()->GetRootWindow()->RemovePreTargetHandler(
&test_event_handler);
}
-#endif // defined(USE_X11)
-TEST_F(MenuControllerTest, FirstSelectedItem) {
- scoped_ptr<Widget> owner(CreateOwnerWidget());
- scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView);
- menu_item->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One"));
- menu_item->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two"));
- // Disabling the item "One" gets it skipped when a menu is first opened.
- menu_item->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false);
+#endif // defined(USE_X11)
- SetupMenu(owner.get(), menu_item.get());
+TEST_F(MenuControllerTest, FirstSelectedItem) {
+ // disabling all items but "Two".
+ menu_item()->GetSubmenu()->GetMenuItemAt(0)->SetEnabled(false);
+ menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false);
+ menu_item()->GetSubmenu()->GetMenuItemAt(3)->SetEnabled(false);
- // First selectable item should be item "Two".
- MenuItemView* first_selectable = FindFirstSelectableMenuItem(menu_item.get());
+ // "Two" should be the first selectable item.
+ MenuItemView* first_selectable = FindFirstSelectableMenuItem(menu_item());
EXPECT_EQ(2, first_selectable->GetCommand());
// There should be no next or previous selectable item since there is only a
// single enabled item in the menu.
SetPendingStateItem(first_selectable);
- EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item.get(), 1, 1));
- EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item.get(), 1, -1));
+ EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item(), 1, 1));
+ EXPECT_EQ(nullptr, FindNextSelectableMenuItem(menu_item(), 1, -1));
// Clear references in menu controller to the menu item that is going away.
ResetSelection();
}
TEST_F(MenuControllerTest, NextSelectedItem) {
- scoped_ptr<Widget> owner(CreateOwnerWidget());
- scoped_ptr<TestMenuItemView> menu_item(new TestMenuItemView);
- menu_item->AppendMenuItemWithLabel(1, base::ASCIIToUTF16("One"));
- menu_item->AppendMenuItemWithLabel(2, base::ASCIIToUTF16("Two"));
- menu_item->AppendMenuItemWithLabel(3, base::ASCIIToUTF16("Three"));
- menu_item->AppendMenuItemWithLabel(4, base::ASCIIToUTF16("Four"));
// Disabling the item "Three" gets it skipped when using keyboard to navigate.
- menu_item->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false);
-
- SetupMenu(owner.get(), menu_item.get());
+ menu_item()->GetSubmenu()->GetMenuItemAt(2)->SetEnabled(false);
// Fake initial hot selection.
- SetPendingStateItem(menu_item->GetSubmenu()->GetMenuItemAt(0));
+ SetPendingStateItem(menu_item()->GetSubmenu()->GetMenuItemAt(0));
EXPECT_EQ(1, pending_state_item()->GetCommand());
// Move down in the menu.

Powered by Google App Engine
This is Rietveld 408576698