clipboard_history_unittest.cc 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. // Copyright 2020 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.
  4. #include "ash/clipboard/clipboard_history.h"
  5. #include <list>
  6. #include <memory>
  7. #include <unordered_map>
  8. #include "ash/clipboard/clipboard_history_controller_impl.h"
  9. #include "ash/clipboard/clipboard_history_item.h"
  10. #include "ash/clipboard/clipboard_history_util.h"
  11. #include "ash/clipboard/scoped_clipboard_history_pause_impl.h"
  12. #include "ash/shell.h"
  13. #include "ash/test/ash_test_base.h"
  14. #include "base/pickle.h"
  15. #include "base/strings/utf_string_conversions.h"
  16. #include "base/test/metrics/histogram_tester.h"
  17. #include "base/test/repeating_test_future.h"
  18. #include "third_party/skia/include/core/SkBitmap.h"
  19. #include "ui/base/clipboard/clipboard.h"
  20. #include "ui/base/clipboard/clipboard_buffer.h"
  21. #include "ui/base/clipboard/custom_data_helper.h"
  22. #include "ui/base/clipboard/scoped_clipboard_writer.h"
  23. #include "ui/events/event_constants.h"
  24. #include "ui/events/test/event_generator.h"
  25. #include "ui/gfx/image/image_unittest_util.h"
  26. #include "ui/gfx/skia_util.h"
  27. namespace ash {
  28. class ClipboardHistoryTest : public AshTestBase {
  29. public:
  30. ClipboardHistoryTest() = default;
  31. ClipboardHistoryTest(const ClipboardHistoryTest&) = delete;
  32. ClipboardHistoryTest& operator=(const ClipboardHistoryTest&) = delete;
  33. ~ClipboardHistoryTest() override = default;
  34. // AshTestBase:
  35. void SetUp() override {
  36. AshTestBase::SetUp();
  37. clipboard_history_ = const_cast<ClipboardHistory*>(
  38. Shell::Get()->clipboard_history_controller()->history());
  39. event_generator_ = std::make_unique<ui::test::EventGenerator>(
  40. ash::Shell::GetPrimaryRootWindow());
  41. }
  42. const std::list<ClipboardHistoryItem>& GetClipboardHistoryItems() {
  43. return clipboard_history_->GetItems();
  44. }
  45. ui::test::EventGenerator* GetEventGenerator() {
  46. return event_generator_.get();
  47. }
  48. // Writes |input_strings| to the clipboard buffer and ensures that
  49. // |expected_strings| are retained in history. If |in_same_sequence| is true,
  50. // writes to the buffer will be performed in the same task sequence.
  51. void WriteAndEnsureTextHistory(
  52. const std::vector<std::u16string>& input_strings,
  53. const std::vector<std::u16string>& expected_strings,
  54. bool in_same_sequence = false) {
  55. for (const auto& input_string : input_strings) {
  56. {
  57. ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
  58. scw.WriteText(input_string);
  59. }
  60. if (!in_same_sequence)
  61. base::RunLoop().RunUntilIdle();
  62. }
  63. if (in_same_sequence)
  64. base::RunLoop().RunUntilIdle();
  65. EnsureTextHistory(expected_strings);
  66. }
  67. void EnsureTextHistory(const std::vector<std::u16string>& expected_strings) {
  68. const std::list<ClipboardHistoryItem>& items = GetClipboardHistoryItems();
  69. EXPECT_EQ(expected_strings.size(), items.size());
  70. int expected_strings_index = 0;
  71. for (const auto& item : items) {
  72. EXPECT_EQ(expected_strings[expected_strings_index++],
  73. base::UTF8ToUTF16(item.data().text()));
  74. }
  75. }
  76. // Writes |input_bitmaps| to the clipboard buffer and ensures that
  77. // |expected_bitmaps| are retained in history. Writes to the buffer are
  78. // performed in different task sequences to simulate real world behavior.
  79. void WriteAndEnsureBitmapHistory(std::vector<SkBitmap>& input_bitmaps,
  80. std::vector<SkBitmap>& expected_bitmaps) {
  81. for (const auto& input_bitmap : input_bitmaps) {
  82. {
  83. ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
  84. scw.WriteImage(input_bitmap);
  85. }
  86. base::RunLoop().RunUntilIdle();
  87. }
  88. const std::list<ClipboardHistoryItem>& items = GetClipboardHistoryItems();
  89. EXPECT_EQ(expected_bitmaps.size(), items.size());
  90. int expected_bitmaps_index = 0;
  91. for (const auto& item : items) {
  92. // The PNG should not have yet been encoded.
  93. const auto& maybe_png = item.data().maybe_png();
  94. EXPECT_FALSE(maybe_png.has_value());
  95. auto maybe_bitmap = item.data().GetBitmapIfPngNotEncoded();
  96. EXPECT_TRUE(maybe_bitmap.has_value());
  97. EXPECT_TRUE(gfx::BitmapsAreEqual(
  98. expected_bitmaps[expected_bitmaps_index++], maybe_bitmap.value()));
  99. }
  100. }
  101. // Writes |input_data| to the clipboard buffer and ensures that
  102. // |expected_data| is retained in history. After writing to the buffer, the
  103. // current task sequence is run to idle to simulate real world behavior.
  104. void WriteAndEnsureCustomDataHistory(
  105. const std::unordered_map<std::u16string, std::u16string>& input_data,
  106. const std::unordered_map<std::u16string, std::u16string>& expected_data) {
  107. base::Pickle input_data_pickle;
  108. ui::WriteCustomDataToPickle(input_data, &input_data_pickle);
  109. {
  110. ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
  111. scw.WritePickledData(input_data_pickle,
  112. ui::ClipboardFormatType::WebCustomDataType());
  113. }
  114. base::RunLoop().RunUntilIdle();
  115. const std::list<ClipboardHistoryItem> items = GetClipboardHistoryItems();
  116. EXPECT_EQ(expected_data.empty() ? 0u : 1u, items.size());
  117. if (expected_data.empty())
  118. return;
  119. std::unordered_map<std::u16string, std::u16string> actual_data;
  120. ui::ReadCustomDataIntoMap(items.front().data().custom_data_data().c_str(),
  121. items.front().data().custom_data_data().size(),
  122. &actual_data);
  123. EXPECT_EQ(expected_data, actual_data);
  124. }
  125. ClipboardHistory* clipboard_history() { return clipboard_history_; }
  126. private:
  127. std::unique_ptr<ui::test::EventGenerator> event_generator_;
  128. // Owned by ClipboardHistoryControllerImpl.
  129. ClipboardHistory* clipboard_history_ = nullptr;
  130. };
  131. // Tests that with nothing copied, nothing is shown.
  132. TEST_F(ClipboardHistoryTest, NothingCopiedNothingSaved) {
  133. // When nothing is copied, nothing should be saved.
  134. WriteAndEnsureTextHistory(/*input_strings=*/{},
  135. /*expected_strings=*/{});
  136. }
  137. // Tests that if one thing is copied, one thing is saved.
  138. TEST_F(ClipboardHistoryTest, OneThingCopiedOneThingSaved) {
  139. std::vector<std::u16string> input_strings{u"test"};
  140. std::vector<std::u16string> expected_strings = input_strings;
  141. // Test that only one string is in history.
  142. WriteAndEnsureTextHistory(input_strings, expected_strings);
  143. }
  144. // Tests that if the same (non bitmap) thing is copied, only one of the
  145. // duplicates is in the list.
  146. TEST_F(ClipboardHistoryTest, DuplicateBasic) {
  147. std::vector<std::u16string> input_strings{u"test", u"test"};
  148. std::vector<std::u16string> expected_strings{u"test"};
  149. // Test that both things are saved.
  150. WriteAndEnsureTextHistory(input_strings, expected_strings);
  151. }
  152. // Tests that if multiple things are copied in the same task sequence, only the
  153. // most recent thing is saved.
  154. TEST_F(ClipboardHistoryTest, InSameSequenceBasic) {
  155. std::vector<std::u16string> input_strings{u"test1", u"test2", u"test3"};
  156. // Because |input_strings| will be copied in the same task sequence, history
  157. // should only retain the most recent thing.
  158. std::vector<std::u16string> expected_strings{u"test3"};
  159. // Test that only the most recent thing is saved.
  160. WriteAndEnsureTextHistory(input_strings, expected_strings,
  161. /*in_same_sequence=*/true);
  162. }
  163. // Tests the ordering of history is in reverse chronological order.
  164. TEST_F(ClipboardHistoryTest, HistoryIsReverseChronological) {
  165. std::vector<std::u16string> input_strings{u"test1", u"test2", u"test3",
  166. u"test4"};
  167. std::vector<std::u16string> expected_strings = input_strings;
  168. // Reverse the vector, history should match this ordering.
  169. std::reverse(std::begin(expected_strings), std::end(expected_strings));
  170. WriteAndEnsureTextHistory(input_strings, expected_strings);
  171. }
  172. // Tests that when a duplicate is copied, the existing duplicate item moves up
  173. // to the front of the clipboard history.
  174. TEST_F(ClipboardHistoryTest, DuplicatePrecedesPreviousRecord) {
  175. // Input holds a unique string sandwiched by a copy.
  176. std::vector<std::u16string> input_strings{u"test1", u"test2", u"test1",
  177. u"test3"};
  178. // The result should be a reversal of the copied elements. When a duplicate
  179. // is copied, history will have that item moved to the front instead of adding
  180. // a new item.
  181. std::vector<std::u16string> expected_strings{u"test3", u"test1", u"test2"};
  182. WriteAndEnsureTextHistory(input_strings, expected_strings);
  183. }
  184. // Tests that nothing is saved after history is cleared.
  185. TEST_F(ClipboardHistoryTest, ClearHistoryBasic) {
  186. // Input holds a unique string sandwhiched by a copy.
  187. std::vector<std::u16string> input_strings{u"test1", u"test2", u"test1"};
  188. // The result should be a reversal of the last two elements. When a duplicate
  189. // is copied, history will show the most recent version of that duplicate.
  190. std::vector<std::u16string> expected_strings{};
  191. for (const auto& input_string : input_strings) {
  192. ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
  193. scw.WriteText(input_string);
  194. }
  195. clipboard_history()->Clear();
  196. EnsureTextHistory(expected_strings);
  197. }
  198. // Tests that there is no crash when an empty clipboard is cleared with empty
  199. // clipboard history.
  200. TEST_F(ClipboardHistoryTest, ClearHistoryFromClipboardNoHistory) {
  201. ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
  202. }
  203. // Tests that clipboard history is cleared when the clipboard is cleared.
  204. TEST_F(ClipboardHistoryTest, ClearHistoryFromClipboardWithHistory) {
  205. std::vector<std::u16string> input_strings{u"test1", u"test2"};
  206. std::vector<std::u16string> expected_strings_before_clear{u"test2", u"test1"};
  207. std::vector<std::u16string> expected_strings_after_clear{};
  208. for (const auto& input_string : input_strings) {
  209. {
  210. ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
  211. scw.WriteText(input_string);
  212. }
  213. base::RunLoop().RunUntilIdle();
  214. }
  215. EnsureTextHistory(expected_strings_before_clear);
  216. ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
  217. EnsureTextHistory(expected_strings_after_clear);
  218. }
  219. // Tests that the limit of clipboard history is respected.
  220. TEST_F(ClipboardHistoryTest, HistoryLimit) {
  221. std::vector<std::u16string> input_strings{u"test1", u"test2", u"test3",
  222. u"test4", u"test5", u"test6"};
  223. // The result should be a reversal of the last five elements.
  224. std::vector<std::u16string> expected_strings{input_strings.begin() + 1,
  225. input_strings.end()};
  226. std::reverse(expected_strings.begin(), expected_strings.end());
  227. WriteAndEnsureTextHistory(input_strings, expected_strings);
  228. }
  229. // Tests that pausing clipboard history results in no history collected.
  230. TEST_F(ClipboardHistoryTest, PauseHistoryBasic) {
  231. std::vector<std::u16string> input_strings{u"test1", u"test2", u"test1"};
  232. // Because history is paused, there should be nothing stored.
  233. std::vector<std::u16string> expected_strings{};
  234. ScopedClipboardHistoryPauseImpl scoped_pause(clipboard_history());
  235. WriteAndEnsureTextHistory(input_strings, expected_strings);
  236. }
  237. // Tests that pausing clipboard history with the `kAllowReorderOnPaste` pause
  238. // behavior allows clipboard history to be modified, but clipboard data changes
  239. // received during the pause are not recorded as copy operations.
  240. TEST_F(ClipboardHistoryTest, PauseHistoryAllowReorders) {
  241. std::vector<std::u16string> input_strings{u"test1", u"test2"};
  242. std::vector<std::u16string> input_string1{u"test1"};
  243. std::vector<std::u16string> expected_strings_initial{u"test2", u"test1"};
  244. std::vector<std::u16string> expected_strings_reordered = input_strings;
  245. // Populate clipboard history to simulate paste-based reorders.
  246. WriteAndEnsureTextHistory(input_strings, expected_strings_initial);
  247. base::HistogramTester histogram_tester;
  248. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
  249. ScopedClipboardHistoryPauseImpl scoped_pause(
  250. clipboard_history(),
  251. ClipboardHistoryUtil::PauseBehavior::kAllowReorderOnPaste);
  252. WriteAndEnsureTextHistory(input_string1, expected_strings_reordered);
  253. // Clipboard history modifications made during a reorder-on-paste operation
  254. // should not count as copies (or pastes). A reorder is not a user action.
  255. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
  256. }
  257. // Tests that when already-paused clipboard history is paused again with a
  258. // different behavior, the newest behavior overrides all others for the duration
  259. // of the pause's lifetime.
  260. TEST_F(ClipboardHistoryTest, PauseHistoryNested) {
  261. std::vector<std::u16string> input_strings{u"test1", u"test2"};
  262. std::vector<std::u16string> input_string1{u"test1"};
  263. std::vector<std::u16string> input_string2{u"test2"};
  264. std::vector<std::u16string> input_string3{u"test3"};
  265. std::vector<std::u16string> expected_strings_initial{u"test2", u"test1"};
  266. std::vector<std::u16string> expected_strings_reordered1 = input_strings;
  267. std::vector<std::u16string> expected_strings_reordered2 =
  268. expected_strings_initial;
  269. // Populate clipboard history to simulate paste-based reorders.
  270. WriteAndEnsureTextHistory(input_strings, expected_strings_initial);
  271. base::HistogramTester histogram_tester;
  272. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
  273. // By default, pausing prevents clipboard history modifications.
  274. ScopedClipboardHistoryPauseImpl scoped_pause_default_1(
  275. clipboard_history(), ClipboardHistoryUtil::PauseBehavior::kDefault);
  276. WriteAndEnsureTextHistory(input_string3, expected_strings_initial);
  277. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
  278. {
  279. // When allowing paste-based reorders, clipboard history should be modified.
  280. // Ensure that nesting pauses causes earlier pause behavior to be
  281. // overridden.
  282. ScopedClipboardHistoryPauseImpl scoped_pause_allow_reorders(
  283. clipboard_history(),
  284. ClipboardHistoryUtil::PauseBehavior::kAllowReorderOnPaste);
  285. WriteAndEnsureTextHistory(input_string1, expected_strings_reordered1);
  286. // Clipboard history modifications made during a reorder-on-paste operation
  287. // should not count as copies (or pastes). A reorder is not a user action.
  288. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
  289. {
  290. // Test that the newest behavior always applies, regardless of what order
  291. // behaviors were overridden.
  292. ScopedClipboardHistoryPauseImpl scoped_pause_default_2(
  293. clipboard_history(), ClipboardHistoryUtil::PauseBehavior::kDefault);
  294. WriteAndEnsureTextHistory(input_string3, expected_strings_reordered1);
  295. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
  296. }
  297. // Test that the previous behavior is restored when the newest pause goes
  298. // out of scope.
  299. WriteAndEnsureTextHistory(input_string2, expected_strings_reordered2);
  300. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
  301. }
  302. // Test that the previous behavior is restored when the newest pause goes out
  303. // of scope, regardless of what order behaviors were overridden.
  304. WriteAndEnsureTextHistory(input_string3, expected_strings_reordered2);
  305. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
  306. }
  307. // Tests that clipboard history pauses do not have to be destroyed in LIFO
  308. // order.
  309. TEST_F(ClipboardHistoryTest, PauseHistoryResumeOutOfOrder) {
  310. std::vector<std::u16string> input_strings{u"test1", u"test2"};
  311. std::vector<std::u16string> input_string1{u"test1"};
  312. std::vector<std::u16string> input_string2{u"test2"};
  313. std::vector<std::u16string> input_string3{u"test3"};
  314. std::vector<std::u16string> expected_strings_initial{u"test2", u"test1"};
  315. std::vector<std::u16string> expected_strings_reordered1 = input_strings;
  316. std::vector<std::u16string> expected_strings_reordered2 =
  317. expected_strings_initial;
  318. std::vector<std::u16string> expected_strings_new_item = {u"test3", u"test2",
  319. u"test1"};
  320. // Populate clipboard history to simulate paste-based reorders.
  321. WriteAndEnsureTextHistory(input_strings, expected_strings_initial);
  322. base::HistogramTester histogram_tester;
  323. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
  324. auto scoped_pause_default = std::make_unique<ScopedClipboardHistoryPauseImpl>(
  325. clipboard_history(), ClipboardHistoryUtil::PauseBehavior::kDefault);
  326. WriteAndEnsureTextHistory(input_string3, expected_strings_initial);
  327. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
  328. auto scoped_pause_allow_reorders =
  329. std::make_unique<ScopedClipboardHistoryPauseImpl>(
  330. clipboard_history(),
  331. ClipboardHistoryUtil::PauseBehavior::kAllowReorderOnPaste);
  332. WriteAndEnsureTextHistory(input_string1, expected_strings_reordered1);
  333. // Clipboard history modifications made during a reorder-on-paste operation
  334. // should not count as copies (or pastes). A reorder is not a user action.
  335. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
  336. // Verify that pauses can be destroyed in non-LIFO order without changing the
  337. // current pause behavior.
  338. scoped_pause_default.reset();
  339. WriteAndEnsureTextHistory(input_string2, expected_strings_reordered2);
  340. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
  341. // Verify that when all pauses are destroyed, clipboard history is modified as
  342. // usual.
  343. base::test::RepeatingTestFuture<bool> operation_confirmed_future;
  344. Shell::Get()
  345. ->clipboard_history_controller()
  346. ->set_confirmed_operation_callback_for_test(
  347. operation_confirmed_future.GetCallback());
  348. scoped_pause_allow_reorders.reset();
  349. WriteAndEnsureTextHistory(input_string3, expected_strings_new_item);
  350. // Since clipboard history is not paused in any way, data being written to the
  351. // clipboard is interpreted as a copy operation.
  352. EXPECT_EQ(operation_confirmed_future.Take(), true);
  353. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 1u);
  354. }
  355. // Tests that bitmaps are recorded in clipboard history.
  356. TEST_F(ClipboardHistoryTest, BasicBitmap) {
  357. SkBitmap test_bitmap = gfx::test::CreateBitmap(3, 2);
  358. std::vector<SkBitmap> input_bitmaps{test_bitmap};
  359. std::vector<SkBitmap> expected_bitmaps{test_bitmap};
  360. WriteAndEnsureBitmapHistory(input_bitmaps, expected_bitmaps);
  361. }
  362. // Tests that duplicate bitmaps show up in history as one item placed in
  363. // most-recent order.
  364. TEST_F(ClipboardHistoryTest, DuplicateBitmap) {
  365. SkBitmap test_bitmap_1 = gfx::test::CreateBitmap(3, 2);
  366. SkBitmap test_bitmap_2 = gfx::test::CreateBitmap(4, 3);
  367. std::vector<SkBitmap> input_bitmaps{test_bitmap_1, test_bitmap_2,
  368. test_bitmap_1};
  369. std::vector<SkBitmap> expected_bitmaps{test_bitmap_1, test_bitmap_2};
  370. WriteAndEnsureBitmapHistory(input_bitmaps, expected_bitmaps);
  371. }
  372. // Tests that unrecognized custom data is omitted from clipboard history.
  373. TEST_F(ClipboardHistoryTest, BasicCustomData) {
  374. const std::unordered_map<std::u16string, std::u16string> input_data = {
  375. {u"custom-format-1", u"custom-data-1"},
  376. {u"custom-format-2", u"custom-data-2"}};
  377. // Custom data which is not recognized is omitted from history.
  378. WriteAndEnsureCustomDataHistory(input_data, /*expected_data=*/{});
  379. }
  380. // Tests that file system data is recorded in clipboard history.
  381. TEST_F(ClipboardHistoryTest, BasicFileSystemData) {
  382. const std::unordered_map<std::u16string, std::u16string> input_data = {
  383. {u"fs/sources", u"/path/to/My%20File.txt"}};
  384. const std::unordered_map<std::u16string, std::u16string> expected_data =
  385. input_data;
  386. WriteAndEnsureCustomDataHistory(input_data, expected_data);
  387. }
  388. // Tests that the ClipboardHistoryDisplayFormat for HTML with no <img or <table
  389. // tags is text.
  390. TEST_F(ClipboardHistoryTest, DisplayFormatForPlainHTML) {
  391. ui::ClipboardData data;
  392. data.set_markup_data("plain html with no img or table tags");
  393. EXPECT_EQ(ClipboardHistoryUtil::ClipboardHistoryDisplayFormat::kText,
  394. ClipboardHistoryUtil::CalculateDisplayFormat(data));
  395. data.set_markup_data("<img> </img>");
  396. EXPECT_EQ(ClipboardHistoryUtil::ClipboardHistoryDisplayFormat::kHtml,
  397. ClipboardHistoryUtil::CalculateDisplayFormat(data));
  398. }
  399. // Tests that Ash.ClipboardHistory.ControlToVDelay is only recorded if
  400. // ui::VKEY_V is pressed with only ui::VKEY_CONTROL pressed.
  401. TEST_F(ClipboardHistoryTest, RecordControlV) {
  402. base::HistogramTester histogram_tester;
  403. auto* event_generator = GetEventGenerator();
  404. // Press Ctrl + V, a histogram should be emitted.
  405. event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE);
  406. PressAndReleaseKey(ui::VKEY_V, ui::EF_CONTROL_DOWN);
  407. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ControlToVDelay", 1u);
  408. // Press and release V again, no additional histograms should be emitted.
  409. PressAndReleaseKey(ui::VKEY_V, ui::EF_CONTROL_DOWN);
  410. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ControlToVDelay", 1u);
  411. // Release Control to return to no keys pressed.
  412. event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_NONE);
  413. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ControlToVDelay", 1u);
  414. // Hold shift while pressing ctrl + V, no histogram should be recorded.
  415. event_generator->PressKey(ui::VKEY_SHIFT, ui::EF_NONE);
  416. event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_SHIFT_DOWN);
  417. PressAndReleaseKey(ui::VKEY_V, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
  418. event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_SHIFT_DOWN);
  419. event_generator->ReleaseKey(ui::VKEY_SHIFT, ui::EF_NONE);
  420. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ControlToVDelay", 1u);
  421. // Press Ctrl, then press and release a random key, then press V. A histogram
  422. // should be recorded.
  423. event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE);
  424. PressAndReleaseKey(ui::VKEY_X, ui::EF_CONTROL_DOWN);
  425. PressAndReleaseKey(ui::VKEY_V, ui::EF_CONTROL_DOWN);
  426. histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ControlToVDelay", 2u);
  427. }
  428. } // namespace ash