Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/common/extensions/command.h" | 5 #include "chrome/common/extensions/command.h" |
| 6 | 6 |
| 7 #include "base/memory/scoped_ptr.h" | 7 #include "base/memory/scoped_ptr.h" |
| 8 #include "base/strings/string_number_conversions.h" | 8 #include "base/strings/string_number_conversions.h" |
| 9 #include "base/strings/string_util.h" | 9 #include "base/strings/string_util.h" |
| 10 #include "base/strings/utf_string_conversions.h" | 10 #include "base/strings/utf_string_conversions.h" |
| 11 #include "base/values.h" | 11 #include "base/values.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" | 12 #include "testing/gtest/include/gtest/gtest.h" |
| 13 | 13 |
| 14 class CommandTest : public testing::Test { | 14 class CommandTest : public testing::Test { |
| 15 }; | 15 }; |
| 16 | 16 |
| 17 typedef const struct { | |
| 18 bool expected_result; | |
| 19 ui::Accelerator accelerator; | |
| 20 const char* command_name; | |
| 21 const char* key; | |
| 22 const char* description; | |
| 23 } ConstCommandsTestData; | |
| 24 | |
| 25 void CheckExpectations(ConstCommandsTestData data, | |
| 26 int i, | |
| 27 bool skip_simple_parse, | |
| 28 bool platform_default, | |
| 29 bool windows, | |
| 30 bool mac, | |
| 31 bool chromeos) { | |
| 32 scoped_ptr<base::DictionaryValue> input(new base::DictionaryValue); | |
| 33 extensions::Command command; | |
| 34 base::string16 error; | |
| 35 | |
| 36 // First parse the command as a simple string. | |
| 37 if (!skip_simple_parse) { | |
| 38 input->SetString("suggested_key", data.key); | |
| 39 input->SetString("description", data.description); | |
| 40 | |
| 41 SCOPED_TRACE(std::string("Command name: |") + data.command_name + | |
| 42 "| key: |" + data.key + "| description: |" + data.description + | |
| 43 "| index: " + base::IntToString(i)); | |
| 44 | |
| 45 bool result = command.Parse(input.get(), data.command_name, i, &error); | |
| 46 | |
| 47 EXPECT_EQ(data.expected_result, result); | |
| 48 if (result) { | |
| 49 EXPECT_STREQ(data.description, | |
| 50 base::UTF16ToASCII(command.description()).c_str()); | |
| 51 EXPECT_STREQ(data.command_name, command.command_name().c_str()); | |
| 52 EXPECT_EQ(data.accelerator, command.accelerator()); | |
| 53 } | |
| 54 } | |
| 55 | |
| 56 // Now parse the command as a dictionary of multiple values. | |
| 57 if (data.key[0] != '\0') { | |
| 58 input.reset(new base::DictionaryValue); | |
| 59 base::DictionaryValue* key_dict = new base::DictionaryValue(); | |
| 60 | |
| 61 if (platform_default) | |
| 62 key_dict->SetString("default", data.key); | |
| 63 if (windows) | |
| 64 key_dict->SetString("windows", data.key); | |
| 65 if (mac) | |
| 66 key_dict->SetString("mac", data.key); | |
| 67 if (chromeos) | |
| 68 key_dict->SetString("chromeos", data.key); | |
| 69 | |
| 70 input->Set("suggested_key", key_dict); | |
| 71 input->SetString("description", data.description); | |
| 72 | |
| 73 bool result = command.Parse(input.get(), data.command_name, i, &error); | |
| 74 | |
| 75 EXPECT_EQ(data.expected_result, result); | |
| 76 if (result) { | |
| 77 EXPECT_STREQ(data.description, | |
| 78 base::UTF16ToASCII(command.description()).c_str()); | |
| 79 EXPECT_STREQ(data.command_name, command.command_name().c_str()); | |
| 80 EXPECT_EQ(data.accelerator, command.accelerator()); | |
| 81 } | |
| 82 } | |
| 83 } | |
|
Finnur
2014/08/21 11:18:11
Same here. Version we reviewed before is more up-t
David Tseng
2014/08/21 16:17:56
Acknowledged.
| |
| 84 | |
| 17 TEST(CommandTest, ExtensionCommandParsing) { | 85 TEST(CommandTest, ExtensionCommandParsing) { |
| 18 const ui::Accelerator none = ui::Accelerator(); | 86 const ui::Accelerator none = ui::Accelerator(); |
| 19 const ui::Accelerator shift_f = ui::Accelerator(ui::VKEY_F, | 87 const ui::Accelerator shift_f = ui::Accelerator(ui::VKEY_F, |
| 20 ui::EF_SHIFT_DOWN); | 88 ui::EF_SHIFT_DOWN); |
| 21 #if defined(OS_MACOSX) | 89 #if defined(OS_MACOSX) |
| 22 int ctrl = ui::EF_COMMAND_DOWN; | 90 int ctrl = ui::EF_COMMAND_DOWN; |
| 23 #else | 91 #else |
| 24 int ctrl = ui::EF_CONTROL_DOWN; | 92 int ctrl = ui::EF_CONTROL_DOWN; |
| 25 #endif | 93 #endif |
| 26 | 94 |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 45 const ui::Accelerator ctrl_pgdwn = ui::Accelerator(ui::VKEY_NEXT, ctrl); | 113 const ui::Accelerator ctrl_pgdwn = ui::Accelerator(ui::VKEY_NEXT, ctrl); |
| 46 const ui::Accelerator next_track = | 114 const ui::Accelerator next_track = |
| 47 ui::Accelerator(ui::VKEY_MEDIA_NEXT_TRACK, ui::EF_NONE); | 115 ui::Accelerator(ui::VKEY_MEDIA_NEXT_TRACK, ui::EF_NONE); |
| 48 const ui::Accelerator prev_track = | 116 const ui::Accelerator prev_track = |
| 49 ui::Accelerator(ui::VKEY_MEDIA_PREV_TRACK, ui::EF_NONE); | 117 ui::Accelerator(ui::VKEY_MEDIA_PREV_TRACK, ui::EF_NONE); |
| 50 const ui::Accelerator play_pause = | 118 const ui::Accelerator play_pause = |
| 51 ui::Accelerator(ui::VKEY_MEDIA_PLAY_PAUSE, ui::EF_NONE); | 119 ui::Accelerator(ui::VKEY_MEDIA_PLAY_PAUSE, ui::EF_NONE); |
| 52 const ui::Accelerator stop = | 120 const ui::Accelerator stop = |
| 53 ui::Accelerator(ui::VKEY_MEDIA_STOP, ui::EF_NONE); | 121 ui::Accelerator(ui::VKEY_MEDIA_STOP, ui::EF_NONE); |
| 54 | 122 |
| 55 const struct { | 123 ConstCommandsTestData kTests[] = { |
| 56 bool expected_result; | 124 // Negative test (one or more missing required fields). We don't need to |
| 57 ui::Accelerator accelerator; | 125 // test |command_name| being blank as it is used as a key in the manifest, |
| 58 const char* command_name; | 126 // so it can't be blank (and we CHECK() when it is). A blank shortcut is |
| 59 const char* key; | 127 // permitted. |
| 60 const char* description; | 128 {false, none, "command", "", ""}, |
| 61 } kTests[] = { | 129 {false, none, "command", "Ctrl+f", ""}, |
| 62 // Negative test (one or more missing required fields). We don't need to | 130 // Ctrl+Alt is not permitted, see MSDN link in comments in Parse function. |
| 63 // test |command_name| being blank as it is used as a key in the manifest, | 131 {false, none, "command", "Ctrl+Alt+F", "description"}, |
| 64 // so it can't be blank (and we CHECK() when it is). A blank shortcut is | 132 // Unsupported shortcuts/too many, or missing modifier. |
| 65 // permitted. | 133 {false, none, "command", "A", "description"}, |
| 66 { false, none, "command", "", "" }, | 134 {false, none, "command", "F10", "description"}, |
| 67 { false, none, "command", "Ctrl+f", "" }, | 135 {false, none, "command", "Ctrl+F+G", "description"}, |
| 68 // Ctrl+Alt is not permitted, see MSDN link in comments in Parse function. | 136 {false, none, "command", "Ctrl+Alt+Shift+G", "description"}, |
| 69 { false, none, "command", "Ctrl+Alt+F", "description" }, | 137 // Shift on its own is not supported. |
| 70 // Unsupported shortcuts/too many, or missing modifier. | 138 {false, shift_f, "command", "Shift+F", "description"}, |
| 71 { false, none, "command", "A", "description" }, | 139 {false, shift_f, "command", "F+Shift", "description"}, |
| 72 { false, none, "command", "F10", "description" }, | 140 // Basic tests. |
| 73 { false, none, "command", "Ctrl+F+G", "description" }, | 141 {true, none, "command", "", "description"}, |
| 74 { false, none, "command", "Ctrl+Alt+Shift+G", "description" }, | 142 {true, ctrl_f, "command", "Ctrl+F", "description"}, |
| 75 // Shift on its own is not supported. | 143 {true, alt_f, "command", "Alt+F", "description"}, |
| 76 { false, shift_f, "command", "Shift+F", "description" }, | 144 {true, ctrl_shift_f, "command", "Ctrl+Shift+F", "description"}, |
| 77 { false, shift_f, "command", "F+Shift", "description" }, | 145 {true, alt_shift_f, "command", "Alt+Shift+F", "description"}, |
| 78 // Basic tests. | 146 {true, ctrl_1, "command", "Ctrl+1", "description"}, |
| 79 { true, none, "command", "", "description" }, | 147 // Shortcut token order tests. |
| 80 { true, ctrl_f, "command", "Ctrl+F", "description" }, | 148 {true, ctrl_f, "command", "F+Ctrl", "description"}, |
| 81 { true, alt_f, "command", "Alt+F", "description" }, | 149 {true, alt_f, "command", "F+Alt", "description"}, |
| 82 { true, ctrl_shift_f, "command", "Ctrl+Shift+F", "description" }, | 150 {true, ctrl_shift_f, "command", "F+Ctrl+Shift", "description"}, |
| 83 { true, alt_shift_f, "command", "Alt+Shift+F", "description" }, | 151 {true, ctrl_shift_f, "command", "F+Shift+Ctrl", "description"}, |
| 84 { true, ctrl_1, "command", "Ctrl+1", "description" }, | 152 {true, alt_shift_f, "command", "F+Alt+Shift", "description"}, |
| 85 // Shortcut token order tests. | 153 {true, alt_shift_f, "command", "F+Shift+Alt", "description"}, |
| 86 { true, ctrl_f, "command", "F+Ctrl", "description" }, | 154 // Case insensitivity is not OK. |
| 87 { true, alt_f, "command", "F+Alt", "description" }, | 155 {false, ctrl_f, "command", "Ctrl+f", "description"}, |
| 88 { true, ctrl_shift_f, "command", "F+Ctrl+Shift", "description" }, | 156 {false, ctrl_f, "command", "cTrL+F", "description"}, |
| 89 { true, ctrl_shift_f, "command", "F+Shift+Ctrl", "description" }, | 157 // Skipping description is OK for browser- and pageActions. |
| 90 { true, alt_shift_f, "command", "F+Alt+Shift", "description" }, | 158 {true, ctrl_f, "_execute_browser_action", "Ctrl+F", ""}, |
| 91 { true, alt_shift_f, "command", "F+Shift+Alt", "description" }, | 159 {true, ctrl_f, "_execute_page_action", "Ctrl+F", ""}, |
| 92 // Case insensitivity is not OK. | 160 // Home, End, Arrow keys, etc. |
| 93 { false, ctrl_f, "command", "Ctrl+f", "description" }, | 161 {true, ctrl_comma, "_execute_browser_action", "Ctrl+Comma", ""}, |
| 94 { false, ctrl_f, "command", "cTrL+F", "description" }, | 162 {true, ctrl_dot, "_execute_browser_action", "Ctrl+Period", ""}, |
| 95 // Skipping description is OK for browser- and pageActions. | 163 {true, ctrl_left, "_execute_browser_action", "Ctrl+Left", ""}, |
| 96 { true, ctrl_f, "_execute_browser_action", "Ctrl+F", "" }, | 164 {true, ctrl_right, "_execute_browser_action", "Ctrl+Right", ""}, |
| 97 { true, ctrl_f, "_execute_page_action", "Ctrl+F", "" }, | 165 {true, ctrl_up, "_execute_browser_action", "Ctrl+Up", ""}, |
| 98 // Home, End, Arrow keys, etc. | 166 {true, ctrl_down, "_execute_browser_action", "Ctrl+Down", ""}, |
| 99 { true, ctrl_comma, "_execute_browser_action", "Ctrl+Comma", "" }, | 167 {true, ctrl_ins, "_execute_browser_action", "Ctrl+Insert", ""}, |
| 100 { true, ctrl_dot, "_execute_browser_action", "Ctrl+Period", "" }, | 168 {true, ctrl_del, "_execute_browser_action", "Ctrl+Delete", ""}, |
| 101 { true, ctrl_left, "_execute_browser_action", "Ctrl+Left", "" }, | 169 {true, ctrl_home, "_execute_browser_action", "Ctrl+Home", ""}, |
| 102 { true, ctrl_right, "_execute_browser_action", "Ctrl+Right", "" }, | 170 {true, ctrl_end, "_execute_browser_action", "Ctrl+End", ""}, |
| 103 { true, ctrl_up, "_execute_browser_action", "Ctrl+Up", "" }, | 171 {true, ctrl_pgup, "_execute_browser_action", "Ctrl+PageUp", ""}, |
| 104 { true, ctrl_down, "_execute_browser_action", "Ctrl+Down", "" }, | 172 {true, ctrl_pgdwn, "_execute_browser_action", "Ctrl+PageDown", ""}, |
| 105 { true, ctrl_ins, "_execute_browser_action", "Ctrl+Insert", "" }, | 173 // Media keys. |
| 106 { true, ctrl_del, "_execute_browser_action", "Ctrl+Delete", "" }, | 174 {true, next_track, "command", "MediaNextTrack", "description"}, |
| 107 { true, ctrl_home, "_execute_browser_action", "Ctrl+Home", "" }, | 175 {true, play_pause, "command", "MediaPlayPause", "description"}, |
| 108 { true, ctrl_end, "_execute_browser_action", "Ctrl+End", "" }, | 176 {true, prev_track, "command", "MediaPrevTrack", "description"}, |
| 109 { true, ctrl_pgup, "_execute_browser_action", "Ctrl+PageUp", "" }, | 177 {true, stop, "command", "MediaStop", "description"}, |
| 110 { true, ctrl_pgdwn, "_execute_browser_action", "Ctrl+PageDown", "" }, | 178 {false, none, "_execute_browser_action", "MediaNextTrack", ""}, |
| 111 // Media keys. | 179 {false, none, "_execute_page_action", "MediaPrevTrack", ""}, |
| 112 { true, next_track, "command", "MediaNextTrack", "description" }, | 180 {false, none, "command", "Ctrl+Shift+MediaPrevTrack", "description"}, |
| 113 { true, play_pause, "command", "MediaPlayPause", "description" }, | |
| 114 { true, prev_track, "command", "MediaPrevTrack", "description" }, | |
| 115 { true, stop, "command", "MediaStop", "description" }, | |
| 116 { false, none, "_execute_browser_action", "MediaNextTrack", "" }, | |
| 117 { false, none, "_execute_page_action", "MediaPrevTrack", "" }, | |
| 118 { false, none, "command", "Ctrl+Shift+MediaPrevTrack", "description" }, | |
| 119 }; | 181 }; |
| 120 | 182 |
| 121 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) { | 183 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) { |
| 122 // First parse the command as a simple string. | 184 CheckExpectations(kTests[i], false, i, true, true, true, true); |
| 123 scoped_ptr<base::DictionaryValue> input(new base::DictionaryValue); | |
| 124 input->SetString("suggested_key", kTests[i].key); | |
| 125 input->SetString("description", kTests[i].description); | |
| 126 | |
| 127 SCOPED_TRACE(std::string("Command name: |") + kTests[i].command_name + | |
| 128 "| key: |" + kTests[i].key + | |
| 129 "| description: |" + kTests[i].description + | |
| 130 "| index: " + base::IntToString(i)); | |
| 131 | |
| 132 extensions::Command command; | |
| 133 base::string16 error; | |
| 134 bool result = | |
| 135 command.Parse(input.get(), kTests[i].command_name, i, &error); | |
| 136 | |
| 137 EXPECT_EQ(kTests[i].expected_result, result); | |
| 138 if (result) { | |
| 139 EXPECT_STREQ(kTests[i].description, | |
| 140 base::UTF16ToASCII(command.description()).c_str()); | |
| 141 EXPECT_STREQ(kTests[i].command_name, command.command_name().c_str()); | |
| 142 EXPECT_EQ(kTests[i].accelerator, command.accelerator()); | |
| 143 } | |
| 144 | |
| 145 // Now parse the command as a dictionary of multiple values. | |
| 146 if (kTests[i].key[0] != '\0') { | |
| 147 input.reset(new base::DictionaryValue); | |
| 148 base::DictionaryValue* key_dict = new base::DictionaryValue(); | |
| 149 key_dict->SetString("default", kTests[i].key); | |
| 150 key_dict->SetString("windows", kTests[i].key); | |
| 151 key_dict->SetString("mac", kTests[i].key); | |
| 152 input->Set("suggested_key", key_dict); | |
| 153 input->SetString("description", kTests[i].description); | |
| 154 | |
| 155 result = command.Parse(input.get(), kTests[i].command_name, i, &error); | |
| 156 | |
| 157 EXPECT_EQ(kTests[i].expected_result, result); | |
| 158 if (result) { | |
| 159 EXPECT_STREQ(kTests[i].description, | |
| 160 base::UTF16ToASCII(command.description()).c_str()); | |
| 161 EXPECT_STREQ(kTests[i].command_name, command.command_name().c_str()); | |
| 162 EXPECT_EQ(kTests[i].accelerator, command.accelerator()); | |
| 163 } | |
| 164 } | |
| 165 } | 185 } |
| 166 } | 186 } |
| 167 | 187 |
| 168 TEST(CommandTest, ExtensionCommandParsingFallback) { | 188 TEST(CommandTest, ExtensionCommandParsingFallback) { |
| 169 std::string description = "desc"; | 189 std::string description = "desc"; |
| 170 std::string command_name = "foo"; | 190 std::string command_name = "foo"; |
| 171 | 191 |
| 172 // Test that platform specific keys are honored on each platform, despite | 192 // Test that platform specific keys are honored on each platform, despite |
| 173 // fallback being given. | 193 // fallback being given. |
| 174 scoped_ptr<base::DictionaryValue> input(new base::DictionaryValue); | 194 scoped_ptr<base::DictionaryValue> input(new base::DictionaryValue); |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 241 key_dict->SetString("windows", "Ctrl+Shift+W"); | 261 key_dict->SetString("windows", "Ctrl+Shift+W"); |
| 242 #endif | 262 #endif |
| 243 EXPECT_FALSE(command.Parse(input.get(), command_name, 0, &error)); | 263 EXPECT_FALSE(command.Parse(input.get(), command_name, 0, &error)); |
| 244 | 264 |
| 245 // Make sure Mac specific keys are not processed on other platforms. | 265 // Make sure Mac specific keys are not processed on other platforms. |
| 246 #if !defined(OS_MACOSX) | 266 #if !defined(OS_MACOSX) |
| 247 key_dict->SetString("windows", "Command+Shift+M"); | 267 key_dict->SetString("windows", "Command+Shift+M"); |
| 248 EXPECT_FALSE(command.Parse(input.get(), command_name, 0, &error)); | 268 EXPECT_FALSE(command.Parse(input.get(), command_name, 0, &error)); |
| 249 #endif | 269 #endif |
| 250 } | 270 } |
| 271 | |
| 272 TEST(CommandTest, ExtensionCommandParsingPlatformSpecific) { | |
| 273 ui::Accelerator search_shift_z(ui::VKEY_Z, | |
| 274 ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN); | |
| 275 | |
| 276 ConstCommandsTestData kChromeOsTests[] = { | |
| 277 {true, search_shift_z, "command", "Search+Shift+Z", "description"}, | |
| 278 {false, search_shift_z, "command", "Command+Shift+Z", "description"}, | |
| 279 }; | |
| 280 | |
| 281 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kChromeOsTests); ++i) { | |
| 282 // Search is only valid on Chrome OS and default platforms. | |
| 283 CheckExpectations(kChromeOsTests[i], i, false, true, false, false, true); | |
| 284 } | |
| 285 | |
| 286 ConstCommandsTestData kNonChromeOsSearchTests[] = { | |
| 287 {false, search_shift_z, "command", "Search+Shift+Z", "description"}, | |
| 288 }; | |
| 289 | |
| 290 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kNonChromeOsSearchTests); ++i) { | |
| 291 // Make sure dictionary parsing fails on Windows and Mac. | |
| 292 CheckExpectations( | |
| 293 kNonChromeOsSearchTests[i], i, true, false, true, true, false); | |
| 294 } | |
| 295 } | |
| OLD | NEW |