| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <set> |
| #include <tuple> |
| |
| #include "ash/accelerators/accelerator_table.h" |
| #include "base/macros.h" |
| #include "base/md5.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // The number of non-Search-based accelerators. |
| constexpr int kNonSearchAcceleratorsNum = 88; |
| // The hash of non-Search-based accelerators. See HashAcceleratorData(). |
| constexpr char kNonSearchAcceleratorsHash[] = |
| "66028d49a13708e1c1d8c5791d37a8e4"; |
| |
| struct Cmp { |
| bool operator()(const AcceleratorData& lhs, |
| const AcceleratorData& rhs) const { |
| // Do not check |action|. |
| return std::tie(lhs.trigger_on_press, lhs.keycode, lhs.modifiers) < |
| std::tie(rhs.trigger_on_press, rhs.keycode, rhs.modifiers); |
| } |
| }; |
| |
| std::string AcceleratorDataToString(const AcceleratorData& accelerator) { |
| return base::StringPrintf( |
| "trigger_on_press=%s keycode=%d shift=%s control=%s alt=%s search=%s " |
| "action=%d", |
| accelerator.trigger_on_press ? "true" : "false", accelerator.keycode, |
| (accelerator.modifiers & ui::EF_SHIFT_DOWN) ? "true" : "false", |
| (accelerator.modifiers & ui::EF_CONTROL_DOWN) ? "true" : "false", |
| (accelerator.modifiers & ui::EF_ALT_DOWN) ? "true" : "false", |
| (accelerator.modifiers & ui::EF_COMMAND_DOWN) ? "true" : "false", |
| accelerator.action); |
| } |
| |
| std::string HashAcceleratorData( |
| const std::vector<AcceleratorData> accelerators) { |
| base::MD5Context context; |
| base::MD5Init(&context); |
| for (const AcceleratorData& accelerator : accelerators) |
| base::MD5Update(&context, AcceleratorDataToString(accelerator)); |
| |
| base::MD5Digest digest; |
| base::MD5Final(&digest, &context); |
| return MD5DigestToBase16(digest); |
| } |
| |
| } // namespace |
| |
| TEST(AcceleratorTableTest, CheckDuplicatedAccelerators) { |
| std::set<AcceleratorData, Cmp> accelerators; |
| for (size_t i = 0; i < kAcceleratorDataLength; ++i) { |
| const AcceleratorData& entry = kAcceleratorData[i]; |
| EXPECT_TRUE(accelerators.insert(entry).second) |
| << "Duplicated accelerator: " << AcceleratorDataToString(entry); |
| } |
| } |
| |
| TEST(AcceleratorTableTest, CheckDuplicatedReservedActions) { |
| std::set<AcceleratorAction> actions; |
| for (size_t i = 0; i < kReservedActionsLength; ++i) { |
| EXPECT_TRUE(actions.insert(kReservedActions[i]).second) |
| << "Duplicated action: " << kReservedActions[i]; |
| } |
| } |
| |
| TEST(AcceleratorTableTest, CheckDuplicatedActionsAllowedAtLoginOrLockScreen) { |
| std::set<AcceleratorAction> actions; |
| for (size_t i = 0; i < kActionsAllowedAtLoginOrLockScreenLength; ++i) { |
| EXPECT_TRUE(actions.insert(kActionsAllowedAtLoginOrLockScreen[i]).second) |
| << "Duplicated action: " << kActionsAllowedAtLoginOrLockScreen[i]; |
| } |
| for (size_t i = 0; i < kActionsAllowedAtLockScreenLength; ++i) { |
| EXPECT_TRUE(actions.insert(kActionsAllowedAtLockScreen[i]).second) |
| << "Duplicated action: " << kActionsAllowedAtLockScreen[i]; |
| } |
| } |
| |
| TEST(AcceleratorTableTest, CheckDuplicatedActionsAllowedAtPowerMenu) { |
| std::set<AcceleratorAction> actions; |
| for (size_t i = 0; i < kActionsAllowedAtPowerMenuLength; ++i) { |
| EXPECT_TRUE(actions.insert(kActionsAllowedAtPowerMenu[i]).second) |
| << "Duplicated action: " << kActionsAllowedAtPowerMenu[i]; |
| } |
| } |
| |
| TEST(AcceleratorTableTest, CheckDuplicatedActionsAllowedAtModalWindow) { |
| std::set<AcceleratorAction> actions; |
| for (size_t i = 0; i < kActionsAllowedAtModalWindowLength; ++i) { |
| EXPECT_TRUE(actions.insert(kActionsAllowedAtModalWindow[i]).second) |
| << "Duplicated action: " << kActionsAllowedAtModalWindow[i] |
| << " at index: " << i; |
| } |
| } |
| |
| TEST(AcceleratorTableTest, CheckDuplicatedRepeatableActions) { |
| std::set<AcceleratorAction> actions; |
| for (size_t i = 0; i < kRepeatableActionsLength; ++i) { |
| EXPECT_TRUE(actions.insert(kRepeatableActions[i]).second) |
| << "Duplicated action: " << kRepeatableActions[i] << " at index: " << i; |
| } |
| } |
| |
| TEST(AcceleratorTableTest, CheckDeprecatedAccelerators) { |
| std::set<AcceleratorData, Cmp> deprecated_actions; |
| for (size_t i = 0; i < kDeprecatedAcceleratorsLength; ++i) { |
| // A deprecated action can never appear twice in the list. |
| const AcceleratorData& entry = kDeprecatedAccelerators[i]; |
| EXPECT_TRUE(deprecated_actions.insert(entry).second) |
| << "Duplicate deprecated accelerator: " |
| << AcceleratorDataToString(entry); |
| } |
| |
| std::set<AcceleratorAction> actions; |
| for (size_t i = 0; i < kDeprecatedAcceleratorsDataLength; ++i) { |
| // There must never be any duplicated actions. |
| const DeprecatedAcceleratorData& data = kDeprecatedAcceleratorsData[i]; |
| EXPECT_TRUE(actions.insert(data.action).second) << "Deprecated action: " |
| << data.action; |
| |
| // The UMA histogram name must be of the format "Ash.Accelerators.*" |
| std::string uma_histogram(data.uma_histogram_name); |
| EXPECT_TRUE(base::StartsWith(uma_histogram, "Ash.Accelerators.", |
| base::CompareCase::SENSITIVE)); |
| } |
| } |
| |
| // All new accelerators should be Search-based and approved by UX. |
| TEST(AcceleratorTableTest, CheckSearchBasedAccelerators) { |
| std::vector<AcceleratorData> non_search_accelerators; |
| for (size_t i = 0; i < kAcceleratorDataLength; ++i) { |
| const AcceleratorData& entry = kAcceleratorData[i]; |
| if (entry.modifiers & ui::EF_COMMAND_DOWN) |
| continue; |
| non_search_accelerators.emplace_back(entry); |
| } |
| |
| const int accelerators_number = non_search_accelerators.size(); |
| EXPECT_EQ(accelerators_number, kNonSearchAcceleratorsNum) |
| << "All new accelerators should be Search-based and approved by UX."; |
| |
| std::stable_sort(non_search_accelerators.begin(), |
| non_search_accelerators.end(), Cmp()); |
| const std::string non_search_accelerators_hash = |
| HashAcceleratorData(non_search_accelerators); |
| |
| EXPECT_EQ(non_search_accelerators_hash, kNonSearchAcceleratorsHash) |
| << "New accelerators must use the Search key. Please talk to the UX " |
| "team.\n" |
| "If you are removing a non-Search-based accelerator, please update " |
| "the date along with the following values\n" |
| << "kNonSearchAcceleratorsNum=" << accelerators_number << " and " |
| << "kNonSearchAcceleratorsHash=\"" << non_search_accelerators_hash |
| << "\""; |
| } |
| |
| } // namespace ash |