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 #import <Cocoa/Cocoa.h>
7 #include "base/logging.h"
8 #import "chrome/browser/ui/cocoa/accelerators_cocoa.h"
9 #include "chrome/test/base/in_process_browser_test.h"
10 #include "testing/gtest_mac.h"
11 #include "ui/base/accelerators/platform_accelerator_cocoa.h"
12 #import "ui/events/keycodes/keyboard_code_conversion_mac.h"
14 typedef InProcessBrowserTest AcceleratorsCocoaBrowserTest;
18 // Adds all NSMenuItems with an accelerator to the array.
19 void AddAcceleratorItemsToArray(NSMenu* menu, NSMutableArray* array) {
20 for (NSMenuItem* item in [menu itemArray]) {
21 NSMenu* submenu = item.submenu;
23 AddAcceleratorItemsToArray(submenu, array);
25 if (item.keyEquivalent.length > 0)
26 [array addObject:item];
30 // Returns the NSMenuItem that has the given keyEquivalent and modifiers, or
32 NSMenuItem* MenuContainsAccelerator(NSMenu* menu,
33 NSString* key_equivalent,
34 NSUInteger modifiers) {
35 for (NSMenuItem* item in [menu itemArray]) {
36 NSMenu* submenu = item.submenu;
39 MenuContainsAccelerator(submenu, key_equivalent, modifiers);
44 if ([item.keyEquivalent isEqual:key_equivalent]) {
46 (modifiers == item.keyEquivalentModifierMask) ||
47 ((modifiers & (~NSShiftKeyMask)) == item.keyEquivalentModifierMask);
57 // Checks that each NSMenuItem in the main menu has a corresponding accelerator,
58 // and the keyEquivalent/modifiers match.
59 IN_PROC_BROWSER_TEST_F(AcceleratorsCocoaBrowserTest,
60 MainMenuAcceleratorsInMapping) {
61 NSMenu* menu = [NSApp mainMenu];
62 NSMutableArray* array = [NSMutableArray array];
63 AddAcceleratorItemsToArray(menu, array);
65 for (NSMenuItem* item in array) {
66 NSInteger command_id = item.tag;
67 AcceleratorsCocoa* keymap = AcceleratorsCocoa::GetInstance();
68 const ui::Accelerator* accelerator;
70 // If the tag is zero, then the NSMenuItem must use a custom selector.
71 // Check that the accelerator is present as an un-mapped accelerator.
72 if (command_id == 0) {
73 accelerator = keymap->GetAcceleratorForHotKey(
74 item.keyEquivalent, item.keyEquivalentModifierMask);
76 EXPECT_TRUE(accelerator);
80 // If the tag isn't zero, then it must correspond to an IDC_* command.
81 accelerator = keymap->GetAcceleratorForCommand(command_id);
82 EXPECT_TRUE(accelerator);
86 // Get the Cocoa key_equivalent associated with the accelerator.
87 const ui::PlatformAcceleratorCocoa* platform_accelerator =
88 static_cast<const ui::PlatformAcceleratorCocoa*>(
89 accelerator->platform_accelerator());
90 NSString* key_equivalent = platform_accelerator->characters();
92 // Check that the menu item's keyEquivalent matches the one from the
93 // Cocoa accelerator map.
94 EXPECT_NSEQ(key_equivalent, item.keyEquivalent);
96 // Check that the menu item's modifier mask matches the one stored in the
97 // accelerator. A mask that include NSShiftKeyMask may not include the
98 // relevant bit (the information is reflected in the keyEquivalent of the
100 NSUInteger mask = platform_accelerator->modifier_mask();
102 (mask == item.keyEquivalentModifierMask) ||
103 ((mask & (~NSShiftKeyMask)) == item.keyEquivalentModifierMask);
104 EXPECT_TRUE(maskEqual);
108 // Check that each accelerator with a command_id has an associated NSMenuItem
109 // in the main menu. If the selector is commandDispatch:, then the tag must
110 // match the command_id.
111 IN_PROC_BROWSER_TEST_F(AcceleratorsCocoaBrowserTest,
112 MappingAcceleratorsInMainMenu) {
113 AcceleratorsCocoa* keymap = AcceleratorsCocoa::GetInstance();
114 for (AcceleratorsCocoa::AcceleratorMap::iterator it =
115 keymap->accelerators_.begin();
116 it != keymap->accelerators_.end();
118 const ui::PlatformAcceleratorCocoa* platform_accelerator =
119 static_cast<const ui::PlatformAcceleratorCocoa*>(
120 it->second.platform_accelerator());
122 // Check that there exists a corresponding NSMenuItem.
124 MenuContainsAccelerator([NSApp mainMenu],
125 platform_accelerator->characters(),
126 platform_accelerator->modifier_mask());
129 // If the menu uses a commandDispatch:, the tag must match the command id!
130 if (item.action == @selector(commandDispatch:))
131 EXPECT_EQ(item.tag, it->first);